commit 9314ffb79d1e9ea06a9b8345b6a0e3c8fdd5f4ec
Author: unknown
Date: Tue Feb 8 16:49:56 2022 -0500
Initial commit
diff --git a/.github/workflows/build-eboot-and-release.yml b/.github/workflows/build-eboot-and-release.yml
new file mode 100644
index 0000000..04343b5
--- /dev/null
+++ b/.github/workflows/build-eboot-and-release.yml
@@ -0,0 +1,69 @@
+name: Build EBOOTs and Publish Release
+on: [push]
+jobs:
+ Compile-EBOOTs:
+ runs-on: ubuntu-latest
+ container:
+ image: bmaupin/pspdev
+ steps:
+ - uses: actions/checkout@v2
+ - name: Get apt ready
+ run: |
+ apt update
+ apt install -y zip
+ - name: Build
+ working-directory: ./
+ run: |
+ cd source/libpspmath
+ make && make install
+ cd ../../
+ make -f MakePHAT install
+ make -f MakePHAT clean
+ make -f MakeSLIM install
+ - name: Generate Build Date
+ id: date
+ run: echo "::set-output name=date::$(date +'%Y-%m-%d-%H-%M-%S')"
+ - name: Zip EBOOTs
+ working-directory: ./build
+ run: |
+ zip -r psp-nzp-eboots.zip EBOOT.PBP EBOOT2000.PBP
+ - name: Delete Old Release
+ uses: dev-drprasad/delete-tag-and-release@v0.2.0
+ with:
+ delete_release: true
+ tag_name: bleeding-edge
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: bleeding-edge
+ release_name: Automated Release ${{ steps.date.outputs.date }}
+ body: |
+ This is a **bleeding edge** NZ:P PSP EBOOT release, stability is not guarenteed.
+
+ To install:
+ - Grab the .ZIP archive (psp-nzp-eboots.zip)
+ - Extract the contents of the .ZIP archive to `PSP/GAME/nzportable`.
+ - If on PSP SLIM, delete `EBOOT.PBP` and rename `EBOOT2000.PBP` to `EBOOT.PBP`.
+ draft: true
+ prerelease: false
+ - name: Upload EBOOT Archive
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./build/psp-nzp-eboots.zip
+ asset_name: psp-nzp-eboots.zip
+ asset_content_type: application/zip
+ - name: Publish Release
+ uses: StuYarrow/publish-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ id: ${{ steps.create_release.outputs.id }}
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f1b9771
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+build/*
+*.o
+*.elf
+*.PBP
+*.prx
+*.SFO
+PARAM.SFO
+EBOOT.PBP
+EBOOT2000.PBP
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..bf96377
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,87 @@
+GNU GENERAL PUBLIC LICENSE
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+Preamble
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+
+a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+
+END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/MakePHAT b/MakePHAT
new file mode 100644
index 0000000..5b26406
--- /dev/null
+++ b/MakePHAT
@@ -0,0 +1,134 @@
+PSPSDK = $(shell psp-config --pspsdk-path)
+PSPLIBSDIR = $(PSPSDK)/..
+
+TARGET = nzportable
+
+PSP_EBOOT_TITLE = Nazi Zombies Portable Reboot
+
+PSP_EBOOT_ICON = source/psp/pics/icon.png
+PSP_EBOOT_SND0 = source/psp/pics/snd0.at3
+PSP_EBOOT_PIC1 = source/psp/pics/pic.png
+
+PSP_FW_VERSION=660
+
+MODE=-DKERNEL_MODE
+
+
+BUILD_PRX = 1
+
+COMMON_OBJS = \
+ source/psp/battery.o \
+ source/thread.o \
+ source/psp/VramExt.o \
+ source/psp/input.o \
+ source/psp/main.o \
+ source/psp/math.o \
+ source/psp/sound.o \
+ source/psp/system.o \
+ source/psp/module.o \
+ source/psp/network.o \
+ source/psp/network_psp.o \
+ source/psp/gethost.o \
+ source/psp/fnmatch.o \
+ source/psp/cd.o \
+ source/psp/mp3.o \
+ source/psp/Random.o \
+ \
+ source/chase.o \
+ source/cl_demo.o \
+ source/cl_input.o \
+ source/cl_main.o \
+ source/cl_parse.o \
+ source/cl_tent.o \
+ source/cl_slist.o \
+ source/cmd.o \
+ source/common.o \
+ source/console.o \
+ source/crc.o \
+ source/cvar.o \
+ source/host.o \
+ source/host_cmd.o \
+ source/keys.o \
+ source/mathlib.o \
+ source/menu.o \
+ source/net_dgrm.o \
+ source/net_loop.o \
+ source/net_main.o \
+ source/net_vcr.o \
+ source/pr_cmds.o \
+ source/pr_edict.o \
+ source/pr_exec.o \
+ source/snd_dma.o \
+ source/snd_mem.o \
+ source/snd_mix.o \
+ source/cl_hud.o \
+ source/sv_main.o \
+ source/sv_move.o \
+ source/sv_phys.o \
+ source/sv_user.o \
+ source/view.o \
+ source/wad.o \
+ source/world.o \
+ source/zone.o \
+ source/crypter.o
+
+HARDWARE_VIDEO_ONLY_OBJS = \
+ source/psp/wad3.o \
+ source/psp/clipping.o \
+ source/psp/vram.o \
+ source/psp/video_hardware.o \
+ source/psp/video_hardware_resample.o \
+ source/psp/video_hardware_images.o \
+ source/psp/video_hardware_fullbright.o \
+ source/psp/video_hardware_hlmdl.o \
+ source/psp/video_hardware_draw.o \
+ source/psp/video_hardware_entity_fragment.o \
+ source/psp/video_hardware_QMB.o \
+ source/psp/video_hardware_decals_QMB.o \
+ source/psp/video_hardware_part.o \
+ source/psp/video_hardware_light.o \
+ source/psp/video_hardware_main.o \
+ source/psp/video_hardware_mesh.o \
+ source/psp/video_hardware_mhex2.o \
+ source/psp/video_hardware_misc.o \
+ source/psp/video_hardware_model.o \
+ source/psp/video_hardware_screen.o \
+ source/psp/video_hardware_surface.o \
+ source/psp/video_hardware_warp.o \
+ source/psp/video_hardware_fog.o \
+ source/psp/video_hardware_dxtn.o
+HARDWARE_VIDEO_ONLY_FLAGS = -DPSP_HARDWARE_VIDEO
+
+
+
+OBJS = $(COMMON_OBJS) $(HARDWARE_VIDEO_ONLY_OBJS)
+
+
+#LIBS = -lpspaudiolib -lpspaudio -lpspgum -lpspgu -lpsprtc -lpsppower -lpspwlan -lstdc++ -lm
+
+GU_LIBS = -lpspgum_vfpu -lpspvfpu -lpspgu -lpspvram
+AUDIO_LIBS = -lpspaudiolib -lpspaudio -lpspmp3 source/psp/m33libs/libpspaudiocodec.a source/psp/m33libs/libpspkubridge.a
+
+MISC_LIBS = -lpsprtc -lpspmath -lpsppower -lpsphprm -ljpeg -lpng source/psp/m33libs/libz.a source/psp/iridlibs/libPerf.a
+NET_LIBS = -lpspwlan -lpspnet_adhoc -lpspnet_adhocctl
+STD_LIBS = -lstdc++ -lm -lc
+LIBS = $(GU_LIBS) $(AUDIO_LIBS) $(MISC_LIBS) $(STD_LIBS) $(NET_LIBS)
+
+CFLAGS = -ffast-math -O3 -G0 -Wall -Did386="0" -DPSP $(MODE) $(HARDWARE_VIDEO_ONLY_FLAGS) -DSWIZZLE32 -DPSP_MP3_HWDECODE -DFULLBRIGHT -DHL_RENDER -Wno-strict-aliasing -DPSP_VFPU
+CXXFLAGS = -fno-rtti -Wcast-qual -Wno-write-strings -Wno-sign-compare -Wno-strict-aliasing
+ASFLAGS = $(CFLAGS) -c
+
+include $(PSPSDK)/lib/build.mak
+
+ifneq ($(VS_PATH),)
+CC = vs-psp-gcc
+CXX = vs-psp-g++
+endif
+
+install: EBOOT.PBP
+ mkdir -p build/exec/
+ mv EBOOT.PBP build/
+ mv *.elf build/exec/
+ mv *.prx build/exec/
+ mv *.SFO build/exec/
+ @echo DONE
diff --git a/MakeSLIM b/MakeSLIM
new file mode 100644
index 0000000..4e54e19
--- /dev/null
+++ b/MakeSLIM
@@ -0,0 +1,136 @@
+PSPSDK = $(shell psp-config --pspsdk-path)
+PSPLIBSDIR = $(PSPSDK)/..
+
+TARGET = nzportable
+
+PSP_EBOOT_TITLE = Nazi Zombies Portable Reboot
+
+PSP_EBOOT_ICON = source/psp/pics/icon.png
+PSP_EBOOT_SND0 = source/psp/pics/snd0.at3
+PSP_EBOOT_PIC1 = source/psp/pics/pic.png
+
+PSP_FW_VERSION=660
+
+PSP_LARGE_MEMORY = 1
+
+MODE=-DKERNEL_MODE
+
+
+BUILD_PRX = 1
+
+COMMON_OBJS = \
+ source/psp/battery.o \
+ source/thread.o \
+ source/psp/VramExt.o \
+ source/psp/input.o \
+ source/psp/main.o \
+ source/psp/math.o \
+ source/psp/sound.o \
+ source/psp/system.o \
+ source/psp/module.o \
+ source/psp/network.o \
+ source/psp/network_psp.o \
+ source/psp/gethost.o \
+ source/psp/fnmatch.o \
+ source/psp/cd.o \
+ source/psp/mp3.o \
+ source/psp/Random.o \
+ \
+ source/chase.o \
+ source/cl_demo.o \
+ source/cl_input.o \
+ source/cl_main.o \
+ source/cl_parse.o \
+ source/cl_tent.o \
+ source/cl_slist.o \
+ source/cmd.o \
+ source/common.o \
+ source/console.o \
+ source/crc.o \
+ source/cvar.o \
+ source/host.o \
+ source/host_cmd.o \
+ source/keys.o \
+ source/mathlib.o \
+ source/menu.o \
+ source/net_dgrm.o \
+ source/net_loop.o \
+ source/net_main.o \
+ source/net_vcr.o \
+ source/pr_cmds.o \
+ source/pr_edict.o \
+ source/pr_exec.o \
+ source/snd_dma.o \
+ source/snd_mem.o \
+ source/snd_mix.o \
+ source/cl_hud.o \
+ source/sv_main.o \
+ source/sv_move.o \
+ source/sv_phys.o \
+ source/sv_user.o \
+ source/view.o \
+ source/wad.o \
+ source/world.o \
+ source/zone.o \
+ source/crypter.o
+
+HARDWARE_VIDEO_ONLY_OBJS = \
+ source/psp/wad3.o \
+ source/psp/clipping.o \
+ source/psp/vram.o \
+ source/psp/video_hardware.o \
+ source/psp/video_hardware_resample.o \
+ source/psp/video_hardware_images.o \
+ source/psp/video_hardware_fullbright.o \
+ source/psp/video_hardware_hlmdl.o \
+ source/psp/video_hardware_draw.o \
+ source/psp/video_hardware_entity_fragment.o \
+ source/psp/video_hardware_QMB.o \
+ source/psp/video_hardware_decals_QMB.o \
+ source/psp/video_hardware_part.o \
+ source/psp/video_hardware_light.o \
+ source/psp/video_hardware_main.o \
+ source/psp/video_hardware_mesh.o \
+ source/psp/video_hardware_mhex2.o \
+ source/psp/video_hardware_misc.o \
+ source/psp/video_hardware_model.o \
+ source/psp/video_hardware_screen.o \
+ source/psp/video_hardware_surface.o \
+ source/psp/video_hardware_warp.o \
+ source/psp/video_hardware_fog.o \
+ source/psp/video_hardware_dxtn.o
+HARDWARE_VIDEO_ONLY_FLAGS = -DPSP_HARDWARE_VIDEO
+
+
+
+OBJS = $(COMMON_OBJS) $(HARDWARE_VIDEO_ONLY_OBJS)
+
+
+#LIBS = -lpspaudiolib -lpspaudio -lpspgum -lpspgu -lpsprtc -lpsppower -lpspwlan -lstdc++ -lm
+
+GU_LIBS = -lpspgum_vfpu -lpspvfpu -lpspgu -lpspvram
+AUDIO_LIBS = -lpspaudiolib -lpspaudio -lpspmp3 source/psp/m33libs/libpspaudiocodec.a source/psp/m33libs/libpspkubridge.a
+
+MISC_LIBS = -lpsprtc -lpsppower -lpspmath -lpsphprm -ljpeg -lpng source/psp/m33libs/libz.a source/psp/iridlibs/libPerf.a
+NET_LIBS = -lpspwlan -lpspnet_adhoc -lpspnet_adhocctl
+STD_LIBS = -lstdc++ -lm -lc
+LIBS = $(GU_LIBS) $(AUDIO_LIBS) $(MISC_LIBS) $(STD_LIBS) $(NET_LIBS)
+
+CFLAGS = -ffast-math -O3 -G0 -Wall -Did386="0" -DPSP $(MODE) $(HARDWARE_VIDEO_ONLY_FLAGS) -DSWIZZLE32 -DSLIM -DPSP_MP3_HWDECODE -DFULLBRIGHT -DHL_RENDER -Wno-strict-aliasing -DPSP_VFPU
+CXXFLAGS = -fno-rtti -Wcast-qual -Wno-write-strings -Wno-sign-compare -Wno-strict-aliasing
+ASFLAGS = $(CFLAGS) -c
+
+include $(PSPSDK)/lib/build.mak
+
+ifneq ($(VS_PATH),)
+CC = vs-psp-gcc
+CXX = vs-psp-g++
+endif
+
+install: EBOOT.PBP
+ mkdir -p build/exec/
+ mv EBOOT.PBP build/EBOOT2000.PBP
+ mv *.elf build/exec/
+ mv *.prx build/exec/
+ mv *.SFO build/exec/
+ @echo DONE
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..555c261
--- /dev/null
+++ b/README.md
@@ -0,0 +1,22 @@
+# Nazi Zombies: Portable dQuakePlus
+
+## About
+This repository contains the PSP engine for NZ:P, based on dQuakePlus and containing optimizations from the NZ:P Team, adQuake, and Xash3D-PSP, as well as NZ:P-specific feature implementation. It has also been modified to build on the latest versions of the [PSPSDK](https://github.com/pspdev/pspsdk).
+
+## Building (Advanced)
+Building requires a full install of [psptoolchain](https://github.com/pspdev/psptoolchain/). You can either follow the instructions on the GitHub repository or use a Docker container (we recommend [bmaupin](https://hub.docker.com/r/bmaupin/pspdev)'s)!
+
+With the psptoolchain installed, you now need to install `libpspmath`, which we have included in the GitHub repository:
+```bash
+cd source/libpspmath
+make && make install
+```
+Now you can navigate back to the root of the repository and build an `EBOOT`.
+
+```bash
+cd ../../
+make -f MakePHAT install # for PSP PHAT/1000
+make -f MakeSLIM install # for any other model
+```
+
+We also provide prebuilt EBOOTs on the [Releases](https://github.com/nzp-team/dquakeplus/releases/tag/bleeding-edge) page.
\ No newline at end of file
diff --git a/source/anorm_dots.h b/source/anorm_dots.h
new file mode 100644
index 0000000..2845fa2
--- /dev/null
+++ b/source/anorm_dots.h
@@ -0,0 +1,37 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+{
+{1.23,1.30,1.47,1.35,1.56,1.71,1.37,1.38,1.59,1.60,1.79,1.97,1.88,1.92,1.79,1.02,0.93,1.07,0.82,0.87,0.88,0.94,0.96,1.14,1.11,0.82,0.83,0.89,0.89,0.86,0.94,0.91,1.00,1.21,0.98,1.48,1.30,1.57,0.96,1.07,1.14,1.60,1.61,1.40,1.37,1.72,1.78,1.79,1.93,1.99,1.90,1.68,1.71,1.86,1.60,1.68,1.78,1.86,1.93,1.99,1.97,1.44,1.22,1.49,0.93,0.99,0.99,1.23,1.22,1.44,1.49,0.89,0.89,0.97,0.91,0.98,1.19,0.82,0.76,0.82,0.71,0.72,0.73,0.76,0.79,0.86,0.83,0.72,0.76,0.76,0.89,0.82,0.89,0.82,0.89,0.91,0.83,0.96,1.14,0.97,1.40,1.19,0.98,0.94,1.00,1.07,1.37,1.21,1.48,1.30,1.57,1.61,1.37,0.86,0.83,0.91,0.82,0.82,0.88,0.89,0.96,1.14,0.98,0.87,0.93,0.94,1.02,1.30,1.07,1.35,1.38,1.11,1.56,1.92,1.79,1.79,1.59,1.60,1.72,1.90,1.79,0.80,0.85,0.79,0.93,0.80,0.85,0.77,0.74,0.72,0.77,0.74,0.72,0.70,0.70,0.71,0.76,0.73,0.79,0.79,0.73,0.76,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.26,1.26,1.48,1.23,1.50,1.71,1.14,1.19,1.38,1.46,1.64,1.94,1.87,1.84,1.71,1.02,0.92,1.00,0.79,0.85,0.84,0.91,0.90,0.98,0.99,0.77,0.77,0.83,0.82,0.79,0.86,0.84,0.92,0.99,0.91,1.24,1.03,1.33,0.88,0.94,0.97,1.41,1.39,1.18,1.11,1.51,1.61,1.59,1.80,1.91,1.76,1.54,1.65,1.76,1.70,1.70,1.85,1.85,1.97,1.99,1.93,1.28,1.09,1.39,0.92,0.97,0.99,1.18,1.26,1.52,1.48,0.83,0.85,0.90,0.88,0.93,1.00,0.77,0.73,0.78,0.72,0.71,0.74,0.75,0.79,0.86,0.81,0.75,0.81,0.79,0.96,0.88,0.94,0.86,0.93,0.92,0.85,1.08,1.33,1.05,1.55,1.31,1.01,1.05,1.27,1.31,1.60,1.47,1.70,1.54,1.76,1.76,1.57,0.93,0.90,0.99,0.88,0.88,0.95,0.97,1.11,1.39,1.20,0.92,0.97,1.01,1.10,1.39,1.22,1.51,1.58,1.32,1.64,1.97,1.85,1.91,1.77,1.74,1.88,1.99,1.91,0.79,0.86,0.80,0.94,0.84,0.88,0.74,0.74,0.71,0.82,0.77,0.76,0.70,0.73,0.72,0.73,0.70,0.74,0.85,0.77,0.82,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.34,1.27,1.53,1.17,1.46,1.71,0.98,1.05,1.20,1.34,1.48,1.86,1.82,1.71,1.62,1.09,0.94,0.99,0.79,0.85,0.82,0.90,0.87,0.93,0.96,0.76,0.74,0.79,0.76,0.74,0.79,0.78,0.85,0.92,0.85,1.00,0.93,1.06,0.81,0.86,0.89,1.16,1.12,0.97,0.95,1.28,1.38,1.35,1.60,1.77,1.57,1.33,1.50,1.58,1.69,1.63,1.82,1.74,1.91,1.92,1.80,1.04,0.97,1.21,0.90,0.93,0.97,1.05,1.21,1.48,1.37,0.77,0.80,0.84,0.85,0.88,0.92,0.73,0.71,0.74,0.74,0.71,0.75,0.73,0.79,0.84,0.78,0.79,0.86,0.81,1.05,0.94,0.99,0.90,0.95,0.92,0.86,1.24,1.44,1.14,1.59,1.34,1.02,1.27,1.50,1.49,1.80,1.69,1.86,1.72,1.87,1.80,1.69,1.00,0.98,1.23,0.95,0.96,1.09,1.16,1.37,1.63,1.46,0.99,1.10,1.25,1.24,1.51,1.41,1.67,1.77,1.55,1.72,1.95,1.89,1.98,1.91,1.86,1.97,1.99,1.94,0.81,0.89,0.85,0.98,0.90,0.94,0.75,0.78,0.73,0.89,0.83,0.82,0.72,0.77,0.76,0.72,0.70,0.71,0.91,0.83,0.89,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.46,1.34,1.60,1.16,1.46,1.71,0.94,0.99,1.05,1.26,1.33,1.74,1.76,1.57,1.54,1.23,0.98,1.05,0.83,0.89,0.84,0.92,0.87,0.91,0.96,0.78,0.74,0.79,0.72,0.72,0.75,0.76,0.80,0.88,0.83,0.94,0.87,0.95,0.76,0.80,0.82,0.97,0.96,0.89,0.88,1.08,1.11,1.10,1.37,1.59,1.37,1.07,1.27,1.34,1.57,1.45,1.69,1.55,1.77,1.79,1.60,0.93,0.90,0.99,0.86,0.87,0.93,0.96,1.07,1.35,1.18,0.73,0.76,0.77,0.81,0.82,0.85,0.70,0.71,0.72,0.78,0.73,0.77,0.73,0.79,0.82,0.76,0.83,0.90,0.84,1.18,0.98,1.03,0.92,0.95,0.90,0.86,1.32,1.45,1.15,1.53,1.27,0.99,1.42,1.65,1.58,1.93,1.83,1.94,1.81,1.88,1.74,1.70,1.19,1.17,1.44,1.11,1.15,1.36,1.41,1.61,1.81,1.67,1.22,1.34,1.50,1.42,1.65,1.61,1.82,1.91,1.75,1.80,1.89,1.89,1.98,1.99,1.94,1.98,1.92,1.87,0.86,0.95,0.92,1.14,0.98,1.03,0.79,0.84,0.77,0.97,0.90,0.89,0.76,0.82,0.82,0.74,0.72,0.71,0.98,0.89,0.97,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.60,1.44,1.68,1.22,1.49,1.71,0.93,0.99,0.99,1.23,1.22,1.60,1.68,1.44,1.49,1.40,1.14,1.19,0.89,0.96,0.89,0.97,0.89,0.91,0.98,0.82,0.76,0.82,0.71,0.72,0.73,0.76,0.79,0.86,0.83,0.91,0.83,0.89,0.72,0.76,0.76,0.89,0.89,0.82,0.82,0.98,0.96,0.97,1.14,1.40,1.19,0.94,1.00,1.07,1.37,1.21,1.48,1.30,1.57,1.61,1.37,0.86,0.83,0.91,0.82,0.82,0.88,0.89,0.96,1.14,0.98,0.70,0.72,0.73,0.77,0.76,0.79,0.70,0.72,0.71,0.82,0.77,0.80,0.74,0.79,0.80,0.74,0.87,0.93,0.85,1.23,1.02,1.02,0.93,0.93,0.87,0.85,1.30,1.35,1.07,1.38,1.11,0.94,1.47,1.71,1.56,1.97,1.88,1.92,1.79,1.79,1.59,1.60,1.30,1.35,1.56,1.37,1.38,1.59,1.60,1.79,1.92,1.79,1.48,1.57,1.72,1.61,1.78,1.79,1.93,1.99,1.90,1.86,1.78,1.86,1.93,1.99,1.97,1.90,1.79,1.72,0.94,1.07,1.00,1.37,1.21,1.30,0.86,0.91,0.83,1.14,0.98,0.96,0.82,0.88,0.89,0.79,0.76,0.73,1.07,0.94,1.11,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.74,1.57,1.76,1.33,1.54,1.71,0.94,1.05,0.99,1.26,1.16,1.46,1.60,1.34,1.46,1.59,1.37,1.37,0.97,1.11,0.96,1.10,0.95,0.94,1.08,0.89,0.82,0.88,0.72,0.76,0.75,0.80,0.80,0.88,0.87,0.91,0.83,0.87,0.72,0.76,0.74,0.83,0.84,0.78,0.79,0.96,0.89,0.92,0.98,1.23,1.05,0.86,0.92,0.95,1.11,0.98,1.22,1.03,1.34,1.42,1.14,0.79,0.77,0.84,0.78,0.76,0.82,0.82,0.89,0.97,0.90,0.70,0.71,0.71,0.73,0.72,0.74,0.73,0.76,0.72,0.86,0.81,0.82,0.76,0.79,0.77,0.73,0.90,0.95,0.86,1.18,1.03,0.98,0.92,0.90,0.83,0.84,1.19,1.17,0.98,1.15,0.97,0.89,1.42,1.65,1.44,1.93,1.83,1.81,1.67,1.61,1.36,1.41,1.32,1.45,1.58,1.57,1.53,1.74,1.70,1.88,1.94,1.81,1.69,1.77,1.87,1.79,1.89,1.92,1.98,1.99,1.98,1.89,1.65,1.80,1.82,1.91,1.94,1.75,1.61,1.50,1.07,1.34,1.27,1.60,1.45,1.55,0.93,0.99,0.90,1.35,1.18,1.07,0.87,0.93,0.96,0.85,0.82,0.77,1.15,0.99,1.27,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.86,1.71,1.82,1.48,1.62,1.71,0.98,1.20,1.05,1.34,1.17,1.34,1.53,1.27,1.46,1.77,1.60,1.57,1.16,1.38,1.12,1.35,1.06,1.00,1.28,0.97,0.89,0.95,0.76,0.81,0.79,0.86,0.85,0.92,0.93,0.93,0.85,0.87,0.74,0.78,0.74,0.79,0.82,0.76,0.79,0.96,0.85,0.90,0.94,1.09,0.99,0.81,0.85,0.89,0.95,0.90,0.99,0.94,1.10,1.24,0.98,0.75,0.73,0.78,0.74,0.72,0.77,0.76,0.82,0.89,0.83,0.73,0.71,0.71,0.71,0.70,0.72,0.77,0.80,0.74,0.90,0.85,0.84,0.78,0.79,0.75,0.73,0.92,0.95,0.86,1.05,0.99,0.94,0.90,0.86,0.79,0.81,1.00,0.98,0.91,0.96,0.89,0.83,1.27,1.50,1.23,1.80,1.69,1.63,1.46,1.37,1.09,1.16,1.24,1.44,1.49,1.69,1.59,1.80,1.69,1.87,1.86,1.72,1.82,1.91,1.94,1.92,1.95,1.99,1.98,1.91,1.97,1.89,1.51,1.72,1.67,1.77,1.86,1.55,1.41,1.25,1.33,1.58,1.50,1.80,1.63,1.74,1.04,1.21,0.97,1.48,1.37,1.21,0.93,0.97,1.05,0.92,0.88,0.84,1.14,1.02,1.34,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.94,1.84,1.87,1.64,1.71,1.71,1.14,1.38,1.19,1.46,1.23,1.26,1.48,1.26,1.50,1.91,1.80,1.76,1.41,1.61,1.39,1.59,1.33,1.24,1.51,1.18,0.97,1.11,0.82,0.88,0.86,0.94,0.92,0.99,1.03,0.98,0.91,0.90,0.79,0.84,0.77,0.79,0.84,0.77,0.83,0.99,0.85,0.91,0.92,1.02,1.00,0.79,0.80,0.86,0.88,0.84,0.92,0.88,0.97,1.10,0.94,0.74,0.71,0.74,0.72,0.70,0.73,0.72,0.76,0.82,0.77,0.77,0.73,0.74,0.71,0.70,0.73,0.83,0.85,0.78,0.92,0.88,0.86,0.81,0.79,0.74,0.75,0.92,0.93,0.85,0.96,0.94,0.88,0.86,0.81,0.75,0.79,0.93,0.90,0.85,0.88,0.82,0.77,1.05,1.27,0.99,1.60,1.47,1.39,1.20,1.11,0.95,0.97,1.08,1.33,1.31,1.70,1.55,1.76,1.57,1.76,1.70,1.54,1.85,1.97,1.91,1.99,1.97,1.99,1.91,1.77,1.88,1.85,1.39,1.64,1.51,1.58,1.74,1.32,1.22,1.01,1.54,1.76,1.65,1.93,1.70,1.85,1.28,1.39,1.09,1.52,1.48,1.26,0.97,0.99,1.18,1.00,0.93,0.90,1.05,1.01,1.31,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.97,1.92,1.88,1.79,1.79,1.71,1.37,1.59,1.38,1.60,1.35,1.23,1.47,1.30,1.56,1.99,1.93,1.90,1.60,1.78,1.61,1.79,1.57,1.48,1.72,1.40,1.14,1.37,0.89,0.96,0.94,1.07,1.00,1.21,1.30,1.14,0.98,0.96,0.86,0.91,0.83,0.82,0.88,0.82,0.89,1.11,0.87,0.94,0.93,1.02,1.07,0.80,0.79,0.85,0.82,0.80,0.87,0.85,0.93,1.02,0.93,0.77,0.72,0.74,0.71,0.70,0.70,0.71,0.72,0.77,0.74,0.82,0.76,0.79,0.72,0.73,0.76,0.89,0.89,0.82,0.93,0.91,0.86,0.83,0.79,0.73,0.76,0.91,0.89,0.83,0.89,0.89,0.82,0.82,0.76,0.72,0.76,0.86,0.83,0.79,0.82,0.76,0.73,0.94,1.00,0.91,1.37,1.21,1.14,0.98,0.96,0.88,0.89,0.96,1.14,1.07,1.60,1.40,1.61,1.37,1.57,1.48,1.30,1.78,1.93,1.79,1.99,1.92,1.90,1.79,1.59,1.72,1.79,1.30,1.56,1.35,1.38,1.60,1.11,1.07,0.94,1.68,1.86,1.71,1.97,1.68,1.86,1.44,1.49,1.22,1.44,1.49,1.22,0.99,0.99,1.23,1.19,0.98,0.97,0.97,0.98,1.19,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.94,1.97,1.87,1.91,1.85,1.71,1.60,1.77,1.58,1.74,1.51,1.26,1.48,1.39,1.64,1.99,1.97,1.99,1.70,1.85,1.76,1.91,1.76,1.70,1.88,1.55,1.33,1.57,0.96,1.08,1.05,1.31,1.27,1.47,1.54,1.39,1.20,1.11,0.93,0.99,0.90,0.88,0.95,0.88,0.97,1.32,0.92,1.01,0.97,1.10,1.22,0.84,0.80,0.88,0.79,0.79,0.85,0.86,0.92,1.02,0.94,0.82,0.76,0.77,0.72,0.73,0.70,0.72,0.71,0.74,0.74,0.88,0.81,0.85,0.75,0.77,0.82,0.94,0.93,0.86,0.92,0.92,0.86,0.85,0.79,0.74,0.79,0.88,0.85,0.81,0.82,0.83,0.77,0.78,0.73,0.71,0.75,0.79,0.77,0.74,0.77,0.73,0.70,0.86,0.92,0.84,1.14,0.99,0.98,0.91,0.90,0.84,0.83,0.88,0.97,0.94,1.41,1.18,1.39,1.11,1.33,1.24,1.03,1.61,1.80,1.59,1.91,1.84,1.76,1.64,1.38,1.51,1.71,1.26,1.50,1.23,1.19,1.46,0.99,1.00,0.91,1.70,1.85,1.65,1.93,1.54,1.76,1.52,1.48,1.26,1.28,1.39,1.09,0.99,0.97,1.18,1.31,1.01,1.05,0.90,0.93,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.86,1.95,1.82,1.98,1.89,1.71,1.80,1.91,1.77,1.86,1.67,1.34,1.53,1.51,1.72,1.92,1.91,1.99,1.69,1.82,1.80,1.94,1.87,1.86,1.97,1.59,1.44,1.69,1.05,1.24,1.27,1.49,1.50,1.69,1.72,1.63,1.46,1.37,1.00,1.23,0.98,0.95,1.09,0.96,1.16,1.55,0.99,1.25,1.10,1.24,1.41,0.90,0.85,0.94,0.79,0.81,0.85,0.89,0.94,1.09,0.98,0.89,0.82,0.83,0.74,0.77,0.72,0.76,0.73,0.75,0.78,0.94,0.86,0.91,0.79,0.83,0.89,0.99,0.95,0.90,0.90,0.92,0.84,0.86,0.79,0.75,0.81,0.85,0.80,0.78,0.76,0.77,0.73,0.74,0.71,0.71,0.73,0.74,0.74,0.71,0.76,0.72,0.70,0.79,0.85,0.78,0.98,0.92,0.93,0.85,0.87,0.82,0.79,0.81,0.89,0.86,1.16,0.97,1.12,0.95,1.06,1.00,0.93,1.38,1.60,1.35,1.77,1.71,1.57,1.48,1.20,1.28,1.62,1.27,1.46,1.17,1.05,1.34,0.96,0.99,0.90,1.63,1.74,1.50,1.80,1.33,1.58,1.48,1.37,1.21,1.04,1.21,0.97,0.97,0.93,1.05,1.34,1.02,1.14,0.84,0.88,0.92,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.74,1.89,1.76,1.98,1.89,1.71,1.93,1.99,1.91,1.94,1.82,1.46,1.60,1.65,1.80,1.79,1.77,1.92,1.57,1.69,1.74,1.87,1.88,1.94,1.98,1.53,1.45,1.70,1.18,1.32,1.42,1.58,1.65,1.83,1.81,1.81,1.67,1.61,1.19,1.44,1.17,1.11,1.36,1.15,1.41,1.75,1.22,1.50,1.34,1.42,1.61,0.98,0.92,1.03,0.83,0.86,0.89,0.95,0.98,1.23,1.14,0.97,0.89,0.90,0.78,0.82,0.76,0.82,0.77,0.79,0.84,0.98,0.90,0.98,0.83,0.89,0.97,1.03,0.95,0.92,0.86,0.90,0.82,0.86,0.79,0.77,0.84,0.81,0.76,0.76,0.72,0.73,0.70,0.72,0.71,0.73,0.73,0.72,0.74,0.71,0.78,0.74,0.72,0.75,0.80,0.76,0.94,0.88,0.91,0.83,0.87,0.84,0.79,0.76,0.82,0.80,0.97,0.89,0.96,0.88,0.95,0.94,0.87,1.11,1.37,1.10,1.59,1.57,1.37,1.33,1.05,1.08,1.54,1.34,1.46,1.16,0.99,1.26,0.96,1.05,0.92,1.45,1.55,1.27,1.60,1.07,1.34,1.35,1.18,1.07,0.93,0.99,0.90,0.93,0.87,0.96,1.27,0.99,1.15,0.77,0.82,0.85,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.60,1.78,1.68,1.93,1.86,1.71,1.97,1.99,1.99,1.97,1.93,1.60,1.68,1.78,1.86,1.61,1.57,1.79,1.37,1.48,1.59,1.72,1.79,1.92,1.90,1.38,1.35,1.60,1.23,1.30,1.47,1.56,1.71,1.88,1.79,1.92,1.79,1.79,1.30,1.56,1.35,1.37,1.59,1.38,1.60,1.90,1.48,1.72,1.57,1.61,1.79,1.21,1.00,1.30,0.89,0.94,0.96,1.07,1.14,1.40,1.37,1.14,0.96,0.98,0.82,0.88,0.82,0.89,0.83,0.86,0.91,1.02,0.93,1.07,0.87,0.94,1.11,1.02,0.93,0.93,0.82,0.87,0.80,0.85,0.79,0.80,0.85,0.77,0.72,0.74,0.71,0.70,0.70,0.71,0.72,0.77,0.74,0.72,0.76,0.73,0.82,0.79,0.76,0.73,0.79,0.76,0.93,0.86,0.91,0.83,0.89,0.89,0.82,0.72,0.76,0.76,0.89,0.82,0.89,0.82,0.89,0.91,0.83,0.96,1.14,0.97,1.40,1.44,1.19,1.22,0.99,0.98,1.49,1.44,1.49,1.22,0.99,1.23,0.98,1.19,0.97,1.21,1.30,1.00,1.37,0.94,1.07,1.14,0.98,0.96,0.86,0.91,0.83,0.88,0.82,0.89,1.11,0.94,1.07,0.73,0.76,0.79,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.46,1.65,1.60,1.82,1.80,1.71,1.93,1.91,1.99,1.94,1.98,1.74,1.76,1.89,1.89,1.42,1.34,1.61,1.11,1.22,1.36,1.50,1.61,1.81,1.75,1.15,1.17,1.41,1.18,1.19,1.42,1.44,1.65,1.83,1.67,1.94,1.81,1.88,1.32,1.58,1.45,1.57,1.74,1.53,1.70,1.98,1.69,1.87,1.77,1.79,1.92,1.45,1.27,1.55,0.97,1.07,1.11,1.34,1.37,1.59,1.60,1.35,1.07,1.18,0.86,0.93,0.87,0.96,0.90,0.93,0.99,1.03,0.95,1.15,0.90,0.99,1.27,0.98,0.90,0.92,0.78,0.83,0.77,0.84,0.79,0.82,0.86,0.73,0.71,0.73,0.72,0.70,0.73,0.72,0.76,0.81,0.76,0.76,0.82,0.77,0.89,0.85,0.82,0.75,0.80,0.80,0.94,0.88,0.94,0.87,0.95,0.96,0.88,0.72,0.74,0.76,0.83,0.78,0.84,0.79,0.87,0.91,0.83,0.89,0.98,0.92,1.23,1.34,1.05,1.16,0.99,0.96,1.46,1.57,1.54,1.33,1.05,1.26,1.08,1.37,1.10,0.98,1.03,0.92,1.14,0.86,0.95,0.97,0.90,0.89,0.79,0.84,0.77,0.82,0.76,0.82,0.97,0.89,0.98,0.71,0.72,0.74,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.34,1.51,1.53,1.67,1.72,1.71,1.80,1.77,1.91,1.86,1.98,1.86,1.82,1.95,1.89,1.24,1.10,1.41,0.95,0.99,1.09,1.25,1.37,1.63,1.55,0.96,0.98,1.16,1.05,1.00,1.27,1.23,1.50,1.69,1.46,1.86,1.72,1.87,1.24,1.49,1.44,1.69,1.80,1.59,1.69,1.97,1.82,1.94,1.91,1.92,1.99,1.63,1.50,1.74,1.16,1.33,1.38,1.58,1.60,1.77,1.80,1.48,1.21,1.37,0.90,0.97,0.93,1.05,0.97,1.04,1.21,0.99,0.95,1.14,0.92,1.02,1.34,0.94,0.86,0.90,0.74,0.79,0.75,0.81,0.79,0.84,0.86,0.71,0.71,0.73,0.76,0.73,0.77,0.74,0.80,0.85,0.78,0.81,0.89,0.84,0.97,0.92,0.88,0.79,0.85,0.86,0.98,0.92,1.00,0.93,1.06,1.12,0.95,0.74,0.74,0.78,0.79,0.76,0.82,0.79,0.87,0.93,0.85,0.85,0.94,0.90,1.09,1.27,0.99,1.17,1.05,0.96,1.46,1.71,1.62,1.48,1.20,1.34,1.28,1.57,1.35,0.90,0.94,0.85,0.98,0.81,0.89,0.89,0.83,0.82,0.75,0.78,0.73,0.77,0.72,0.76,0.89,0.83,0.91,0.71,0.70,0.72,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.26,1.39,1.48,1.51,1.64,1.71,1.60,1.58,1.77,1.74,1.91,1.94,1.87,1.97,1.85,1.10,0.97,1.22,0.88,0.92,0.95,1.01,1.11,1.39,1.32,0.88,0.90,0.97,0.96,0.93,1.05,0.99,1.27,1.47,1.20,1.70,1.54,1.76,1.08,1.31,1.33,1.70,1.76,1.55,1.57,1.88,1.85,1.91,1.97,1.99,1.99,1.70,1.65,1.85,1.41,1.54,1.61,1.76,1.80,1.91,1.93,1.52,1.26,1.48,0.92,0.99,0.97,1.18,1.09,1.28,1.39,0.94,0.93,1.05,0.92,1.01,1.31,0.88,0.81,0.86,0.72,0.75,0.74,0.79,0.79,0.86,0.85,0.71,0.73,0.75,0.82,0.77,0.83,0.78,0.85,0.88,0.81,0.88,0.97,0.90,1.18,1.00,0.93,0.86,0.92,0.94,1.14,0.99,1.24,1.03,1.33,1.39,1.11,0.79,0.77,0.84,0.79,0.77,0.84,0.83,0.90,0.98,0.91,0.85,0.92,0.91,1.02,1.26,1.00,1.23,1.19,0.99,1.50,1.84,1.71,1.64,1.38,1.46,1.51,1.76,1.59,0.84,0.88,0.80,0.94,0.79,0.86,0.82,0.77,0.76,0.74,0.74,0.71,0.73,0.70,0.72,0.82,0.77,0.85,0.74,0.70,0.73,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}
+}
diff --git a/source/anorms.h b/source/anorms.h
new file mode 100644
index 0000000..11a9007
--- /dev/null
+++ b/source/anorms.h
@@ -0,0 +1,181 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+{-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/source/bspfile.h b/source/bspfile.h
new file mode 100644
index 0000000..6751d8f
--- /dev/null
+++ b/source/bspfile.h
@@ -0,0 +1,326 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+
+// upper design bounds
+
+#define MAX_MAP_HULLS 4
+
+#define MAX_MAP_MODELS 256
+#define MAX_MAP_BRUSHES 4096
+#define MAX_MAP_ENTITIES 1024
+#define MAX_MAP_ENTSTRING 65536
+
+#define MAX_MAP_PLANES 32767
+#define MAX_MAP_NODES 32767 // because negative shorts are contents
+#define MAX_MAP_CLIPNODES 32767 //
+#define MAX_MAP_LEAFS 8192
+#define MAX_MAP_VERTS 65535
+#define MAX_MAP_FACES 65535
+#define MAX_MAP_MARKSURFACES 65535
+#define MAX_MAP_TEXINFO 4096
+#define MAX_MAP_EDGES 256000
+#define MAX_MAP_SURFEDGES 512000
+#define MAX_MAP_TEXTURES 512
+#define MAX_MAP_MIPTEX 0x200000
+#define MAX_MAP_LIGHTING 0x100000
+#define MAX_MAP_VISIBILITY 0x100000
+
+#define MAX_MAP_PORTALS 65536
+
+// key / value pair sizes
+
+#define MAX_KEY 32
+#define MAX_VALUE 1024
+
+//=============================================================================
+
+
+#define BSPVERSION 29
+#define HL_BSPVERSION 30
+#define NZP_BSPVERSION 1337
+#define TOOLVERSION 2
+
+typedef struct
+{
+ int fileofs, filelen;
+} lump_t;
+
+#define LUMP_ENTITIES 0
+#define LUMP_PLANES 1
+#define LUMP_TEXTURES 2
+#define LUMP_VERTEXES 3
+#define LUMP_VISIBILITY 4
+#define LUMP_NODES 5
+#define LUMP_TEXINFO 6
+#define LUMP_FACES 7
+#define LUMP_LIGHTING 8
+#define LUMP_CLIPNODES 9
+#define LUMP_LEAFS 10
+#define LUMP_MARKSURFACES 11
+#define LUMP_EDGES 12
+#define LUMP_SURFEDGES 13
+#define LUMP_MODELS 14
+#define HEADER_LUMPS 15
+
+typedef struct
+{
+ float mins[3], maxs[3];
+ float origin[3];
+ int headnode[MAX_MAP_HULLS];
+ int visleafs; // not including the solid leaf 0
+ int firstface, numfaces;
+} dmodel_t;
+
+typedef struct
+{
+ int version;
+ lump_t lumps[HEADER_LUMPS];
+} dheader_t;
+
+typedef struct
+{
+ int nummiptex;
+ int dataofs[4]; // [nummiptex]
+} dmiptexlump_t;
+
+#define MIPLEVELS 4
+typedef struct miptex_s
+{
+ char name[16];
+ unsigned width, height;
+ unsigned offsets[MIPLEVELS]; // four mip maps stored
+} miptex_t;
+
+
+typedef struct
+{
+ float point[3];
+} dvertex_t;
+
+
+// 0-2 are axial planes
+#define PLANE_X 0
+#define PLANE_Y 1
+#define PLANE_Z 2
+
+// 3-5 are non-axial planes snapped to the nearest
+#define PLANE_ANYX 3
+#define PLANE_ANYY 4
+#define PLANE_ANYZ 5
+
+typedef struct
+{
+ float normal[3];
+ float dist;
+ int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+} dplane_t;
+
+
+
+#define CONTENTS_EMPTY -1
+#define CONTENTS_SOLID -2
+#define CONTENTS_WATER -3
+#define CONTENTS_SLIME -4
+#define CONTENTS_LAVA -5
+#define CONTENTS_SKY -6
+#define CONTENTS_ORIGIN -7 // removed at csg time
+#define CONTENTS_CLIP -8 // changed to contents_solid
+
+#define CONTENTS_CURRENT_0 -9
+#define CONTENTS_CURRENT_90 -10
+#define CONTENTS_CURRENT_180 -11
+#define CONTENTS_CURRENT_270 -12
+#define CONTENTS_CURRENT_UP -13
+#define CONTENTS_CURRENT_DOWN -14
+
+
+// !!! if this is changed, it must be changed in asm_i386.h too !!!
+typedef struct
+{
+ int planenum;
+ short children[2]; // negative numbers are -(leafs+1), not nodes
+ short mins[3]; // for sphere culling
+ short maxs[3];
+ unsigned short firstface;
+ unsigned short numfaces; // counting both sides
+} dnode_t;
+
+typedef struct
+{
+ int planenum;
+ short children[2]; // negative numbers are contents
+} dclipnode_t;
+
+
+typedef struct texinfo_s
+{
+ float vecs[2][4]; // [s/t][xyz offset]
+ int miptex;
+ int flags;
+} texinfo_t;
+#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision
+
+// note that edge 0 is never used, because negative edge nums are used for
+// counterclockwise use of the edge in a face
+typedef struct
+{
+ unsigned short v[2]; // vertex numbers
+} dedge_t;
+
+#define MAXLIGHTMAPS 4
+typedef struct
+{
+ short planenum;
+ short side;
+
+ int firstedge; // we must support > 64k edges
+ short numedges;
+ short texinfo;
+
+// lighting info
+ byte styles[MAXLIGHTMAPS];
+ int lightofs; // start of [numstyles*surfsize] samples
+} dface_t;
+
+
+
+#define AMBIENT_WATER 0
+#define AMBIENT_SKY 1
+#define AMBIENT_SLIME 2
+#define AMBIENT_LAVA 3
+
+#define NUM_AMBIENTS 4 // automatic ambient sounds
+
+// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas
+// all other leafs need visibility info
+typedef struct
+{
+ int contents;
+ int visofs; // -1 = no visibility info
+
+ short mins[3]; // for frustum culling
+ short maxs[3];
+
+ unsigned short firstmarksurface;
+ unsigned short nummarksurfaces;
+
+ byte ambient_level[NUM_AMBIENTS];
+} dleaf_t;
+
+
+//============================================================================
+
+#ifndef QUAKE_GAME
+
+#define ANGLE_UP -1
+#define ANGLE_DOWN -2
+
+
+// the utilities get to be lazy and just use large static arrays
+
+extern int nummodels;
+extern dmodel_t dmodels[MAX_MAP_MODELS];
+
+extern int visdatasize;
+extern byte dvisdata[MAX_MAP_VISIBILITY];
+
+extern int lightdatasize;
+extern byte dlightdata[MAX_MAP_LIGHTING];
+
+extern int texdatasize;
+extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
+
+extern int entdatasize;
+extern char dentdata[MAX_MAP_ENTSTRING];
+
+extern int numleafs;
+extern dleaf_t dleafs[MAX_MAP_LEAFS];
+
+extern int numplanes;
+extern dplane_t dplanes[MAX_MAP_PLANES];
+
+extern int numvertexes;
+extern dvertex_t dvertexes[MAX_MAP_VERTS];
+
+extern int numnodes;
+extern dnode_t dnodes[MAX_MAP_NODES];
+
+extern int numtexinfo;
+extern texinfo_t texinfo[MAX_MAP_TEXINFO];
+
+extern int numfaces;
+extern dface_t dfaces[MAX_MAP_FACES];
+
+extern int numclipnodes;
+extern dclipnode_t dclipnodes[MAX_MAP_CLIPNODES];
+
+extern int numedges;
+extern dedge_t dedges[MAX_MAP_EDGES];
+
+extern int nummarksurfaces;
+extern unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES];
+
+extern int numsurfedges;
+extern int dsurfedges[MAX_MAP_SURFEDGES];
+
+
+void DecompressVis (byte *in, byte *decompressed);
+int CompressVis (byte *vis, byte *dest);
+
+void LoadBSPFile (char *filename);
+void WriteBSPFile (char *filename);
+void PrintBSPFileSizes (void);
+
+//===============
+
+
+typedef struct epair_s
+{
+ struct epair_s *next;
+ char *key;
+ char *value;
+} epair_t;
+
+typedef struct
+{
+ vec3_t origin;
+ int firstbrush;
+ int numbrushes;
+ epair_t *epairs;
+} entity_t;
+
+
+extern int num_entities;
+extern entity_t entities[MAX_MAP_ENTITIES];
+
+void ParseEntities (void);
+void UnparseEntities (void);
+
+void SetKeyValue (entity_t *ent, char *key, char *value);
+char *ValueForKey (entity_t *ent, char *key);
+// will return "" if not present
+
+vec_t FloatForKey (entity_t *ent, char *key);
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
+
+epair_t *ParseEpair (void);
+
+#endif
diff --git a/source/cdaudio.h b/source/cdaudio.h
new file mode 100644
index 0000000..5b30c47
--- /dev/null
+++ b/source/cdaudio.h
@@ -0,0 +1,31 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+int CDAudio_Init(void);
+void CDAudio_Play(byte track, qboolean looping);
+void CDAudio_Stop(void);
+void CDAudio_Pause(void);
+void CDAudio_Resume(void);
+void CDAudio_Shutdown(void);
+void CDAudio_Update(void);
+void CDAudio_Next(void);
+void CDAudio_Prev(void);
+void CDAudio_PrintMusicList(void);
+void CDAudio_Track(char* trackname);
diff --git a/source/chase.c b/source/chase.c
new file mode 100644
index 0000000..92f98ef
--- /dev/null
+++ b/source/chase.c
@@ -0,0 +1,182 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// chase.c -- chase camera code
+
+#include "quakedef.h"
+
+cvar_t chase_back = {"chase_back", "100"};
+cvar_t chase_up = {"chase_up", "16"};
+cvar_t chase_right = {"chase_right", "0"};
+cvar_t chase_active = {"chase_active", "0"};
+cvar_t chase_roll = {"chase_roll", "0"};
+cvar_t chase_yaw = {"chase_yaw", "0"};
+cvar_t chase_pitch = {"chase_pitch", "0"};
+
+vec3_t chase_pos;
+vec3_t chase_angles;
+
+vec3_t chase_dest;
+vec3_t chase_dest_angles;
+
+
+void Chase_Init (void)
+{
+ Cvar_RegisterVariable (&chase_back);
+ Cvar_RegisterVariable (&chase_up);
+ Cvar_RegisterVariable (&chase_right);
+ Cvar_RegisterVariable (&chase_active);
+ Cvar_RegisterVariable (&chase_roll);
+ Cvar_RegisterVariable (&chase_yaw);
+ Cvar_RegisterVariable (&chase_pitch);
+}
+
+void Chase_Reset (void)
+{
+ // for respawning and teleporting
+// start position 12 units behind head
+}
+
+void TraceLine (vec3_t start, vec3_t end, vec3_t impact)
+{
+ trace_t trace;
+
+ memset (&trace, 0, sizeof(trace));
+ SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
+
+ VectorCopy (trace.endpos, impact);
+}
+
+/*
+#define NUM_TESTS 64 (delete)
+#define CHASE_DEST_OFFSET 2.0f
+*/
+
+qboolean chase_nodraw;
+
+#define NUM_TESTS 64
+#define CHASE_DEST_OFFSET 2.0f
+
+void Chase_Update (void)
+{
+ int i;
+ float dist;
+ vec3_t forward, up, right;
+ vec3_t dest, stop;
+ int best;
+ int viewcontents;
+
+ // if can't see player, reset
+ AngleVectors (cl.viewangles, forward, right, up);
+
+ // calc exact destination
+ for (i = 0; i < 3; i++)
+ chase_dest[i] = r_refdef.vieworg[i] - forward[i] * chase_back.value - right[i] * chase_right.value;
+
+ chase_dest[2] = r_refdef.vieworg[2] + chase_up.value;
+
+ // take the contents of the view leaf
+ viewcontents = (Mod_PointInLeaf (r_refdef.vieworg, cl.worldmodel))->contents;
+
+ for (best = 0; best < NUM_TESTS; best++)
+ {
+ float chase_newdest[3];
+
+ chase_newdest[0] = r_refdef.vieworg[0] + (chase_dest[0] - r_refdef.vieworg[0]) * best / NUM_TESTS;
+ chase_newdest[1] = r_refdef.vieworg[1] + (chase_dest[1] - r_refdef.vieworg[1]) * best / NUM_TESTS;
+ chase_newdest[2] = r_refdef.vieworg[2] + (chase_dest[2] - r_refdef.vieworg[2]) * best / NUM_TESTS;
+
+ // check for a leaf hit with different contents
+ if ((Mod_PointInLeaf (chase_newdest, cl.worldmodel))->contents != viewcontents)
+ {
+ // go back to the previous best as this one is bad
+ // unless the first one was also bad, (viewleaf contents != viewleaf contents!!!)
+ if (best > 0)
+ best--;
+ else best = NUM_TESTS;
+ break;
+ }
+ }
+
+ // certain surfaces can be viewed at an oblique enough angle that they are partially clipped
+ // by znear, so now we fix that too...
+ for (; best >= 0; best--)
+ {
+ // number of matches
+ int nummatches = 0;
+
+ // adjust
+ chase_dest[0] = r_refdef.vieworg[0] + (chase_dest[0] - r_refdef.vieworg[0]) * best / NUM_TESTS;
+ chase_dest[1] = r_refdef.vieworg[1] + (chase_dest[1] - r_refdef.vieworg[1]) * best / NUM_TESTS;
+ chase_dest[2] = r_refdef.vieworg[2] + (chase_dest[2] - r_refdef.vieworg[2]) * best / NUM_TESTS;
+
+ // move x to neg
+ chase_dest[0] -= CHASE_DEST_OFFSET;
+ if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
+ chase_dest[0] += CHASE_DEST_OFFSET;
+
+ // move x to pos
+ chase_dest[0] += CHASE_DEST_OFFSET;
+ if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
+ chase_dest[0] -= CHASE_DEST_OFFSET;
+
+ // move y to neg
+ chase_dest[1] -= CHASE_DEST_OFFSET;
+ if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
+ chase_dest[1] += CHASE_DEST_OFFSET;
+
+ // move y to pos
+ chase_dest[1] += CHASE_DEST_OFFSET;
+ if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
+ chase_dest[1] -= CHASE_DEST_OFFSET;
+
+ // move z to neg
+ chase_dest[2] -= CHASE_DEST_OFFSET;
+ if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
+ chase_dest[2] += CHASE_DEST_OFFSET;
+
+ // move z to pos
+ chase_dest[2] += CHASE_DEST_OFFSET;
+ if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++;
+ chase_dest[2] -= CHASE_DEST_OFFSET;
+
+ // all tests passed so we're good!
+ if (nummatches == 6) break;
+ }
+
+ // find the spot the player is looking at
+ VectorMA (r_refdef.vieworg, 4096, forward, dest);
+ TraceLine (r_refdef.vieworg, dest, stop);
+
+ // calculate pitch to look at the same spot from camera
+ VectorSubtract (stop, r_refdef.vieworg, stop);
+ dist = DotProduct (stop, forward);
+ if (dist < 1)
+ dist = 1;
+
+ #ifdef PSP_VFPU
+ r_refdef.viewangles[PITCH] = -vfpu_atanf(stop[2] / dist) / M_PI * 180;
+ #else
+ r_refdef.viewangles[PITCH] = -atan(stop[2] / dist) / M_PI * 180;
+ #endif
+
+ // move towards destination
+ VectorCopy (chase_dest, r_refdef.vieworg);
+}
+
diff --git a/source/cl_demo.c b/source/cl_demo.c
new file mode 100644
index 0000000..15c3c68
--- /dev/null
+++ b/source/cl_demo.c
@@ -0,0 +1,380 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+void CL_FinishTimeDemo (void);
+
+/*
+==============================================================================
+
+DEMO CODE
+
+When a demo is playing back, all NET_SendMessages are skipped, and
+NET_GetMessages are read from the demo file.
+
+Whenever cl.time gets past the last received message, another message is
+read from the demo file.
+==============================================================================
+*/
+
+/*
+==============
+CL_StopPlayback
+
+Called when a demo file runs out, or the user starts a game
+==============
+*/
+void CL_StopPlayback (void)
+{
+ if (!cls.demoplayback)
+ return;
+
+ Sys_FileClose(cls.demofile);
+ cls.demoplayback = false;
+ cls.demofile = -1;
+ cls.state = ca_disconnected;
+
+ if (cls.timedemo)
+ CL_FinishTimeDemo ();
+}
+
+/*
+====================
+CL_WriteDemoMessage
+
+Dumps the current net message, prefixed by the length and view angles
+====================
+*/
+void CL_WriteDemoMessage (void)
+{
+ int len;
+ int i;
+ float f;
+
+ len = LittleLong (net_message.cursize);
+ Sys_FileWrite(cls.demofile, &len, 4);
+ for (i=0 ; i<3 ; i++)
+ {
+ f = LittleFloat (cl.viewangles[i]);
+ Sys_FileWrite(cls.demofile, &f, 4);
+ }
+ Sys_FileWrite(cls.demofile, net_message.data, net_message.cursize);
+}
+
+/*
+====================
+CL_GetMessage
+
+Handles recording and playback of demos, on top of NET_ code
+====================
+*/
+int CL_GetMessage (void)
+{
+ int r, i;
+ float f;
+
+ if (cls.demoplayback)
+ {
+ // decide if it is time to grab the next message
+ if (cls.signon == SIGNONS) // allways grab until fully connected
+ {
+ if (cls.timedemo)
+ {
+ if (host_framecount == cls.td_lastframe)
+ return 0; // allready read this frame's message
+ cls.td_lastframe = host_framecount;
+ // if this is the second frame, grab the real td_starttime
+ // so the bogus time on the first frame doesn't count
+ if (host_framecount == cls.td_startframe + 1)
+ cls.td_starttime = realtime;
+ }
+ else if ( /* cl.time > 0 && */ cl.time <= cl.mtime[0])
+ {
+ return 0; // don't need another message yet
+ }
+ }
+
+ // get the next message
+ Sys_FileRead(cls.demofile, &net_message.cursize, 4);
+ VectorCopy (cl.mviewangles[0], cl.mviewangles[1]);
+ for (i=0 ; i<3 ; i++)
+ {
+ r = Sys_FileRead(cls.demofile, &f, 4) / 4;
+ cl.mviewangles[0][i] = LittleFloat (f);
+ }
+
+ net_message.cursize = LittleLong (net_message.cursize);
+ if (net_message.cursize > MAX_MSGLEN)
+ Sys_Error ("Demo message (0x%08x) > MAX_MSGLEN (%d)", net_message.cursize, MAX_MSGLEN);
+ r = Sys_FileRead(cls.demofile, net_message.data, net_message.cursize) / net_message.cursize;
+ if (r != 1)
+ {
+ CL_StopPlayback ();
+ return 0;
+ }
+
+ return 1;
+ }
+
+ while (1)
+ {
+ r = NET_GetMessage (cls.netcon);
+
+ if (r != 1 && r != 2)
+ return r;
+
+ // discard nop keepalive message
+ if (net_message.cursize == 1 && net_message.data[0] == svc_nop)
+ Con_Printf ("<-- server to client keepalive\n");
+ else
+ break;
+ }
+
+ if (cls.demorecording)
+ CL_WriteDemoMessage ();
+
+ return r;
+}
+
+
+/*
+====================
+CL_Stop_f
+
+stop recording a demo
+====================
+*/
+void CL_Stop_f (void)
+{
+ if (cmd_source != src_command)
+ return;
+
+ if (!cls.demorecording)
+ {
+ Con_Printf ("Not recording a demo.\n");
+ return;
+ }
+
+// write a disconnect message to the demo file
+ SZ_Clear (&net_message);
+ MSG_WriteByte (&net_message, svc_disconnect);
+ CL_WriteDemoMessage ();
+
+// finish up
+ Sys_FileClose(cls.demofile);
+ cls.demofile = -1;
+ cls.demorecording = false;
+ Con_Printf ("Completed demo\n");
+}
+
+/*
+====================
+CL_Record_f
+
+record [cd track]
+====================
+*/
+void CL_Record_f (void)
+{
+ int c;
+ char name[MAX_OSPATH];
+ int track;
+ char forcetrack[16];
+
+ if (cmd_source != src_command)
+ return;
+
+ c = Cmd_Argc();
+ if (c != 2 && c != 3 && c != 4)
+ {
+ Con_Printf ("record [ [cd track]]\n");
+ return;
+ }
+
+ if (strstr(Cmd_Argv(1), ".."))
+ {
+ Con_Printf ("Relative pathnames are not allowed.\n");
+ return;
+ }
+
+ if (c == 2 && cls.state == ca_connected)
+ {
+ Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
+ return;
+ }
+
+// write the forced cd track number, or -1
+ if (c == 4)
+ {
+ track = atoi(Cmd_Argv(3));
+ Con_Printf ("Forcing CD track to %i\n", cls.forcetrack);
+ }
+ else
+ track = -1;
+
+ sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
+
+//
+// start the map up
+//
+ if (c > 2)
+ Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
+
+//
+// open the demo file
+//
+ COM_DefaultExtension (name, ".dem");
+
+ Con_Printf ("recording to %s.\n", name);
+ cls.demofile = Sys_FileOpenWrite(name);
+ if (cls.demofile < 0)
+ {
+ Con_Printf ("ERROR: couldn't open demo for writing.\n");
+ return;
+ }
+
+ cls.forcetrack = track;
+ sprintf(forcetrack, "%i\n", cls.forcetrack);
+ Sys_FileWrite(cls.demofile, forcetrack, strlen(forcetrack));
+
+ cls.demorecording = true;
+}
+
+
+/*
+====================
+CL_PlayDemo_f
+
+play [demoname]
+====================
+*/
+static int CL_FileGetChar(int handle)
+{
+ char c;
+
+ if (Sys_FileRead(handle, &c, 1) != 1)
+ {
+ return EOF;
+ }
+
+ return c;
+}
+
+void CL_PlayDemo_f (void)
+{
+ char name[256];
+ int c;
+ qboolean neg = false;
+
+ if (cmd_source != src_command)
+ return;
+
+ if (Cmd_Argc() != 2)
+ {
+ Con_Printf ("play : plays a demo\n");
+ return;
+ }
+
+//
+// disconnect from server
+//
+ CL_Disconnect ();
+
+//
+// open the demo file
+//
+ strcpy (name, Cmd_Argv(1));
+ COM_DefaultExtension (name, ".dem");
+
+ Con_Printf ("Playing demo from %s.\n", name);
+ COM_FOpenFile (name, &cls.demofile);
+ if (cls.demofile < 0)
+ {
+ Con_Printf ("ERROR: couldn't open demo for reading.\n");
+ cls.demonum = -1; // stop demo loop
+ return;
+ }
+
+ cls.demoplayback = true;
+ cls.state = ca_connected;
+ cls.forcetrack = 0;
+
+ while ((c = CL_FileGetChar(cls.demofile)) != '\n')
+ if (c == '-')
+ neg = true;
+ else
+ cls.forcetrack = cls.forcetrack * 10 + (c - '0');
+
+ if (neg)
+ cls.forcetrack = -cls.forcetrack;
+// ZOID, fscanf is evil
+// fscanf (cls.demofile, "%i\n", &cls.forcetrack);
+}
+
+/*
+====================
+CL_FinishTimeDemo
+
+====================
+*/
+void CL_FinishTimeDemo (void)
+{
+ int frames;
+ double time;
+
+ cls.timedemo = false;
+
+// the first frame didn't count
+ frames = (host_framecount - cls.td_startframe) - 1;
+ time = realtime - cls.td_starttime;
+ if (time < 1)
+ time = 1;
+ Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time);
+}
+
+/*
+====================
+CL_TimeDemo_f
+
+timedemo [demoname]
+====================
+*/
+void CL_TimeDemo_f (void)
+{
+ if (cmd_source != src_command)
+ return;
+
+ if (Cmd_Argc() != 2)
+ {
+ Con_Printf ("timedemo : gets demo speeds\n");
+ return;
+ }
+
+ CL_PlayDemo_f ();
+
+// cls.td_starttime will be grabbed at the second frame of the demo, so
+// all the loading time doesn't get counted
+
+ cls.timedemo = true;
+ cls.td_startframe = host_framecount;
+ cls.td_lastframe = -1; // get a new message this frame
+}
+
diff --git a/source/cl_hud.c b/source/cl_hud.c
new file mode 100644
index 0000000..ed5bce0
--- /dev/null
+++ b/source/cl_hud.c
@@ -0,0 +1,1423 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// cl_hud.c -- status bar code
+
+#include "quakedef.h"
+#include
+
+#ifdef PSP_VFPU
+#include
+#endif
+
+qpic_t *sb_round[5];
+qpic_t *sb_round_num[10];
+qpic_t *sb_moneyback;
+qpic_t *instapic;
+qpic_t *x2pic;
+qpic_t *revivepic;
+qpic_t *jugpic;
+qpic_t *floppic;
+qpic_t *staminpic;
+qpic_t *doublepic;
+qpic_t *speedpic;
+qpic_t *deadpic;
+qpic_t *mulepic;
+qpic_t *fragpic;
+qpic_t *bettypic;
+
+
+qpic_t *b_circle;
+qpic_t *b_square;
+qpic_t *b_cross;
+qpic_t *b_triangle;
+qpic_t *b_left;
+qpic_t *b_right;
+qpic_t *b_up;
+qpic_t *b_down;
+qpic_t *b_lt;
+qpic_t *b_rt;
+qpic_t *b_start;
+qpic_t *b_select;
+qpic_t *b_home;
+
+qpic_t *fx_blood_lu;
+qpic_t *fx_blood_ru;
+qpic_t *fx_blood_ld;
+qpic_t *fx_blood_rd;
+
+qboolean sb_showscores;
+qboolean domaxammo;
+
+int x_value, y_value;
+
+void M_DrawPic (int x, int y, qpic_t *pic);
+
+double HUD_Change_time;//hide hud when not chagned
+
+extern cvar_t waypoint_mode;
+
+
+int old_points;
+int current_points;
+int point_change_interval;
+int point_change_interval_neg;
+int alphabling = 0;
+float round_center_x;
+float round_center_y;
+
+typedef struct
+{
+ int points;
+ int negative;
+ float x;
+ float y;
+ float move_x;
+ float move_y;
+ double alive_time;
+} point_change_t;
+
+point_change_t point_change[10];
+
+/*
+===============
+HUD_Init
+===============
+*/
+void HUD_Init (void)
+{
+ int i;
+
+ for (i=0 ; i<5 ; i++)
+ {
+ sb_round[i] = Draw_CachePic (va("gfx/hud/r%i",i + 1));
+ }
+
+ for (i=0 ; i<10 ; i++)
+ {
+ sb_round_num[i] = Draw_CachePic (va("gfx/hud/r_num%i",i));
+ }
+
+ sb_moneyback = Draw_CachePic ("gfx/hud/moneyback");
+ instapic = Draw_CachePic ("gfx/hud/in_kill");
+ x2pic = Draw_CachePic ("gfx/hud/2x");
+
+ revivepic = Draw_CachePic ("gfx/hud/revive");
+ jugpic = Draw_CachePic ("gfx/hud/jug");
+ floppic = Draw_CachePic ("gfx/hud/flopper");
+ staminpic = Draw_CachePic ("gfx/hud/stamin");
+ doublepic = Draw_CachePic ("gfx/hud/double");
+ speedpic = Draw_CachePic ("gfx/hud/speed");
+ deadpic = Draw_CachePic ("gfx/hud/dead");
+ mulepic = Draw_CachePic ("gfx/hud/mule");
+ fragpic = Draw_CachePic ("gfx/hud/frag");
+ bettypic = Draw_CachePic ("gfx/hud/betty");
+
+ b_circle = Draw_CachePic ("gfx/butticons/circle");
+ b_square = Draw_CachePic ("gfx/butticons/square");
+ b_cross = Draw_CachePic ("gfx/butticons/cross");
+ b_triangle = Draw_CachePic ("gfx/butticons/triangle");
+ b_left = Draw_CachePic ("gfx/butticons/left");
+ b_right = Draw_CachePic ("gfx/butticons/right");
+ b_up = Draw_CachePic ("gfx/butticons/up");
+ b_down = Draw_CachePic ("gfx/butticons/down");
+ b_lt = Draw_CachePic ("gfx/butticons/lt");
+ b_rt = Draw_CachePic ("gfx/butticons/rt");
+ b_start = Draw_CachePic ("gfx/butticons/start");
+ b_select = Draw_CachePic ("gfx/butticons/select");
+ b_home = Draw_CachePic ("gfx/butticons/home");
+
+
+ fx_blood_lu = Draw_CachePic ("gfx/hud/blood");
+ /*fx_blood_lu = Draw_CachePic ("gfx/hud/blood_tl");
+ /fx_blood_ru = Draw_CachePic ("gfx/hud/blood_tr");
+ fx_blood_ld = Draw_CachePic ("gfx/hud/blood_bl");
+ fx_blood_rd = Draw_CachePic ("gfx/hud/blood_br");*/
+
+ Achievement_Init();
+}
+
+/*
+===============
+HUD_NewMap
+===============
+*/
+void HUD_NewMap (void)
+{
+ int i;
+ alphabling = 0;
+
+ for (i=0 ; i<10 ; i++)
+ {
+ point_change[i].points = 0;
+ point_change[i].negative = 0;
+ point_change[i].x = 0.0;
+ point_change[i].y = 0.0;
+ point_change[i].move_x = 0.0;
+ point_change[i].move_y = 0.0;
+ point_change[i].alive_time = 0.0;
+ }
+
+ old_points = 500;
+ current_points = 500;
+ point_change_interval = 0;
+ point_change_interval_neg = 0;
+
+ round_center_x = (vid.width - sb_round[0]->width) /2;
+ round_center_y = (vid.height - sb_round[0]->height) /2;
+}
+
+
+/*
+=============
+HUD_itoa
+=============
+*/
+int HUD_itoa (int num, char *buf)
+{
+ char *str;
+ int pow10;
+ int dig;
+
+ str = buf;
+
+ if (num < 0)
+ {
+ *str++ = '-';
+ num = -num;
+ }
+
+ for (pow10 = 10 ; num >= pow10 ; pow10 *= 10)
+ ;
+
+ do
+ {
+ pow10 /= 10;
+ dig = num/pow10;
+ *str++ = '0'+dig;
+ num -= dig*pow10;
+ } while (pow10 != 1);
+
+ *str = 0;
+
+ return str-buf;
+}
+
+
+//=============================================================================
+
+int pointsort[MAX_SCOREBOARD];
+
+char scoreboardtext[MAX_SCOREBOARD][20];
+int scoreboardtop[MAX_SCOREBOARD];
+int scoreboardbottom[MAX_SCOREBOARD];
+int scoreboardcount[MAX_SCOREBOARD];
+int scoreboardlines;
+
+/*
+===============
+HUD_Sorpoints
+===============
+*/
+void HUD_Sortpoints (void)
+{
+ int i, j, k;
+
+// sort by points
+ scoreboardlines = 0;
+ for (i=0 ; iname[0])
+ continue;
+
+ Draw_String (x, 78 + y, s->name);
+
+ d = strlen (va("%i",s->kills));
+ Draw_String (x + (20 - d)*8, 78 + y, va("%i",s->kills));
+
+ d = strlen (va("%i",s->points));
+ Draw_String (x + (31 - d)*8, 78 + y, va("%i",s->points));
+ y += 10;
+ }
+
+}
+
+
+//=============================================================================
+
+ //=============================================================================//
+ //===============================DRAW FUNCTIONS================================//
+//=============================================================================//
+
+/*
+==================
+HUD_Points
+
+==================
+*/
+
+
+void HUD_Parse_Point_Change (int points, int negative, int x_start, int y_start)
+{
+ int i, f;
+ char str[10];
+ i=9;
+ while (i>0)
+ {
+ point_change[i] = point_change[i - 1];
+ i--;
+ }
+
+ point_change[i].points = points;
+ point_change[i].negative = negative;
+
+ f = HUD_itoa (points, str);
+ point_change[i].x = x_start - 10.0 - 8.0*f;
+ point_change[i].y = y_start;
+ point_change[i].move_x = 1.0;
+ point_change[i].move_y = ((rand ()&0x7fff) / ((float)0x7fff)) - 0.5;
+
+ point_change[i].alive_time = Sys_FloatTime() + 0.4;
+}
+
+void HUD_Points (void)
+{
+ int i, k, l;
+ int x, y, f, xplus;
+ scoreboard_t *s;
+ char str[12];
+
+// scores
+ HUD_Sortpoints ();
+
+// draw the text
+ l = scoreboardlines;
+
+
+ x = vid.width - sb_moneyback->width;
+ y = vid.height - 16 - fragpic->height - 4 - 16 - sb_moneyback->height;
+ for (i=0 ; iname[0])
+ continue;
+
+ // draw background
+
+ // draw number
+ f = s->points;
+ if (f > current_points)
+ {
+ point_change_interval_neg = 0;
+ if (!point_change_interval)
+ {
+ point_change_interval = (int)(f - old_points)/55;;
+ }
+ current_points = old_points + point_change_interval;
+ if (f < current_points)
+ {
+ current_points = f;
+ point_change_interval = 0;
+ }
+ }
+ else if (f < current_points)
+ {
+ point_change_interval = 0;
+ if (!point_change_interval_neg)
+ {
+ point_change_interval_neg = (int)(old_points - f)/55;
+ }
+ current_points = old_points - point_change_interval_neg;
+ if (f > current_points)
+ {
+ current_points = f;
+ point_change_interval_neg = 0;
+ }
+ }
+ Draw_Pic (x, y, sb_moneyback);
+ xplus = HUD_itoa (f, str);
+ Draw_String (vid.width - (xplus*8) - 16, y + 3, va("%i", current_points));
+
+ if (old_points != f)
+ {
+ if (f > old_points)
+ HUD_Parse_Point_Change(f - old_points, 0, vid.width - (xplus*8) - 16, y + 3);
+ else
+ HUD_Parse_Point_Change(old_points - f, 1, vid.width - (xplus*8) - 16, y + 3);
+
+ old_points = f;
+ }
+
+
+
+ y += 10;
+ }
+}
+
+
+/*
+==================
+HUD_Point_Change
+
+==================
+*/
+void HUD_Point_Change (void)
+{
+ int i;
+
+ for (i=0 ; i<10 ; i++)
+ {
+ if (point_change[i].points)
+ {
+ if (point_change[i].negative)
+ Draw_ColoredString (point_change[i].x, point_change[i].y, va ("-%i", point_change[i].points), 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString (point_change[i].x, point_change[i].y, va ("+%i", point_change[i].points), 255, 255, 0, 255, 1);
+ point_change[i].y = point_change[i].y + point_change[i].move_y;
+ point_change[i].x = point_change[i].x - point_change[i].move_x;
+ if (point_change[i].alive_time && point_change[i].alive_time < Sys_FloatTime())
+ {
+ point_change[i].points = 0;
+ point_change[i].negative = 0;
+ point_change[i].x = 0.0;
+ point_change[i].y = 0.0;
+ point_change[i].move_x = 0.0;
+ point_change[i].move_y = 0.0;
+ point_change[i].alive_time = 0.0;
+ }
+ }
+ }
+}
+
+
+/*
+==================
+HUD_Blood
+
+==================
+*/
+void HUD_Blood (void)
+{
+ float alpha;
+ //blubswillrule:
+ //this function scales linearly from health = 0 to health = 100
+ //alpha = (100.0 - (float)cl.stats[STAT_HEALTH])/100*255;
+ //but we want the picture to be fully visible at health = 20, so use this function instead
+ alpha = (100.0 - ((1.25 * (float) cl.stats[STAT_HEALTH]) - 25))/100*255;
+
+ if (alpha <= 0.0)
+ return;
+
+ #ifdef PSP_VFPU
+ float modifier = (vfpu_sinf(cl.time * 10) * 20) - 20;//always negative
+ #else
+ float modifier = (sin(cl.time * 10) * 20) - 20;//always negative
+ #endif
+
+ if(modifier < -35.0)
+ modifier = -35.0;
+
+ alpha += modifier;
+
+ if(alpha < 0.0)
+ return;
+ float color = 255.0 + modifier;
+
+ Draw_ColorPic(0,0,fx_blood_lu,color,color,color,alpha);
+ //Draw_ColorPic (0, 0, fx_blood_lu, 82, 6, 6, alpha);
+ /*Draw_ColorPic (0, vid.height - fx_blood_ru->height, fx_blood_ld, 82, 6, 6, alpha);
+ Draw_ColorPic (vid.width - fx_blood_ru->width, 0, fx_blood_ru, 82, 6, 6, alpha);
+ Draw_ColorPic (vid.width - fx_blood_ru->width, vid.height - fx_blood_ru->height, fx_blood_rd, 82, 6, 6, alpha);*/
+}
+
+/*
+===============
+HUD_GetWorldText
+===============
+*/
+
+// modified from scatter's worldspawn parser
+// FIXME - unoptimized, could probably save a bit of
+// memory here in the future.
+void HUD_WorldText(int alpha)
+{
+ // for parser
+ char key[128], value[4096];
+ char *data;
+
+ // first, parse worldspawn
+ data = COM_Parse(cl.worldmodel->entities);
+
+ if (!data)
+ return; // err
+ if (com_token[0] != '{')
+ return; // err
+
+ while(1) {
+ data = COM_Parse(data);
+
+ if (!data)
+ return; // err
+ if (com_token[0] == '}')
+ break; // end of worldspawn
+
+ if (com_token[0] == '_')
+ strcpy(key, com_token + 1);
+ else
+ strcpy(key, com_token);
+
+ while (key[strlen(key)-1] == ' ') // remove trailing spaces
+ key[strlen(key)-1] = 0;
+
+ data = COM_Parse(data);
+ if (!data)
+ return; // err
+
+ strcpy(value, com_token);
+
+ if (!strcmp("location", key)) // search for location key
+ {
+ Draw_ColoredString(4, vid.height/2 + 50, value, 255, 255, 255, alpha, 1);
+ }
+ if (!strcmp("date", key)) // search for date key
+ {
+ Draw_ColoredString(4, vid.height/2 + 60, value, 255, 255, 255, alpha, 1);
+ }
+ if (!strcmp("person", key)) // search for person key
+ {
+ Draw_ColoredString(4, vid.height/2 + 70, value, 255, 255, 255, alpha, 1);
+ }
+ }
+}
+
+/*
+===============
+HUD_MaxAmmo
+===============
+*/
+int maxammoy;
+int maxammoopac;
+
+void HUD_MaxAmmo(void)
+{
+ maxammoy -= cl.time * 0.003;
+ maxammoopac -= 5;
+
+ Draw_ColoredString(vid.width/2 - strlen("MAX AMMO!")*4, maxammoy, "MAX AMMO!", 255, 255, 255, maxammoopac, 1);
+
+ if (maxammoopac <= 0) {
+ domaxammo = false;
+ }
+}
+
+/*
+===============
+HUD_Rounds
+===============
+*/
+
+float color_shift[3];
+float color_shift_end[3];
+float color_shift_steps[3];
+int color_shift_init;
+int blinking;
+int textstate;
+int value, value2;
+
+int reallydumbvalue;
+int f222;
+int maxammoy;
+int maxammoopac;
+
+void HUD_Rounds (void)
+{
+ int i, x_offset, icon_num, savex;
+ int num[3];
+ x_offset = 0;
+ savex = 0;
+
+ // Round and Title text - moto
+ // extra creds to scatterbox for some x/y vals
+ // ------------------
+ // First, fade from white to red, ~3s duration
+ if (!textstate) {
+ if (!value)
+ value = 255;
+
+ Draw_ColoredString(vid.width/2 - strlen("Round")*8, 80, "Round", 255, value, value, 255, 2);
+
+ value -= cl.time * 0.4;
+
+ // prep values for next stage
+ if (value <= 0) {
+ value = 255;
+ value2 = 0;
+ textstate = 1;
+ }
+ }
+ // Now, fade out, and start fading worldtext in
+ // ~3s for fade out,
+ else if (textstate == 1) {
+ Draw_ColoredString(vid.width/2 - strlen("Round")*8, 80, "Round", 255, 0, 0, value, 2);
+
+ HUD_WorldText(value2);
+ Draw_ColoredString(4, vid.height/2 + 40, "'Nazi Zombies'", 255, 255, 255, value2, 1);
+
+ value -= cl.time * 0.4;
+ value2 += cl.time * 0.4;
+
+ // prep values for next stage
+ if (value <= 0) {
+ value2 = 0;
+ textstate = 2;
+ }
+ }
+ // Hold world text for a few seconds
+ else if (textstate == 2) {
+ HUD_WorldText(255);
+ Draw_ColoredString(4, vid.height/2 + 40, "'Nazi Zombies'", 255, 255, 255, 255, 1);
+
+ value2 += cl.time * 0.4;
+
+ if (value2 >= 255) {
+ value2 = 255;
+ textstate = 3;
+ }
+ }
+ // Fade worldtext out, finally.
+ else if (textstate == 3) {
+ HUD_WorldText(value2);
+ Draw_ColoredString(4, vid.height/2 + 40, "'Nazi Zombies'", 255, 255, 255, value2, 1);
+
+ value2 -= cl.time * 0.4;
+
+ // prep values for next stage
+ if (value2 <= 0) {
+ textstate = -1;
+ }
+ }
+ // ------------------
+ // End Round and Title text - moto
+
+ if (cl.stats[STAT_ROUNDCHANGE] == 1)//this is the rounds icon at the middle of the screen
+ {
+ if (textstate == -1) {
+ value = 0;
+ value2 = 0;
+ textstate = 0;
+ }
+
+ Draw_ColorPic ((vid.width - sb_round[0]->width) /2, (vid.height - sb_round[0]->height) /2, sb_round[0], 107, 1, 0, alphabling);
+
+ alphabling = alphabling + 15;
+
+ if (alphabling < 0)
+ alphabling = 0;
+ else if (alphabling > 255)
+ alphabling = 255;
+ }
+ else if (cl.stats[STAT_ROUNDCHANGE] == 2)//this is the rounds icon moving from middle
+ {
+ Draw_ColorPic (round_center_x, round_center_y, sb_round[0], 107, 1, 0, 255);
+ round_center_x = round_center_x - (229/108) - 0.2;
+ round_center_y = round_center_y + 1;
+ if (round_center_x <= 5)
+ round_center_x = 5;
+ if (round_center_y >= 220)
+ round_center_y = 220;
+ }
+ else if (cl.stats[STAT_ROUNDCHANGE] == 3)//shift to white
+ {
+ if (!color_shift_init)
+ {
+ color_shift[0] = 107;
+ color_shift[1] = 1;
+ color_shift[2] = 0;
+ for (i = 0; i < 3; i++)
+ {
+ color_shift_end[i] = 255;
+ color_shift_steps[i] = (color_shift_end[i] - color_shift[i])/60;
+ }
+ color_shift_init = 1;
+ }
+ for (i = 0; i < 3; i++)
+ {
+ if (color_shift[i] < color_shift_end[i])
+ color_shift[i] = color_shift[i] + color_shift_steps[i];
+
+ if (color_shift[i] >= color_shift_end[i])
+ color_shift[i] = color_shift_end[i];
+ }
+ if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11)
+ {
+
+ for (i = 0; i < cl.stats[STAT_ROUNDS]; i++)
+ {
+ if (i == 4)
+ {
+ Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+ savex = x_offset + 10;
+ x_offset = x_offset + 10;
+ continue;
+ }
+ if (i == 9)
+ {
+ Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+ continue;
+ }
+ if (i > 4)
+ icon_num = i - 5;
+ else
+ icon_num = i;
+
+ Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+
+ x_offset = x_offset + sb_round[icon_num]->width + 3;
+ }
+ }
+ else
+ {
+ if (cl.stats[STAT_ROUNDS] >= 100)
+ {
+ num[2] = (int)(cl.stats[STAT_ROUNDS]/100);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+ x_offset = x_offset + sb_round_num[num[2]]->width - 8;
+ }
+ else
+ num[2] = 0;
+ if (cl.stats[STAT_ROUNDS] >= 10)
+ {
+ num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+ x_offset = x_offset + sb_round_num[num[1]]->width - 8;
+ }
+ else
+ num[1] = 0;
+
+ num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10;
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+ x_offset = x_offset + sb_round_num[num[0]]->width - 8;
+ }
+ }
+ else if (cl.stats[STAT_ROUNDCHANGE] == 4)//blink white
+ {
+ blinking = ((int)(realtime*1000)&510) - 255;
+ blinking = abs(blinking);
+ if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11)
+ {
+ for (i = 0; i < cl.stats[STAT_ROUNDS]; i++)
+ {
+ if (i == 4)
+ {
+ Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking);
+ savex = x_offset + 10;
+ x_offset = x_offset + 10;
+ continue;
+ }
+ if (i == 9)
+ {
+ Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking);
+ continue;
+ }
+ if (i > 4)
+ icon_num = i - 5;
+ else
+ icon_num = i;
+
+ Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], 255, 255, 255, blinking);
+
+ x_offset = x_offset + sb_round[icon_num]->width + 3;
+ }
+ }
+ else
+ {
+ if (cl.stats[STAT_ROUNDS] >= 100)
+ {
+ num[2] = (int)(cl.stats[STAT_ROUNDS]/100);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], 255, 255, 255, blinking);
+ x_offset = x_offset + sb_round_num[num[2]]->width - 8;
+ }
+ else
+ num[2] = 0;
+ if (cl.stats[STAT_ROUNDS] >= 10)
+ {
+ num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], 255, 255, 255, blinking);
+ x_offset = x_offset + sb_round_num[num[1]]->width - 8;
+ }
+ else
+ num[1] = 0;
+
+ num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10;
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], 255, 255, 255, blinking);
+ x_offset = x_offset + sb_round_num[num[0]]->width - 8;
+ }
+ }
+ else if (cl.stats[STAT_ROUNDCHANGE] == 5)//blink white
+ {
+ if (blinking > 0)
+ blinking = blinking - 10;
+ if (blinking < 0)
+ blinking = 0;
+ if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11)
+ {
+ for (i = 0; i < cl.stats[STAT_ROUNDS]; i++)
+ {
+ if (i == 4)
+ {
+ Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking);
+ savex = x_offset + 10;
+ x_offset = x_offset + 10;
+ continue;
+ }
+ if (i == 9)
+ {
+ Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking);
+ continue;
+ }
+ if (i > 4)
+ icon_num = i - 5;
+ else
+ icon_num = i;
+
+ Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], 255, 255, 255, blinking);
+
+ x_offset = x_offset + sb_round[icon_num]->width + 3;
+ }
+ }
+ else
+ {
+ if (cl.stats[STAT_ROUNDS] >= 100)
+ {
+ num[2] = (int)(cl.stats[STAT_ROUNDS]/100);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], 255, 255, 255, blinking);
+ x_offset = x_offset + sb_round_num[num[2]]->width - 8;
+ }
+ else
+ num[2] = 0;
+ if (cl.stats[STAT_ROUNDS] >= 10)
+ {
+ num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], 255, 255, 255, blinking);
+ x_offset = x_offset + sb_round_num[num[1]]->width - 8;
+ }
+ else
+ num[1] = 0;
+
+ num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10;
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], 255, 255, 255, blinking);
+ x_offset = x_offset + sb_round_num[num[0]]->width - 8;
+ }
+ }
+ else if (cl.stats[STAT_ROUNDCHANGE] == 6)//blink white while fading back
+ {
+ color_shift_init = 0;
+ blinking = ((int)(realtime*1000)&510) - 255;
+ blinking = abs(blinking);
+ if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11)
+ {
+ for (i = 0; i < cl.stats[STAT_ROUNDS]; i++)
+ {
+ if (i == 4)
+ {
+ Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking);
+ savex = x_offset + 10;
+ x_offset = x_offset + 10;
+ continue;
+ }
+ if (i == 9)
+ {
+ Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking);
+ continue;
+ }
+ if (i > 4)
+ icon_num = i - 5;
+ else
+ icon_num = i;
+
+ Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], 255, 255, 255, blinking);
+
+ x_offset = x_offset + sb_round[icon_num]->width + 3;
+ }
+ }
+ else
+ {
+ if (cl.stats[STAT_ROUNDS] >= 100)
+ {
+ num[2] = (int)(cl.stats[STAT_ROUNDS]/100);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], 255, 255, 255, blinking);
+ x_offset = x_offset + sb_round_num[num[2]]->width - 8;
+ }
+ else
+ num[2] = 0;
+ if (cl.stats[STAT_ROUNDS] >= 10)
+ {
+ num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], 255, 255, 255, blinking);
+ x_offset = x_offset + sb_round_num[num[1]]->width - 8;
+ }
+ else
+ num[1] = 0;
+
+ num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10;
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], 255, 255, 255, blinking);
+ x_offset = x_offset + sb_round_num[num[0]]->width - 8;
+ }
+ }
+ else if (cl.stats[STAT_ROUNDCHANGE] == 7)//blink white while fading back
+ {
+ if (!color_shift_init)
+ {
+ color_shift_end[0] = 107;
+ color_shift_end[1] = 1;
+ color_shift_end[2] = 0;
+ for (i = 0; i < 3; i++)
+ {
+ color_shift[i] = 255;
+ color_shift_steps[i] = (color_shift[i] - color_shift_end[i])/60;
+ }
+ color_shift_init = 1;
+ }
+ for (i = 0; i < 3; i++)
+ {
+ if (color_shift[i] > color_shift_end[i])
+ color_shift[i] = color_shift[i] - color_shift_steps[i];
+
+ if (color_shift[i] < color_shift_end[i])
+ color_shift[i] = color_shift_end[i];
+ }
+ if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11)
+ {
+ for (i = 0; i < cl.stats[STAT_ROUNDS]; i++)
+ {
+ if (i == 4)
+ {
+ Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+ savex = x_offset + 10;
+ x_offset = x_offset + 10;
+ continue;
+ }
+ if (i == 9)
+ {
+ Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+ continue;
+ }
+ if (i > 4)
+ icon_num = i - 5;
+ else
+ icon_num = i;
+
+ Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+
+ x_offset = x_offset + sb_round[icon_num]->width + 3;
+ }
+ }
+ else
+ {
+ if (cl.stats[STAT_ROUNDS] >= 100)
+ {
+ num[2] = (int)(cl.stats[STAT_ROUNDS]/100);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+ x_offset = x_offset + sb_round_num[num[2]]->width - 8;
+ }
+ else
+ num[2] = 0;
+ if (cl.stats[STAT_ROUNDS] >= 10)
+ {
+ num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+ x_offset = x_offset + sb_round_num[num[1]]->width - 8;
+ }
+ else
+ num[1] = 0;
+
+ num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10;
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255);
+ x_offset = x_offset + sb_round_num[num[0]]->width - 8;
+ }
+ }
+ else
+ {
+ color_shift[0] = 107;
+ color_shift[1] = 1;
+ color_shift[2] = 0;
+ color_shift_init = 0;
+ alphabling = 0;
+ if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11)
+ {
+ for (i = 0; i < cl.stats[STAT_ROUNDS]; i++)
+ {
+ if (i == 4)
+ {
+ Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], 107, 1, 0, 255);
+ savex = x_offset + 10;
+ x_offset = x_offset + 10;
+ continue;
+ }
+ if (i == 9)
+ {
+ Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], 107, 1, 0, 255);
+ continue;
+ }
+ if (i > 4)
+ icon_num = i - 5;
+ else
+ icon_num = i;
+
+ Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], 107, 1, 0, 255);
+
+ x_offset = x_offset + sb_round[icon_num]->width + 3;
+ }
+ }
+ else
+ {
+ if (cl.stats[STAT_ROUNDS] >= 100)
+ {
+ num[2] = (int)(cl.stats[STAT_ROUNDS]/100);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], 107, 1, 0, 255);
+ x_offset = x_offset + sb_round_num[num[2]]->width - 8;
+ }
+ else
+ num[2] = 0;
+ if (cl.stats[STAT_ROUNDS] >= 10)
+ {
+ num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10);
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], 107, 1, 0, 255);
+ x_offset = x_offset + sb_round_num[num[1]]->width - 8;
+ }
+ else
+ num[1] = 0;
+
+ num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10;
+
+ if(cl.stats[STAT_ROUNDS] == 0)
+ return;
+
+ Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], 107, 1, 0, 255);
+ x_offset = x_offset + sb_round_num[num[0]]->width - 8;
+ }
+ }
+}
+
+/*
+===============
+HUD_Perks
+===============
+*/
+#define P_JUG 1
+#define P_DOUBLE 2
+#define P_SPEED 4
+#define P_REVIVE 8
+#define P_FLOP 16
+#define P_STAMIN 32
+#define P_DEAD 64
+#define P_MULE 128
+
+int perk_order[9];
+int current_perk_order;
+
+void HUD_Perks (void)
+{
+ int i;
+ int y;
+ y = vid.height - sb_round[1]->height - jugpic->height -2;
+
+ for (i = 0; i < 9; i++)
+ {
+ if (perk_order[i])
+ {
+ if (perk_order[i] == P_JUG)
+ {
+ Draw_StretchPic (2, y, jugpic, 20, 20);
+ y = y - 22;
+ }
+ else if (perk_order[i] == P_DOUBLE)
+ {
+ Draw_StretchPic (2, y, doublepic, 20, 20);
+ y = y - 22;
+ }
+ else if (perk_order[i] == P_SPEED)
+ {
+ Draw_StretchPic (2, y, speedpic, 20, 20);
+ y = y - 22;
+ }
+ else if (perk_order[i] == P_REVIVE)
+ {
+ Draw_StretchPic (2, y, revivepic, 20, 20);
+ y = y - 22;
+ }
+ else if (perk_order[i] == P_FLOP)
+ {
+ Draw_StretchPic (2, y, floppic, 20, 20);
+ y = y - 22;
+ }
+ else if (perk_order[i] == P_STAMIN)
+ {
+ Draw_StretchPic (2, y, staminpic, 20, 20);
+ y = y - 22;
+ }
+ else if (perk_order[i] == P_DEAD)
+ {
+ Draw_StretchPic (2, y, deadpic, 20, 20);
+ y = y - 22;
+ }
+ else if (perk_order[i] == P_MULE)
+ {
+ Draw_StretchPic (2, y, mulepic, 20, 20);
+ y = y - 22;
+ }
+ }
+ }
+}
+
+/*
+===============
+HUD_Powerups
+===============
+*/
+void HUD_Powerups (void)
+{
+
+ if(cl.stats[STAT_X2])
+ Draw_StretchPic ((vid.width/2) - 30,0, x2pic, 28, 28);
+ if(cl.stats[STAT_INSTA])
+ Draw_StretchPic ((vid.width/2) + 2,0, instapic, 28, 28);
+}
+
+/*
+===============
+HUD_ProgressBar
+===============
+*/
+void HUD_ProgressBar (void)
+{
+ float progressbar;
+
+ if (cl.progress_bar)
+ {
+ progressbar = 100 - ((cl.progress_bar-sv.time)*10);
+ if (progressbar >= 100)
+ progressbar = 100;
+ Draw_FillByColor ((vid.width)/2 - 51, vid.height*0.75 - 1, 102, 5, GU_RGBA(0, 0, 0,100));
+ Draw_FillByColor ((vid.width)/2 - 50, vid.height*0.75, progressbar, 3, GU_RGBA(255, 255, 255,100));
+
+ Draw_String ((vid.width - (88))/2, vid.height*0.75 + 10, "Reviving...");
+ }
+}
+
+/*
+===============
+HUD_Achievement
+
+Achievements based on code by Arkage
+===============
+*/
+
+
+int achievement; // the background image
+int achievement_unlocked;
+char achievement_text[MAX_QPATH];
+double achievement_time;
+float smallsec;
+int ach_pic;
+void HUD_Achievement (void)
+{
+ if (achievement_unlocked == 1)
+ {
+ smallsec = smallsec + 0.7;
+ if (smallsec >= 55)
+ smallsec = 55;
+ //Background image
+ //Sbar_DrawPic (176, 5, achievement);
+ // The achievement text
+ Draw_AlphaPic (30, smallsec - 50, achievement_list[ach_pic].img, 0.7f);
+ }
+
+ // Reset the achievement
+ if (Sys_FloatTime() >= achievement_time)
+ {
+ achievement_unlocked = 0;
+ }
+}
+
+void HUD_Parse_Achievement (int ach)
+{
+ if (achievement_list[ach].unlocked)
+ return;
+
+ achievement_unlocked = 1;
+ smallsec = 0;
+ achievement_time = Sys_FloatTime() + 10;
+ ach_pic = ach;
+ achievement_list[ach].unlocked = 1;
+ Save_Achivements();
+}
+
+/*
+===============
+HUD_Ammo
+===============
+*/
+
+int GetLowAmmo(int weapon, int type)
+{
+ switch (weapon)
+ {
+ case W_COLT: if (type) return 2; else return 16;
+ case W_KAR: if (type) return 1; else return 10;
+ case W_KAR_SCOPE: if (type) return 1; else return 10;
+ case W_M1A1: if (type) return 4; else return 24;
+ case W_SAWNOFF: if (type) return 1; else return 12;
+ case W_DB: if (type) return 1; else return 12;
+ case W_THOMPSON: if (type) return 6; else return 40;
+ case W_BAR: if (type) return 6; else return 28;
+ default: return 0;
+ }
+}
+
+int IsDualWeapon(int weapon)
+{
+ switch(weapon) {
+ case W_BIATCH:
+ case W_SNUFF:
+ return 1;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+void HUD_Ammo (void)
+{
+ char str[12];
+ int xplus;
+ char *magstring;
+
+ y_value = vid.height - 16;
+
+ magstring = va("%i", cl.stats[STAT_CURRENTMAG]);
+
+ xplus = HUD_itoa(cl.stats[STAT_CURRENTMAG], str);
+
+ // Magazine
+ if (GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 1) >= cl.stats[STAT_CURRENTMAG])
+ Draw_ColoredString(vid.width - 42 - (xplus*8), y_value, magstring, 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(vid.width - 42 - (xplus*8), y_value, magstring, 255, 255, 255, 255, 1);
+
+ // Second mag for dual weps
+ if (IsDualWeapon(cl.stats[STAT_ACTIVEWEAPON])) {
+ magstring = va("%i", cl.stats[STAT_CURRENTMAG2]);
+ xplus = HUD_itoa(cl.stats[STAT_CURRENTMAG2], str);
+
+ if (GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 1) >= cl.stats[STAT_CURRENTMAG2])
+ Draw_ColoredString(vid.width - 56 - (xplus*8), y_value, magstring, 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(vid.width - 56 - (xplus*8), y_value, magstring, 255, 255, 255, 255, 1);
+ }
+
+ // Reserve ammo
+ if (GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 0) >= cl.stats[STAT_AMMO])
+ {
+ Draw_ColoredString (vid.width - 42, y_value, "/", 255, 0, 0, 255, 1);
+ Draw_ColoredString (vid.width - 34, y_value, va ("%i",cl.stats[STAT_AMMO]), 255, 0, 0, 255, 1);
+ }
+ else
+ {
+ Draw_ColoredString (vid.width - 42, y_value, "/", 255, 255, 255, 255, 1);
+ Draw_ColoredString (vid.width - 34, y_value, va ("%i",cl.stats[STAT_AMMO]), 255, 255, 255, 255, 1);
+ }
+}
+
+/*
+===============
+HUD_AmmoString
+===============
+*/
+
+void HUD_AmmoString (void)
+{
+ if (GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 1) >= cl.stats[STAT_CURRENTMAG])
+ {
+ if (0 < cl.stats[STAT_AMMO] && cl.stats[STAT_CURRENTMAG] >= 0) {
+ Draw_ColoredString ((vid.width)/2, (vid.height)/2 + 40, "Reload", 255, 255, 255, 255, 1);
+ } else if (0 < cl.stats[STAT_CURRENTMAG]) {
+ Draw_ColoredString ((vid.width)/2, (vid.height)/2 + 40, "LOW AMMO", 255, 255, 0, 255, 1);
+ } else {
+ Draw_ColoredString ((vid.width)/2, (vid.height)/2 + 40, "NO AMMO", 255, 0, 0, 255, 1);
+ }
+ }
+}
+
+/*
+===============
+HUD_Grenades
+===============
+*/
+#define UI_FRAG 1
+#define UI_BETTY 2
+
+void HUD_Grenades (void)
+{
+ if (cl.stats[STAT_GRENADES])
+ {
+ x_value = vid.width - 50;
+ y_value = vid.height - 16 - fragpic->height - 4;
+ }
+ if (cl.stats[STAT_GRENADES] & UI_FRAG)
+ {
+ Draw_StretchPic (x_value, y_value, fragpic, 22, 22);
+ if (cl.stats[STAT_PRIGRENADES] <= 0)
+ Draw_ColoredString (x_value + 12, y_value + 12, va ("%i",cl.stats[STAT_PRIGRENADES]), 255, 0, 0, 255, 1);
+ else
+ Draw_String (x_value + 12, y_value + 12, va ("%i",cl.stats[STAT_PRIGRENADES]));
+ }
+ if (cl.stats[STAT_GRENADES] & UI_BETTY)
+ {
+ Draw_StretchPic (x_value - fragpic->width - 5, y_value, bettypic, 22, 22);
+ Draw_String (x_value - fragpic->width + 7, y_value + 12, va ("%i",cl.stats[STAT_SECGRENADES]));
+ }
+}
+
+/*
+===============
+HUD_Weapon
+===============
+*/
+void HUD_Weapon (void)
+{
+ char str[32];
+ float l;
+ x_value = vid.width;
+ y_value = vid.height - 16 - fragpic->height - 4 - 16;
+
+ strcpy(str, pr_strings+sv_player->v.Weapon_Name);
+ l = strlen(str);
+
+ x_value = vid.width - 8 - l*8;
+ Draw_String (x_value, y_value, str);
+}
+
+/*
+===============
+HUD_Draw
+===============
+*/
+void HUD_Draw (void)
+{
+ if (scr_con_current == vid.height)
+ return; // console is full screen
+
+ if (key_dest == key_menu_pause)
+ return;
+
+ scr_copyeverything = 1;
+
+
+ if (waypoint_mode.value)
+ {
+ Draw_String (vid.width - 112, 0, "WAYPOINTMODE");
+ Draw_String (vid.width - 240, 8, "Press fire to create waypoint");
+ Draw_String (vid.width - 232, 16, "Press use to select waypoint");
+ Draw_String (vid.width - 216, 24, "Press aim to link waypoint");
+ Draw_String (vid.width - 248, 32, "Press knife to remove waypoint");
+ Draw_String (vid.width - 272, 40, "Press switch to move waypoint here");
+ Draw_String (vid.width - 304, 48, "Press reload to make special waypoint");
+ return;
+ }
+
+ if (cl.stats[STAT_HEALTH] <= 0)
+ {
+ HUD_EndScreen ();
+ return;
+ }
+
+ HUD_Blood();
+ HUD_Rounds();
+ HUD_Perks();
+ HUD_Powerups();
+ HUD_ProgressBar();
+ if ((HUD_Change_time > Sys_FloatTime() || GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 1) >= cl.stats[STAT_CURRENTMAG] || GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 0) >= cl.stats[STAT_AMMO]) && cl.stats[STAT_HEALTH] >= 20)
+ { //these elements are only drawn when relevant for few seconds
+ HUD_Ammo();
+ HUD_Grenades();
+ HUD_Weapon();
+ HUD_AmmoString();
+ }
+ HUD_Points();
+ HUD_Point_Change();
+ HUD_Achievement();
+
+ if (domaxammo == true) {
+ if (maxammoopac <= 0) {
+ maxammoopac = 255;
+ maxammoy = 250;
+ }
+ HUD_MaxAmmo();
+ }
+}
diff --git a/source/cl_hud.h b/source/cl_hud.h
new file mode 100644
index 0000000..ac41220
--- /dev/null
+++ b/source/cl_hud.h
@@ -0,0 +1,53 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+// the status bar is only redrawn if something has changed, but if anything
+// does, the entire thing will be redrawn for the next vid.numpages frames.
+
+void HUD_Init (void);
+
+// call whenever any of the client stats represented on the sbar changes
+
+void HUD_Draw (void);
+// called every frame by screen
+
+// called each frame after the level has been completed
+void HUD_NewMap (void);
+
+extern double HUD_Change_time;
+
+
+
+//achievement stuff
+#define MAX_ACHIEVEMENTS 5//23
+typedef struct achievement_list_s
+{
+ qpic_t *img;
+ int unlocked;
+ char name[64];
+ char description[256];
+ int progress;
+} achievement_list_t;
+
+void Achievement_Init (void);
+extern achievement_list_t achievement_list[MAX_ACHIEVEMENTS];
+extern qpic_t *achievement_locked;
+
+void HUD_Parse_Achievement (int ach);
diff --git a/source/cl_input.c b/source/cl_input.c
new file mode 100644
index 0000000..49d9fb1
--- /dev/null
+++ b/source/cl_input.c
@@ -0,0 +1,696 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// cl.input.c -- builds an intended movement command to send to the server
+
+// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
+// rights reserved.
+
+#include "quakedef.h"
+
+/*
+===============================================================================
+
+KEY BUTTONS
+
+Continuous button event tracking is complicated by the fact that two different
+input sources (say, mouse button 1 and the control key) can both press the
+same button, but the button should only be released when both of the
+pressing key have been released.
+
+When a key event issues a button command (+forward, +attack, etc), it appends
+its key number as a parameter to the command so it can be matched up with
+the release.
+
+state bit 0 is the current state of the key
+state bit 1 is edge triggered on the up to down transition
+state bit 2 is edge triggered on the down to up transition
+
+===============================================================================
+*/
+
+
+kbutton_t in_klook;//Heffo - mlook cvar
+kbutton_t in_left, in_right, in_forward, in_back;
+kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
+kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack, in_grenade, in_reload, in_switch, in_knife, in_aim;
+kbutton_t in_up, in_down;
+
+int in_impulse;
+
+
+
+void KeyDown (kbutton_t *b)
+{
+ int k;
+ char *c;
+
+ c = Cmd_Argv(1);
+ if (c[0])
+ k = atoi(c);
+ else
+ k = -1; // typed manually at the console for continuous down
+
+ if (k == b->down[0] || k == b->down[1])
+ return; // repeating key
+
+ if (!b->down[0])
+ b->down[0] = k;
+ else if (!b->down[1])
+ b->down[1] = k;
+ else
+ {
+ Con_Printf ("Three keys down for a button!\n");
+ return;
+ }
+
+ if (b->state & 1)
+ return; // still down
+ b->state |= 1 + 2; // down + impulse down
+}
+
+void KeyUp (kbutton_t *b)
+{
+ int k;
+ char *c;
+
+ c = Cmd_Argv(1);
+ if (c[0])
+ k = atoi(c);
+ else
+ { // typed manually at the console, assume for unsticking, so clear all
+ b->down[0] = b->down[1] = 0;
+ b->state = 4; // impulse up
+ return;
+ }
+
+ if (b->down[0] == k)
+ b->down[0] = 0;
+ else if (b->down[1] == k)
+ b->down[1] = 0;
+ else
+ return; // key up without coresponding down (menu pass through)
+ if (b->down[0] || b->down[1])
+ return; // some other key is still holding it down
+
+ if (!(b->state & 1))
+ return; // still up (this should not happen)
+ b->state &= ~1; // now up
+ b->state |= 4; // impulse up
+}
+
+qboolean croshhairmoving = false;
+
+void IN_KLookDown (void) {KeyDown(&in_klook);}
+void IN_KLookUp (void) {KeyUp(&in_klook);}
+
+/*void IN_MLookDown (void) {KeyDown(&in_mlook);}
+void IN_MLookUp (void){
+KeyUp(&in_mlook);
+if ( !(in_mlook.state&1) && lookspring.value)
+ V_StartPitchDrift();
+} Heffo - mlook cvar*/
+
+void IN_UpDown(void) {KeyDown(&in_up);}
+void IN_UpUp(void) {KeyUp(&in_up);}
+void IN_DownDown(void) {KeyDown(&in_down);}
+void IN_DownUp(void) {KeyUp(&in_down);}
+void IN_LeftDown(void) {KeyDown(&in_left);}
+void IN_LeftUp(void) {KeyUp(&in_left);}
+void IN_RightDown(void) {KeyDown(&in_right);}
+void IN_RightUp(void) {KeyUp(&in_right);}
+void IN_ForwardDown(void) {KeyDown(&in_forward);}
+void IN_ForwardUp(void) {KeyUp(&in_forward);Cbuf_AddText("impulse 24\n");}
+void IN_BackDown(void) {KeyDown(&in_back);}
+void IN_BackUp(void) {KeyUp(&in_back);}
+void IN_LookupDown(void) {KeyDown(&in_lookup);}
+void IN_LookupUp(void) {KeyUp(&in_lookup);}
+void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
+void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
+void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
+void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
+void IN_MoverightDown(void) {KeyDown(&in_moveright);}
+void IN_MoverightUp(void) {KeyUp(&in_moveright);}
+
+void IN_SpeedDown(void) {KeyDown(&in_speed);}
+void IN_SpeedUp(void) {KeyUp(&in_speed);}
+void IN_StrafeDown(void) {KeyDown(&in_strafe);}
+void IN_StrafeUp(void) {KeyUp(&in_strafe);}
+
+void IN_AttackDown(void) {KeyDown(&in_attack);}
+void IN_AttackUp(void) {KeyUp(&in_attack);}
+
+void IN_UseDown (void) {KeyDown(&in_use);}
+void IN_UseUp (void) {KeyUp(&in_use);}
+void IN_JumpDown (void) {KeyDown(&in_jump);}
+void IN_JumpUp (void) {KeyUp(&in_jump);}
+void IN_GrenadeDown (void) {KeyDown(&in_grenade);}
+void IN_GrenadeUp (void) {KeyUp(&in_grenade);}
+void IN_SwitchDown (void) {KeyDown(&in_switch);}
+void IN_SwitchUp (void) {KeyUp(&in_switch);}
+void IN_ReloadDown (void) {KeyDown(&in_reload);}
+void IN_ReloadUp (void) {KeyUp(&in_reload);}
+void IN_KnifeDown (void) {KeyDown(&in_knife);}
+void IN_KnifeUp (void) {KeyUp(&in_knife);}
+void IN_AimDown (void) {KeyDown(&in_aim);}
+void IN_AimUp (void) {KeyUp(&in_aim);}
+
+void IN_Impulse (void) {in_impulse=Q_atoi(Cmd_Argv(1));}
+
+/*
+===============
+CL_KeyState
+
+Returns 0.25 if a key was pressed and released during the frame,
+0.5 if it was pressed and held
+0 if held then released, and
+1.0 if held for the entire time
+===============
+*/
+float CL_KeyState (kbutton_t *key)
+{
+ float val;
+ qboolean impulsedown, impulseup, down;
+
+ impulsedown = key->state & 2;
+ impulseup = key->state & 4;
+ down = key->state & 1;
+ val = 0;
+
+ if (impulsedown && !impulseup)
+ {
+ if (down)
+ val = 0.5; // pressed and held this frame
+ else
+ val = 0; // I_Error ();
+ }
+ if (impulseup && !impulsedown)
+ {
+ if (down)
+ val = 0; // I_Error ();
+ else
+ val = 0; // released this frame
+ }
+ if (!impulsedown && !impulseup)
+ {
+ if (down)
+ val = 1.0; // held the entire frame
+ else
+ val = 0; // up the entire frame
+ }
+ if (impulsedown && impulseup)
+ {
+ if (down)
+ val = 0.75; // released and re-pressed this frame
+ else
+ val = 0.25; // pressed and released this frame
+ }
+
+ key->state &= 1; // clear impulses
+
+ return val;
+}
+
+
+
+
+//==========================================================================
+
+cvar_t cl_upspeed = {"cl_upspeed","200"};
+float cl_forwardspeed;
+float cl_backspeed;
+float cl_sidespeed;
+
+cvar_t cl_movespeedkey = {"cl_movespeedkey","2.0"};
+
+cvar_t cl_yawspeed = {"cl_yawspeed","140"};
+cvar_t cl_pitchspeed = {"cl_pitchspeed","150"};
+
+cvar_t cl_anglespeedkey = {"cl_anglespeedkey","1.5"};
+
+cvar_t in_mlook = {"in_mlook", "1", true}; //Heffo - mlook cvar
+cvar_t in_aimassist = {"in_aimassist", "1", true};
+
+
+//Shpuld - Porting over lower sens for lower fov
+extern cvar_t scr_fov;
+
+/*
+================
+CL_AdjustAngles
+
+Moves the local angle positions
+================
+*/
+
+
+extern int original_fov, final_fov;
+void CL_AdjustAngles (void)
+{
+ float speed;
+ float up, down;
+
+ if (in_speed.state & 1)
+ speed = host_frametime * cl_anglespeedkey.value;
+ else
+ speed = host_frametime;
+
+ //shpuld begin
+ speed = speed * scr_fov.value/90;
+ //speed = speed*final_fov/original_fov;
+ //shpuld end
+
+
+ if (!(in_strafe.state & 1))
+ {
+ cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right);
+ cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left);
+ cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]);
+ }
+ if (in_klook.state & 1)
+ {
+ V_StopPitchDrift ();
+ cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward);
+ cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back);
+ }
+
+ up = CL_KeyState (&in_lookup);
+ down = CL_KeyState(&in_lookdown);
+
+ cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up;
+ cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down;
+
+ if (up || down)
+ V_StopPitchDrift ();
+
+ if (cl.viewangles[PITCH] > 80)
+ cl.viewangles[PITCH] = 80;
+ if (cl.viewangles[PITCH] < -70)
+ cl.viewangles[PITCH] = -70;
+
+ if (cl.viewangles[ROLL] > 50)
+ cl.viewangles[ROLL] = 50;
+ if (cl.viewangles[ROLL] < -50)
+ cl.viewangles[ROLL] = -50;
+
+}
+
+/*
+================
+CL_BaseMove
+
+Send the intended movement message to the server
+================
+*/
+
+cvar_t waypoint_mode;
+float crosshair_opacity;
+void CL_BaseMove (usercmd_t *cmd)
+{
+ if (cls.signon != SIGNONS)//BLUBS CHANGED HERE
+ return;
+
+ CL_AdjustAngles ();
+
+ Q_memset (cmd, 0, sizeof(*cmd));
+
+ // Moto - we handle movespeed in QC now.
+ cl_backspeed = cl_forwardspeed = cl_sidespeed = sv_player->v.maxspeed;
+
+ // Throttle side and back speeds
+ cl_sidespeed *= 0.8;
+ cl_backspeed *= 0.7;
+
+ if (waypoint_mode.value)
+ cl_backspeed = cl_forwardspeed = cl_sidespeed *= 1.5;
+
+ if (in_strafe.state & 1)
+ {
+ cmd->sidemove += cl_sidespeed * CL_KeyState (&in_right);
+ cmd->sidemove -= cl_sidespeed * CL_KeyState (&in_left);
+ }
+
+ // crosshair stuff
+ croshhairmoving = true;
+ crosshair_opacity -= 8;
+ if (crosshair_opacity <= 128)
+ crosshair_opacity = 128;
+
+ cmd->sidemove += cl_sidespeed * CL_KeyState (&in_moveright);
+ cmd->sidemove -= cl_sidespeed * CL_KeyState (&in_moveleft);
+
+ cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up);
+ cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down);
+
+ if (! (in_klook.state & 1) )
+ {
+ cmd->forwardmove += cl_forwardspeed * CL_KeyState (&in_forward);
+ cmd->forwardmove -= cl_backspeed * CL_KeyState (&in_back);
+ }
+
+//
+// adjust for speed key
+//
+ if (in_speed.state & 1)
+ {
+ cmd->forwardmove *= cl_movespeedkey.value;
+ cmd->sidemove *= cl_movespeedkey.value;
+ cmd->upmove *= cl_movespeedkey.value;
+ }
+
+ // reset crosshair
+ if (!CL_KeyState (&in_moveright) && !CL_KeyState (&in_moveleft) && !CL_KeyState (&in_forward) && !CL_KeyState (&in_back)) {
+ croshhairmoving = false;
+
+ crosshair_opacity += 22;
+
+ if (crosshair_opacity >= 255)
+ crosshair_opacity = 255;
+ }
+}
+
+int infront(edict_t *ent1, edict_t *ent2)
+{
+ vec3_t vec;
+ float dot;
+ VectorSubtract(ent2->v.origin,ent1->v.origin,vec);
+ VectorNormalize(vec);
+
+ vec3_t temp_angle,temp_forward,temp_right,temp_up;
+ VectorCopy(cl.viewangles,temp_angle);
+
+ AngleVectors(temp_angle,temp_forward,temp_right,temp_up);
+
+ dot = DotProduct(vec,temp_forward);
+ if(dot > 0.98)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+int EN_Find(int num,char *string)
+{
+ edict_t *ed;
+
+ int e;
+ e = num;
+
+ for(e++; e < sv.num_edicts; e++)
+ {
+ ed = EDICT_NUM(e);
+ if(ed->free)
+ continue;
+ if(!strcmp(pr_strings + ed->v.classname,string))
+ {
+ return e;
+ }
+ }
+ return 0;
+}
+
+void CL_Aim_Snap(void)
+{
+ edict_t *z,*bz,*player;
+ int znum;
+ trace_t trace;
+ float bestDist = 10000;
+ vec3_t distVec, zOrg, pOrg;
+ //32 is v_ofs num
+
+ bz = sv.edicts;
+
+ int vofs = 32;//32 is actual v_ofs num
+ int aimOfs = -10;//30 is top of bbox, 20 is our goal, so -10
+ //Zombie body bbox vert max = 30
+ //20 is the offset of the height of the zombie that we're aiming at, 20 above the origin
+ //Crawler body bbox vert max = -15
+
+ //Equation = origin + bbox vertical offset - 20
+
+ player = EDICT_NUM(cl.viewentity);
+ VectorCopy(player->v.origin,pOrg);
+ pOrg[2] += vofs;
+
+ if (cl.perks & 64)
+ znum = EN_Find(0,"ai_zombie_head");
+ else
+ znum = EN_Find(0,"ai_zombie");
+
+ z = EDICT_NUM(znum);
+ VectorCopy(z->v.origin,zOrg);
+ zOrg[2] += z->v.maxs[2];//Setting to top of zomb ent
+ zOrg[2] += aimOfs;
+
+ while(znum != 0)
+ {
+ if((z->v.health > 0) && infront(player,z))
+ {
+ VectorCopy(z->v.origin,zOrg);
+ zOrg[2] += aimOfs;
+ VectorSubtract(pOrg,zOrg,distVec);
+ if(VectorLength(distVec) < bestDist)
+ {
+ trace = SV_Move (pOrg, vec3_origin, vec3_origin,zOrg, 1, player);
+ if (trace.fraction >= 1)
+ {
+ bestDist = VectorLength(distVec);
+ bz = z;
+ }
+ }
+ }
+ if (cl.perks & 64)
+ znum = EN_Find(znum,"ai_zombie_head");
+ else
+ znum = EN_Find(znum,"ai_zombie");
+ z = EDICT_NUM(znum);
+ }
+
+ if(bz != sv.edicts)
+ {
+ VectorCopy(bz->v.origin,zOrg);
+ zOrg[2] += bz->v.maxs[2];//Setting to top of bbox
+ zOrg[2] += aimOfs;
+ VectorSubtract(zOrg,pOrg,distVec);
+ VectorNormalize(distVec);
+ vectoangles(distVec,distVec);
+ distVec[0] += (distVec[0] > 180)? -360 : 0;//Need to bound pitch around 0, from -180, to + 180
+ distVec[0] *= -1;//inverting pitch
+
+ if(distVec[0] < -70 || distVec[0] > 80)
+ return;
+
+ VectorCopy(distVec,cl.viewangles);
+ }
+}
+
+
+/*
+==============
+CL_SendMove
+==============
+*/
+int zoom_snap;
+float angledelta(float a);
+float deltaPitch,deltaYaw;
+void CL_SendMove (usercmd_t *cmd)
+{
+ int i;
+ long int bits;
+ sizebuf_t buf;
+ byte data[128];
+ vec3_t tempv;
+ buf.maxsize = 128;
+ buf.cursize = 0;
+ buf.data = data;
+
+ cl.cmd = *cmd;
+
+ //==== Aim Assist Code ====
+ if((cl.stats[STAT_ZOOM]==1 || cl.stats[STAT_ZOOM]==2) && ((in_aimassist.value) || (cl.perks & 64)))
+ {
+ if(!zoom_snap)
+ {
+
+ CL_Aim_Snap();
+ zoom_snap = 1;
+ }
+ }
+ else
+ zoom_snap = 0;
+
+ //==== Sniper Scope Swaying ====
+ if(cl.stats[STAT_ZOOM] == 2)
+ {
+ vec3_t vang;
+
+ VectorCopy(cl.viewangles,vang);
+
+ vang[0] -= deltaPitch;
+ vang[1] -= deltaYaw;
+
+ #ifdef PSP_VFPU
+ deltaPitch =(vfpu_cosf(cl.time/0.7) + vfpu_cosf(cl.time) + vfpu_sinf(cl.time/1.1)) * 0.5;
+ deltaYaw = (vfpu_sinf(cl.time/0.4) + vfpu_cosf(cl.time/0.56) + vfpu_sinf(cl.time)) * 0.5;
+ #else
+ deltaPitch =(cos(cl.time/0.7) + cos(cl.time) + sin(cl.time/1.1)) * 0.5;
+ deltaYaw = (sin(cl.time/0.4) + cos(cl.time/0.56) + sin(cl.time)) * 0.5;
+ #endif
+
+ vang[0] += deltaPitch;
+ vang[1] += deltaYaw;
+ vang[0] = angledelta(vang[0]);
+ vang[1] = angledelta(vang[1]);
+
+ VectorCopy(vang,cl.viewangles);
+ //return 0;
+ }
+
+//
+// send the movement message
+//
+ MSG_WriteByte (&buf, clc_move);
+
+ MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times
+
+ VectorAdd(cl.gun_kick, cl.viewangles, tempv);
+ for (i=0 ; i<3 ; i++)
+ MSG_WriteFloat (&buf, tempv[i]);
+
+ MSG_WriteShort (&buf, cmd->forwardmove);
+ MSG_WriteShort (&buf, cmd->sidemove);
+ MSG_WriteShort (&buf, cmd->upmove);
+
+//
+// send button bits
+//
+ bits = 0;
+
+ if (in_attack.state & 3 )
+ bits |= 1;
+ in_attack.state &= ~2;
+
+ if (in_jump.state & 3)
+ bits |= 2;
+ in_jump.state &= ~2;
+
+ if (in_grenade.state & 3)
+ bits |= 8;
+ in_grenade.state &= ~2;
+
+ if (in_switch.state & 3)
+ bits |= 16;
+ in_switch.state &= ~2;
+
+ if (in_reload.state & 3)
+ bits |= 32;
+ in_reload.state &= ~2;
+
+ if (in_knife.state & 3)
+ bits |= 64;
+ in_knife.state &= ~2;
+
+ if (in_use.state & 3)
+ bits |= 128;
+ in_use.state &= ~2;
+
+ if (in_aim.state & 3)
+ bits |= 256;
+ in_aim.state &= ~2;
+
+ MSG_WriteLong (&buf, bits);
+
+ MSG_WriteByte (&buf, in_impulse);
+ in_impulse = 0;
+
+//
+// deliver the message
+//
+ if (cls.demoplayback)
+ return;
+
+//
+// allways dump the first two message, because it may contain leftover inputs
+// from the last level
+//
+ if (++cl.movemessages <= 2)
+ return;
+
+ if (NET_SendUnreliableMessage (cls.netcon, &buf) == -1)
+ {
+ Con_Printf ("CL_SendMove: lost server connection\n");
+ CL_Disconnect ();
+ }
+}
+
+/*
+============
+CL_InitInput
+============
+*/
+void CL_InitInput (void)
+{
+ Cmd_AddCommand ("+moveup",IN_UpDown);
+ Cmd_AddCommand ("-moveup",IN_UpUp);
+ Cmd_AddCommand ("+movedown",IN_DownDown);
+ Cmd_AddCommand ("-movedown",IN_DownUp);
+ Cmd_AddCommand ("+left",IN_LeftDown);
+ Cmd_AddCommand ("-left",IN_LeftUp);
+ Cmd_AddCommand ("+right",IN_RightDown);
+ Cmd_AddCommand ("-right",IN_RightUp);
+ Cmd_AddCommand ("+forward",IN_ForwardDown);
+ Cmd_AddCommand ("-forward",IN_ForwardUp);
+ Cmd_AddCommand ("+back",IN_BackDown);
+ Cmd_AddCommand ("-back",IN_BackUp);
+ Cmd_AddCommand ("+lookup", IN_LookupDown);
+ Cmd_AddCommand ("-lookup", IN_LookupUp);
+ Cmd_AddCommand ("+lookdown", IN_LookdownDown);
+ Cmd_AddCommand ("-lookdown", IN_LookdownUp);
+ Cmd_AddCommand ("+strafe", IN_StrafeDown);
+ Cmd_AddCommand ("-strafe", IN_StrafeUp);
+ Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
+ Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
+ Cmd_AddCommand ("+moveright", IN_MoverightDown);
+ Cmd_AddCommand ("-moveright", IN_MoverightUp);
+ Cmd_AddCommand ("+speed", IN_SpeedDown);
+ Cmd_AddCommand ("-speed", IN_SpeedUp);
+ Cmd_AddCommand ("+attack", IN_AttackDown);
+ Cmd_AddCommand ("-attack", IN_AttackUp);
+ Cmd_AddCommand ("+use", IN_UseDown);
+ Cmd_AddCommand ("-use", IN_UseUp);
+ Cmd_AddCommand ("+jump", IN_JumpDown);
+ Cmd_AddCommand ("-jump", IN_JumpUp);
+ Cmd_AddCommand ("+grenade", IN_GrenadeDown);
+ Cmd_AddCommand ("-grenade", IN_GrenadeUp);
+ Cmd_AddCommand ("+switch", IN_SwitchDown);
+ Cmd_AddCommand ("-switch", IN_SwitchUp);
+ Cmd_AddCommand ("+reload", IN_ReloadDown);
+ Cmd_AddCommand ("-reload", IN_ReloadUp);
+ Cmd_AddCommand ("+knife", IN_KnifeDown);
+ Cmd_AddCommand ("-knife", IN_KnifeUp);
+ Cmd_AddCommand ("+aim", IN_AimDown);
+ Cmd_AddCommand ("-aim", IN_AimUp);
+ Cmd_AddCommand ("impulse", IN_Impulse);
+ Cmd_AddCommand ("+klook", IN_KLookDown);
+ Cmd_AddCommand ("-klook", IN_KLookUp);
+
+
+
+}
+
diff --git a/source/cl_main.c b/source/cl_main.c
new file mode 100644
index 0000000..c2226c6
--- /dev/null
+++ b/source/cl_main.c
@@ -0,0 +1,1106 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// cl_main.c -- client main loop
+
+#include "quakedef.h"
+#include "cl_slist.h"
+
+// we need to declare some mouse variables here, because the menu system
+// references them even when on a unix system.
+
+// these two are not intended to be set directly
+cvar_t waypoint_mode = {"waypoint_mode", "0", false};// waypoint mode active
+cvar_t autosave_waypoint = {"autosave_waypoint", "0", false};// waypoint mode active
+cvar_t cl_name = {"_cl_name", "player", true};
+cvar_t cl_truelightning = {"cl_truelightning", "1", true};
+cvar_t cl_shownet = {"cl_shownet","0"}; // can be 0, 1, or 2
+cvar_t cl_nolerp = {"cl_nolerp","0"};
+cvar_t cl_lightning_zadjust = {"cl_lightning_zadjust", "0", true};
+
+cvar_t lookspring = {"lookspring","0", true};
+cvar_t lookstrafe = {"lookstrafe","0", true};
+cvar_t in_sensitivity = {"sensitivity","3", true};
+cvar_t in_tolerance = {"tolerance","0.25", true};
+cvar_t in_acceleration = {"acceleration","1.0", true};
+
+cvar_t cl_rocket2grenade = {"cl_r2g", "0"};
+cvar_t cl_deadbodyfilter = {"cl_deadbodyfilter", "0"};
+cvar_t cl_gibfilter = {"cl_gibfilter", "0"};
+
+cvar_t m_pitch = {"m_pitch","0.022", true};
+cvar_t m_yaw = {"m_yaw","0.022", true};
+cvar_t m_forward = {"m_forward","1", true};
+cvar_t m_side = {"m_side","0.8", true};
+
+cvar_t in_disable_analog = {"in_disable_analog", "0", true};
+cvar_t in_analog_strafe = {"in_analog_strafe", "0", true};
+cvar_t in_x_axis_adjust = {"in_x_axis_adjust", "0", true};
+cvar_t in_y_axis_adjust = {"in_y_axis_adjust", "0", true};
+
+//=================================================//
+modelindex_t cl_modelindex[NUM_MODELINDEX]; //
+char *cl_modelnames[NUM_MODELINDEX];//
+ //
+tagentity_t q3player_body, q3player_head; //
+//=================================================//
+client_static_t cls;
+client_state_t cl;
+// FIXME: put these on hunk?
+efrag_t cl_efrags[MAX_EFRAGS];
+entity_t cl_entities[MAX_EDICTS];
+entity_t cl_static_entities[MAX_STATIC_ENTITIES];
+lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
+dlight_t cl_dlights[MAX_DLIGHTS];
+
+modelindex_t cl_modelindex[NUM_MODELINDEX];
+
+int cl_numvisedicts;
+entity_t *cl_visedicts[MAX_VISEDICTS];
+
+void CL_ClearTEnts (void);
+
+/*
+=====================
+CL_ClearState
+
+=====================
+*/
+void CL_ClearState (void)
+{
+ int i;
+
+ if (!sv.active)
+ Host_ClearMemory ();
+
+ CL_ClearTEnts ();
+// wipe the entire cl structure
+ memset (&cl, 0, sizeof(cl));
+
+ SZ_Clear (&cls.message);
+
+// clear other arrays
+ memset (cl_efrags, 0, sizeof(cl_efrags));
+ memset (cl_entities, 0, sizeof(cl_entities));
+ memset (cl_dlights, 0, sizeof(cl_dlights));
+ memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
+ memset (cl_temp_entities, 0, sizeof(cl_temp_entities));
+ memset (cl_beams, 0, sizeof(cl_beams));
+
+ memset (cl_static_entities, 0, sizeof(cl_static_entities));
+//
+// allocate the efrags and chain together into a free list
+//
+ cl.free_efrags = cl_efrags;
+ for (i=0 ; imodel)
+ {
+ Con_Printf ("EMPTY\n");
+ continue;
+ }
+ Con_Printf ("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n"
+ ,ent->model->name,ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]);
+ }
+}
+
+
+/*
+===============
+SetPal
+
+Debugging tool, just flashes the screen
+===============
+*/
+void SetPal (int i)
+{
+#if 0
+ static int old;
+ byte pal[768];
+ int c;
+
+ if (i == old)
+ return;
+ old = i;
+
+ if (i==0)
+ VID_SetPalette (host_basepal);
+ else if (i==1)
+ {
+ for (c=0 ; c<768 ; c+=3)
+ {
+ pal[c] = 0;
+ pal[c+1] = 255;
+ pal[c+2] = 0;
+ }
+ VID_SetPalette (pal);
+ }
+ else
+ {
+ for (c=0 ; c<768 ; c+=3)
+ {
+ pal[c] = 0;
+ pal[c+1] = 0;
+ pal[c+2] = 255;
+ }
+ VID_SetPalette (pal);
+ }
+#endif
+}
+
+void CL_CopyPlayerInfo (entity_t *ent, entity_t *player)
+{
+ memcpy (&ent->baseline, &player->baseline, sizeof(entity_state_t));
+
+ ent->msgtime = player->msgtime;
+ memcpy (ent->msg_origins, player->msg_origins, sizeof(ent->msg_origins));
+ VectorCopy (player->origin, ent->origin);
+ memcpy (ent->msg_angles, player->msg_angles, sizeof(ent->msg_angles));
+ VectorCopy (player->angles, ent->angles);
+
+ ent->model = (ent == &q3player_body.ent) ? cl.model_precache[cl_modelindex[mi_q3torso]] : cl.model_precache[cl_modelindex[mi_q3head]];
+
+ ent->efrag = player->efrag;
+
+ ent->frame = player->frame;
+ ent->syncbase = player->syncbase;
+ ent->colormap = player->colormap;
+ ent->effects = player->effects;
+ ent->skinnum = player->skinnum;
+ ent->visframe = player->visframe;
+ ent->dlightframe = player->dlightframe;
+ ent->dlightbits = player->dlightbits;
+
+ ent->trivial_accept = player->trivial_accept;
+ ent->topnode = player->topnode;
+
+ ent->modelindex = (ent == &q3player_body.ent) ? cl_modelindex[mi_q3torso] : cl_modelindex[mi_q3head];
+
+ ent->noshadow = player->noshadow;
+
+ ent->rendermode = player->rendermode;
+ ent->renderamt = player->renderamt;
+ //ent->rendercolor = player->rendercolor;
+}
+
+/*
+===============
+CL_AllocDlight
+
+===============
+*/
+dlight_t *CL_AllocDlight (int key)
+{
+ int i;
+ dlight_t *dl;
+
+// first look for an exact key match
+ if (key)
+ {
+ dl = cl_dlights;
+ for (i=0 ; ikey == key)
+ {
+ memset (dl, 0, sizeof(*dl));
+ dl->key = key;
+ dl->color[0] = dl->color[1] = dl->color[2] = 1; // LordHavoc: .lit support
+ return dl;
+ }
+ }
+ }
+
+// then look for anything else
+ dl = cl_dlights;
+ for (i=0 ; idie < cl.time)
+ {
+ memset (dl, 0, sizeof(*dl));
+ dl->key = key;
+ dl->color[0] = dl->color[1] = dl->color[2] = 1; // LordHavoc: .lit support
+ return dl;
+ }
+ }
+
+ dl = &cl_dlights[0];
+ memset (dl, 0, sizeof(*dl));
+ dl->key = key;
+ dl->color[0] = dl->color[1] = dl->color[2] = 1; // LordHavoc: .lit support
+ return dl;
+}
+
+void CL_NewDlight (int key, vec3_t origin, float radius, float time, int type)
+{
+ dlight_t *dl;
+
+ dl = CL_AllocDlight (key);
+ VectorCopy (origin, dl->origin);
+ dl->radius = radius;
+ dl->die = cl.time + time;
+ dl->type = type;
+
+}
+
+dlighttype_t SetDlightColor (float f, dlighttype_t def, qboolean random)
+{
+ dlighttype_t colors[NUM_DLIGHTTYPES-4] = {lt_red, lt_blue, lt_redblue, lt_green};
+
+ if ((int)f == 1)
+ return lt_red;
+ else if ((int)f == 2)
+ return lt_blue;
+ else if ((int)f == 3)
+ return lt_redblue;
+ else if ((int)f == 4)
+ return lt_green;
+ else if (((int)f == NUM_DLIGHTTYPES - 3) && random)
+ return colors[rand()%(NUM_DLIGHTTYPES-4)];
+ else
+ return def;
+}
+
+/*
+===============
+CL_DecayLights
+
+===============
+*/
+void CL_DecayLights (void)
+{
+ int i;
+ dlight_t *dl;
+ float time;
+
+ time = cl.time - cl.oldtime;
+
+ dl = cl_dlights;
+ for (i=0 ; idie < cl.time || !dl->radius)
+ continue;
+
+ dl->radius -= time*dl->decay;
+ if (dl->radius < 0)
+ dl->radius = 0;
+ }
+}
+
+
+/*
+===============
+CL_LerpPoint
+
+Determines the fraction between the last two messages that the objects
+should be put at.
+===============
+*/
+float CL_LerpPoint (void)
+{
+ float f, frac;
+
+ f = cl.mtime[0] - cl.mtime[1];
+
+ if (!f || cl_nolerp.value || cls.timedemo || sv.active)
+ {
+ cl.ctime = cl.time = cl.mtime[0];
+ return 1;
+ }
+
+ if (f > 0.1)
+ { // dropped packet, or start of demo
+ cl.mtime[1] = cl.mtime[0] - 0.1;
+ f = 0.1;
+ }
+
+ frac = (cl.ctime - cl.mtime[1]) / f;
+ if (frac < 0)
+ {
+ if (frac < -0.01)
+ {
+ cl.ctime = cl.time = cl.mtime[1];
+ }
+ frac = 0;
+ }
+ else if (frac > 1)
+ {
+ if (frac > 1.01)
+ {
+ cl.ctime = cl.time = cl.mtime[0];
+ }
+ frac = 1;
+ }
+
+ return frac;
+}
+
+
+extern cvar_t scr_fov;
+/*
+===============
+CL_RelinkEntities
+===============
+*/
+
+
+void CL_RelinkEntities (void)
+{
+ entity_t *ent;
+ int i, j;
+ float frac, f, d;
+ vec3_t delta;
+ float bobjrotate;
+ vec3_t oldorg;
+ //model_t *model;
+ dlight_t *dl;
+ //vec3_t smokeorg, smokeorg2;
+ //float scale;
+// determine partial update time
+ frac = CL_LerpPoint ();
+
+
+ cl_numvisedicts = 0;
+
+//
+// interpolate player info
+//
+ for (i=0 ; i<3 ; i++)
+ cl.velocity[i] = cl.mvelocity[1][i] + frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]);
+
+ if (cls.demoplayback)
+ {
+ // interpolate the angles
+ for (j=0 ; j<3 ; j++)
+ {
+ d = cl.mviewangles[0][j] - cl.mviewangles[1][j];
+ if (d > 180)
+ d -= 360;
+ else if (d < -180)
+ d += 360;
+ cl.viewangles[j] = cl.mviewangles[1][j] + frac*d;
+ }
+ }
+
+ bobjrotate = anglemod(100*cl.time);
+
+// start on the entity after the world
+ for (i=1,ent=cl_entities+1 ; imodel)
+ { // empty slot
+ if (ent->forcelink)
+ R_RemoveEfrags (ent); // just became empty
+ continue;
+ }
+
+// if the object wasn't included in the last packet, remove it
+ if (ent->msgtime != cl.mtime[0])
+ {
+ ent->model = NULL;
+ // fenix@io.com: model transform interpolation
+ ent->frame_start_time = 0;
+ ent->translate_start_time = 0;
+ ent->rotate_start_time = 0;
+ continue;
+ }
+
+ VectorCopy (ent->origin, oldorg);
+
+ if (ent->forcelink)
+ { // the entity was not updated in the last message
+ // so move to the final spot
+ VectorCopy (ent->msg_origins[0], ent->origin);
+ VectorCopy (ent->msg_angles[0], ent->angles);
+ }
+ else
+ { // if the delta is large, assume a teleport and don't lerp
+ f = frac;
+ for (j=0 ; j<3 ; j++)
+ {
+ delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j];
+ if (delta[j] > 100 || delta[j] < -100)//blubs check here for interpolating zombies
+ f = 1; // assume a teleportation, not a motion
+ }
+
+ // fenix@io.com: model transform interpolation
+ // interpolation should be reset in the event of a large delta
+ if (f >= 1)
+ {
+ //ent->frame_start_time = 0;
+ ent->translate_start_time = 0;
+ ent->rotate_start_time = 0;
+ }
+
+ // interpolate the origin and angles
+ for (j=0 ; j<3 ; j++)
+ {
+ ent->origin[j] = ent->msg_origins[1][j] + f*delta[j];
+
+ d = ent->msg_angles[0][j] - ent->msg_angles[1][j];
+ if (d > 180)
+ d -= 360;
+ else if (d < -180)
+ d += 360;
+ ent->angles[j] = ent->msg_angles[1][j] + f*d;
+ }
+
+ }
+/*
+ if (ent->modelindex == cl_modelindex[mi_explo1] || ent->modelindex == cl_modelindex[mi_explo2])
+ {
+ // software removal of sprites
+ if (r_explosiontype.value == 2 || r_explosiontype.value == 3)
+ continue;
+
+ if (qmb_initialized && r_part_explosions.value)
+ continue;
+ }
+
+ if (!(model = cl.model_precache[ent->modelindex]))
+ Host_Error ("CL_RelinkEntities: bad modelindex");
+*/
+
+// rotate binary objects locally
+
+ if (ent->model->flags & EF_ROTATE)
+ {
+ ent->angles[1] = bobjrotate;
+
+ #ifdef PSP_VFPU
+ ent->origin[2] += (( vfpu_sinf(bobjrotate/90*M_PI) * 5) + 5 );
+ #else
+ ent->origin[2] += (( sin(bobjrotate/90*M_PI) * 5) + 5 );
+ #endif
+ }
+
+ if (ent->effects & EF_BRIGHTFIELD)
+ R_EntityParticles (ent);
+
+ if (ent->effects & EF_DARKFIELD)
+ R_DarkFieldParticles (ent);
+
+
+ if (ent->effects & EF_MUZZLEFLASH)
+ {
+
+ if (i == cl.viewentity && qmb_initialized && r_part_muzzleflash.value)
+ {
+ vec3_t start, smokeorg, v_forward, v_right, v_up;
+ vec3_t tempangles;
+ float forward_offset, up_offset, right_offset;
+
+ VectorAdd(cl.viewangles,CWeaponRot,tempangles);
+ VectorAdd(tempangles,cl.gun_kick,tempangles);
+
+
+ AngleVectors (tempangles, v_forward, v_right, v_up);
+ VectorCopy (cl_entities[cl.viewentity].origin, smokeorg);
+ smokeorg[2] += 32;
+ VectorCopy(smokeorg,start);
+
+ right_offset = sv_player->v.Flash_Offset[0];
+ up_offset = sv_player->v.Flash_Offset[1];
+ forward_offset = sv_player->v.Flash_Offset[2];
+
+ right_offset = right_offset/1000;
+ up_offset = up_offset/1000;
+ forward_offset = forward_offset/1000;
+
+ VectorMA (start, forward_offset, v_forward ,smokeorg);
+ VectorMA (smokeorg, up_offset, v_up ,smokeorg);
+ VectorMA (smokeorg, right_offset, v_right ,smokeorg);
+ VectorAdd(smokeorg,CWeaponOffset,smokeorg);
+
+ if (sv_player->v.weapon != W_RAY && sv_player->v.weapon != W_PORTER) {
+ QMB_MuzzleFlash (smokeorg);
+ } else {
+ QMB_RayFlash(smokeorg, sv_player->v.weapon);
+ }
+ }
+
+ }
+
+ if (ent->modelindex != cl_modelindex[mi_player] && ent->model->modhint != MOD_PLAYER)
+ {
+ if (ent->effects & EF_BRIGHTLIGHT)
+ {
+ vec3_t tmp;
+
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, tmp);
+ tmp[2] += 16;
+ dl->color[0] = 1;
+ dl->color[1] = 0.8;
+ dl->color[2] = 0.5;
+ CL_NewDlight (i, tmp, 400 + (rand() & 31), 0.1, lt_default);
+ }
+ }
+
+ if (ent->effects & EF_DARKLIGHT)
+ {
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->radius = 200.0 + (rand()&31);
+ dl->die = cl.time + 0.001;
+ dl->dark = true;
+ }
+ if (ent->effects & EF_LIGHT)
+ {
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->radius = 200;
+ dl->die = cl.time + 0.001;
+ }
+
+ if (ent->effects & EF_BLUELIGHT)
+ {
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->die = cl.time + 0.001;
+ dl->radius = 100;
+ dl->color[0] = 0.25;
+ dl->color[1] = 0.25;
+ dl->color[2] = 2;
+ }
+
+ if (ent->effects & EF_REDLIGHT)
+ {
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->die = cl.time + 0.001;
+ dl->radius = 100;
+ dl->color[0] = 2;
+ dl->color[1] = 0.25;
+ dl->color[2] = 0.25;
+ }
+
+ if (ent->effects & EF_ORANGELIGHT)
+ {
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->die = cl.time + 0.001;
+ dl->radius = 100;
+ dl->color[0] = 2;
+ dl->color[1] = 1;
+ dl->color[2] = 0;
+ }
+
+ if (ent->effects & EF_GREENLIGHT)
+ {
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->die = cl.time + 0.001;
+ dl->radius = 100;
+ dl->color[0] = 0.25;
+ dl->color[1] = 2;
+ dl->color[2] = 0.25;
+ }
+ if (ent->effects & EF_ORANGELIGHT)
+ {
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->die = cl.time + 0.001;
+ dl->radius = 100;
+ dl->color[0] = 2;
+ dl->color[1] = 1;
+ dl->color[2] = 0;
+ }
+
+ if (ent->effects & EF_GREENLIGHT)
+ {
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->die = cl.time + 0.001;
+ dl->radius = 100;
+ dl->color[0] = 0.25;
+ dl->color[1] = 2;
+ dl->color[2] = 0.25;
+ }
+
+ if (ent->effects & EF_PURPLELIGHT)
+ {
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->die = cl.time + 0.001;
+ dl->radius = 100;
+ dl->color[0] = 2;
+ dl->color[1] = 0.25;
+ dl->color[2] = 2;
+ }
+
+ if (ent->effects & EF_RAYGREEN)
+ {
+ R_RocketTrail (oldorg, ent->origin, 12);
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->radius = 25;
+ dl->die = cl.time + 0.01;
+ dl->color[0] = 0;
+ dl->color[1] = 255;
+ dl->color[2] = 0;
+ dl->type = SetDlightColor (2, lt_rocket, true);
+ }
+
+ if (ent->effects & EF_RAYRED)
+ {
+ R_RocketTrail (oldorg, ent->origin, 13);
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->radius = 25;
+ dl->die = cl.time + 0.01;
+ dl->color[0] = 255;
+ dl->color[1] = 0;
+ dl->color[2] = 0;
+ dl->type = SetDlightColor (2, lt_rocket, true);
+ }
+
+ if (!strcmp(ent->model->name, "progs/flame2.mdl"))
+ {
+ if (qmb_initialized && r_part_flames.value)
+ {
+ //QMB_BigTorchFlame (ent->origin);
+ if (qmb_initialized && r_part_trails.value)
+ R_RocketTrail (oldorg, ent->origin, LAVA_TRAIL);
+ }
+ }
+
+ if ((!strcmp(ent->model->name, "progs/s_spike.mdl"))||(!strcmp(ent->model->name, "progs/spike.mdl")))
+ {
+ if (qmb_initialized && r_part_trails.value)
+ {
+ R_RocketTrail (oldorg, ent->origin, NAIL_TRAIL);
+ }
+ }
+
+ if (ent->model->flags)
+ {
+ if (ent->model->flags & EF_GIB)
+ R_RocketTrail (oldorg, ent->origin, 2);
+ else if (ent->model->flags & EF_ZOMGIB)
+ R_RocketTrail (oldorg, ent->origin, 4);
+ else if (ent->model->flags & EF_TRACER)
+ R_RocketTrail (oldorg, ent->origin, 3);
+ else if (ent->model->flags & EF_TRACER2)
+ R_RocketTrail (oldorg, ent->origin, 5);
+ else if (ent->model->flags & EF_ROCKET)
+ {
+ R_RocketTrail (oldorg, ent->origin, 0);
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->radius = 200;
+ dl->die = cl.time + 0.01;
+ dl->color[0] = 0.2;
+ dl->color[1] = 0.1;
+ dl->color[2] = 0.5;
+ dl->type = SetDlightColor (2, lt_rocket, true);
+ }
+ else if (ent->model->flags & EF_GRENADE)
+ R_RocketTrail (oldorg, ent->origin, 1);
+ else if (ent->model->flags & EF_TRACER3)
+ R_RocketTrail (oldorg, ent->origin, 6);
+ }
+
+ // Tomaz - QC Glow Begin
+ if (ISLMPOINT(ent))
+ {
+ dl = CL_AllocDlight (i);
+ VectorCopy (ent->origin, dl->origin);
+ dl->radius = 150;
+ dl->die = cl.time + 0.001;
+ dl->color[0] = ent->rendercolor[0];
+ dl->color[1] = ent->rendercolor[1];
+ dl->color[2] = ent->rendercolor[2];
+ }
+ // Tomaz - QC Glow End
+
+ ent->forcelink = false;
+
+ if (i == cl.viewentity && !chase_active.value)
+ continue;
+
+
+ if ( ent->effects & EF_NODRAW )
+ continue;
+
+ if (qmb_initialized)
+ {
+ /*if (ent->modelindex == cl_modelindex[mi_bubble])
+ {
+ QMB_StaticBubble (ent);
+ continue;
+ }
+ else if (r_part_lightning.value && ent->modelindex == cl_modelindex[mi_shambler] && ent->frame >= 65 && ent->frame <= 68)
+ {
+ vec3_t liteorg;
+
+ VectorCopy (ent->origin, liteorg);
+ liteorg[2] += 32;
+ QMB_ShamblerCharge (liteorg);
+ }
+ else */if (r_part_trails.value && ent->model->modhint == MOD_SPIKE)
+ {
+ if (!ent->traildrawn || !VectorL2Compare(ent->trail_origin, ent->origin, 140))
+ {
+ VectorCopy (ent->origin, oldorg); //not present last frame or too far away
+ ent->traildrawn = true;
+ }
+ else
+ {
+ VectorCopy (ent->trail_origin, oldorg);
+ }
+ QMB_RocketTrail (oldorg, ent->origin, BUBBLE_TRAIL);
+ }
+ }
+
+ if (cl_numvisedicts < MAX_VISEDICTS)
+ {
+ cl_visedicts[cl_numvisedicts] = ent;
+ cl_numvisedicts++;
+ }
+ }
+}
+
+/*
+===============
+CL_ReadFromServer
+
+Read all incoming data from the server
+===============
+*/
+int CL_ReadFromServer (void)
+{
+ int ret;
+
+ cl.oldtime = cl.time;
+ cl.time += host_frametime;
+
+ do
+ {
+ ret = CL_GetMessage ();
+
+ if (ret == -1)
+ Host_Error ("CL_ReadFromServer: lost server connection");
+ if (!ret)
+ break;
+
+ //if(ret)
+ //{
+ //Con_Printf("++++++++++++++Got a new server message!+++++++++\n");
+ //}
+ cl.last_received_message = realtime;
+ //Con_Printf("ParseServerMessage \n");
+ CL_ParseServerMessage ();
+ //}
+
+ } while (ret && cls.state == ca_connected);
+
+
+ //Con_Printf("-------------Done with server messages---------------=====\n");
+
+ if (cl_shownet.value)
+ Con_Printf ("\n");
+
+ CL_RelinkEntities ();
+ CL_UpdateTEnts ();
+
+//
+// bring the links up to date
+//
+ return 0;
+}
+
+/*
+=================
+CL_SendCmd
+=================
+*/
+void CL_SendCmd (void)
+{
+ usercmd_t cmd;
+
+ if (cls.state != ca_connected)
+ return;
+
+ if (cls.signon == SIGNONS)
+ {
+ // get basic movement from keyboard
+ CL_BaseMove (&cmd);
+
+ // allow mice or other external controllers to add to the move
+ if (!in_disable_analog.value)
+ IN_Move (&cmd);
+
+ // send the unreliable message
+ CL_SendMove (&cmd);
+
+ }
+
+ if (cls.demoplayback)
+ {
+ SZ_Clear (&cls.message);
+ return;
+ }
+
+// send the reliable message
+ if (!cls.message.cursize)
+ return; // no message at all
+
+ if (!NET_CanSendMessage (cls.netcon))
+ {
+ Con_DPrintf ("CL_WriteToServer: can't send\n");
+ return;
+ }
+
+ if (NET_SendMessage (cls.netcon, &cls.message) == -1)
+ Host_Error ("CL_WriteToServer: lost server connection");
+
+ SZ_Clear (&cls.message);
+}
+
+/*
+=================
+CL_Init
+=================
+*/
+void CL_Init (void)
+{
+ SZ_Alloc (&cls.message, 1024);
+
+ SList_Init ();
+ SList_Load ();
+
+ CL_InitInput ();
+ CL_InitTEnts ();
+//
+// register our commands
+//
+ Cvar_RegisterVariable (&cl_name);
+ Cvar_RegisterVariable (&waypoint_mode);
+ Cvar_RegisterVariable (&autosave_waypoint);
+ Cvar_RegisterVariable (&cl_upspeed);
+ Cvar_RegisterVariable (&cl_movespeedkey);
+ Cvar_RegisterVariable (&cl_yawspeed);
+ Cvar_RegisterVariable (&cl_pitchspeed);
+ Cvar_RegisterVariable (&cl_anglespeedkey);
+ Cvar_RegisterVariable (&cl_shownet);
+ Cvar_RegisterVariable (&cl_nolerp);
+ Cvar_RegisterVariable (&lookspring);
+ Cvar_RegisterVariable (&lookstrafe);
+ Cvar_RegisterVariable (&cl_rocket2grenade);
+ Cvar_RegisterVariable (&cl_deadbodyfilter);
+ Cvar_RegisterVariable (&cl_gibfilter);
+ Cvar_RegisterVariable (&cl_lightning_zadjust);
+ Cvar_RegisterVariable (&cl_truelightning);
+ Cvar_RegisterVariable (&in_sensitivity);
+ Cvar_RegisterVariable (&in_mlook); //Heffo - mlook cvar
+ Cvar_RegisterVariable (&in_aimassist);
+ Cvar_RegisterVariable (&in_tolerance);
+ Cvar_RegisterVariable (&in_acceleration);
+ Cvar_RegisterVariable (&in_disable_analog);
+ Cvar_RegisterVariable (&in_analog_strafe);
+ Cvar_RegisterVariable (&in_x_axis_adjust);
+ Cvar_RegisterVariable (&in_y_axis_adjust);
+
+
+ Cvar_RegisterVariable (&m_pitch);
+ Cvar_RegisterVariable (&m_yaw);
+ Cvar_RegisterVariable (&m_forward);
+ Cvar_RegisterVariable (&m_side);
+
+// Cvar_RegisterVariable (&cl_autofire);
+
+ Cmd_AddCommand ("entities", CL_PrintEntities_f);
+ Cmd_AddCommand ("disconnect", CL_Disconnect_f);
+ Cmd_AddCommand ("record", CL_Record_f);
+ Cmd_AddCommand ("stop", CL_Stop_f);
+ Cmd_AddCommand ("playdemo", CL_PlayDemo_f);
+ Cmd_AddCommand ("timedemo", CL_TimeDemo_f);
+}
+
diff --git a/source/cl_parse.c b/source/cl_parse.c
new file mode 100644
index 0000000..671d7ae
--- /dev/null
+++ b/source/cl_parse.c
@@ -0,0 +1,1382 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// cl_parse.c -- parse a message received from the server
+
+#include "quakedef.h"
+
+extern cvar_t bgmtype;
+
+extern qboolean domaxammo;
+
+char *svc_strings[] =
+{
+ "svc_bad",
+ "svc_nop",
+ "svc_disconnect",
+ "svc_updatestat",
+ "svc_version", // [long] server version
+ "svc_setview", // [short] entity number
+ "svc_sound", //
+ "svc_time", // [float] server time
+ "svc_print", // [string] null terminated string
+ "svc_stufftext", // [string] stuffed into client's console buffer
+ // the string should be \n terminated
+ "svc_setangle", // [vec3] set the view angle to this absolute value
+
+ "svc_serverinfo", // [long] version
+ // [string] signon string
+ // [string]..[0]model cache [string]...[0]sounds cache
+ // [string]..[0]item cache
+ "svc_lightstyle", // [byte] [string]
+ "svc_updatename", // [byte] [string]
+ "svc_updatepoints", // [byte] [short]
+ "svc_clientdata", //
+ "svc_stopsound", //
+ "", // [byte] [byte]
+ "svc_particle", // [vec3]
+ "svc_damage", // [byte] impact [byte] blood [vec3] from
+
+ "svc_spawnstatic",
+ "OBSOLETE svc_spawnbinary",
+ "svc_spawnbaseline",
+
+ "svc_temp_entity", //
+ "svc_setpause",
+ "svc_signonnum",
+ "svc_centerprint",
+ "svc_spawnstaticsound",
+ "svc_intermission",
+ "svc_finale", // [string] music [string] text
+ "svc_cdtrack", // [byte] track [byte] looptrack
+ "svc_sellscreen",
+ "svc_cutscene",
+ "svc_weaponfire",
+ "svc_hitmark",
+ "svc_skybox", // [string] skyname
+ "svc_useprint",
+ "svc_updatekills",
+ "svc_limbupdate",
+ "svc_fog", // 41 // [byte] start [byte] end [byte] red [byte] green [byte] blue [float] time
+ "svc_bspdecal", //42 // [string] name [byte] decal_size [coords] pos
+ "svc_achievement", //43
+ "svc_songegg", //44 [string] track name
+ "svc_maxammo" //45
+};
+
+//=============================================================================
+
+/*
+===============
+CL_EntityNum
+
+This error checks and tracks the total number of entities
+===============
+*/
+entity_t *CL_EntityNum (int num)
+{
+ if (num >= cl.num_entities)
+ {
+ if (num >= MAX_EDICTS)
+ Host_Error ("CL_EntityNum: %i is an invalid number",num);
+ while (cl.num_entities<=num)
+ {
+ cl_entities[cl.num_entities].colormap = vid.colormap;
+ cl.num_entities++;
+ }
+ }
+
+ return &cl_entities[num];
+}
+
+
+/*
+==================
+CL_ParseStartSoundPacket
+==================
+*/
+void CL_ParseStartSoundPacket(void)
+{
+ vec3_t pos;
+ int channel, ent;
+ int sound_num;
+ int volume;
+ int field_mask;
+ float attenuation;
+ int i;
+
+ field_mask = MSG_ReadByte();
+
+ if (field_mask & SND_VOLUME)
+ volume = MSG_ReadByte ();
+ else
+ volume = DEFAULT_SOUND_PACKET_VOLUME;
+
+ if (field_mask & SND_ATTENUATION)
+ attenuation = MSG_ReadByte () / 64.0;
+ else
+ attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
+
+ channel = MSG_ReadShort ();
+ sound_num = MSG_ReadByte ();
+
+ ent = channel >> 3;
+ channel &= 7;
+
+ if (ent > MAX_EDICTS)
+ Host_Error ("CL_ParseStartSoundPacket: ent = %i", ent);
+
+ for (i=0 ; i<3 ; i++)
+ pos[i] = MSG_ReadCoord ();
+
+ S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation);
+}
+
+/*
+==================
+CL_ParseBSPDecal
+
+Spawn decals on map
+Crow_bar.
+==================
+*/
+void CL_ParseBSPDecal (void)
+{
+ vec3_t pos;
+ int decal_size;
+ char *texname;
+
+ texname = MSG_ReadString ();
+ decal_size = MSG_ReadByte ();
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+
+ if(!texname)
+ return;
+
+ Con_Printf("BSPDECAL[tex: %s size: %i pos: %f %f %f]\n", texname, decal_size, pos[0], pos[1], pos[2]);
+
+ R_SpawnDecalBSP(pos, texname, decal_size);
+}
+
+/*
+==================
+CL_KeepaliveMessage
+
+When the client is taking a long time to load stuff, send keepalive messages
+so the server doesn't disconnect.
+==================
+*/
+void CL_KeepaliveMessage (void)
+{
+ double time;
+ static double lastmsg;//BLUBSFIX, this was a float
+ int ret;
+ sizebuf_t old;
+ byte olddata[8192];
+
+ if (sv.active)
+ {
+ //Con_Printf("Active Server...exit keepalive\n");
+ return; // no need if server is local
+ }
+ if (cls.demoplayback)
+ {
+ //Con_Printf("Demo Playback...exit keepalive\n");
+ return;
+ }
+
+// read messages from server, should just be nops
+ old = net_message;
+ memcpy (olddata, net_message.data, net_message.cursize);
+
+ do
+ {
+ ret = CL_GetMessage ();
+ switch (ret)
+ {
+ default:
+ Host_Error ("CL_KeepaliveMessage: CL_GetMessage failed");
+ case 0:
+ break; // nothing waiting
+ case 1:
+ Host_Error ("CL_KeepaliveMessage: received a message");
+ break;
+ case 2:
+ if (MSG_ReadByte() != svc_nop)
+ Host_Error ("CL_KeepaliveMessage: datagram wasn't a nop");
+ break;
+ }
+ } while (ret);
+
+ net_message = old;
+ memcpy (net_message.data, olddata, net_message.cursize);
+
+// check time
+ time = Sys_FloatTime ();
+ if (time - lastmsg < 5)
+ return;
+ //Con_Printf("Time since last keepAlive msg = %f\n",time - lastmsg);
+
+ lastmsg = time;
+
+// write out a nop
+ Con_Printf ("--> client to server keepalive\n");
+
+ MSG_WriteByte (&cls.message, clc_nop);
+ NET_SendMessage (cls.netcon, &cls.message);
+ SZ_Clear (&cls.message);
+}
+
+/*
+==================
+CL_ParseServerInfo
+==================
+*/
+void CL_ParseServerInfo (void)
+{
+ char *str, tempname[MAX_QPATH];;
+ int i;
+ int nummodels, numsounds;
+ char model_precache[MAX_MODELS][MAX_QPATH];
+ char sound_precache[MAX_SOUNDS][MAX_QPATH];
+ extern qboolean r_loadq3player;
+
+ //void R_PreMapLoad (char *);
+
+ //char mapname[MAX_QPATH];
+
+ Con_DPrintf ("Serverinfo packet received.\n");
+ //Con_Printf ("Serverinfo packet received.\n");
+//
+// wipe the client_state_t struct
+//
+ CL_ClearState ();
+
+// parse protocol version number
+ i = MSG_ReadLong ();
+ if (i != PROTOCOL_VERSION)
+ {
+ Con_Printf ("Server returned version %i, not %i", i, PROTOCOL_VERSION);
+ return;
+ }
+
+// parse maxclients
+ cl.maxclients = MSG_ReadByte ();
+ if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD)
+ {
+ Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients);
+ return;
+ }
+ cl.scores = Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores");
+
+// parse gametype
+ cl.gametype = MSG_ReadByte ();
+
+// parse signon message
+ str = MSG_ReadString ();
+ strncpy (cl.levelname, str, sizeof(cl.levelname)-1);
+
+// seperate the printfs so the server message can have a color
+ Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
+ Con_Printf ("%c%s\n", 2, str);
+
+//
+// first we go through and touch all of the precache data that still
+// happens to be in the cache, so precaching something else doesn't
+// needlessly purge it
+//
+
+// precache models
+ for (i=0 ; i= MAX_MODELS)
+ {
+ Con_Printf ("Server sent too many model precaches -> can't load Q3 player model\n");
+ Q_strncpyz (model_precache[cl_modelindex[mi_player]], cl_modelnames[mi_player], sizeof(model_precache[cl_modelindex[mi_player]]));
+ }
+ else
+ {
+ Q_strncpyz (model_precache[nummodels], "models/player/upper.md3", sizeof(model_precache[nummodels]));
+ cl_modelindex[mi_q3torso] = nummodels++;
+ Q_strncpyz (model_precache[nummodels], "models/player/head.md3", sizeof(model_precache[nummodels]));
+ cl_modelindex[mi_q3head] = nummodels++;
+ }
+ }
+
+// precache sounds
+ //Con_Printf("Got Sounds to load: ");
+ memset (cl.sound_precache, 0, sizeof(cl.sound_precache));
+ for (numsounds=1 ; ; numsounds++)
+ {
+
+ str = MSG_ReadString ();
+ if (!str[0])
+ break;
+ if (numsounds==MAX_SOUNDS)
+ {
+ Con_Printf ("Server sent too many sound precaches\n");
+ return;
+ }
+ strcpy (sound_precache[numsounds], str);
+ S_TouchSound (str);
+ //Con_Printf("%i,",numsounds);
+ }
+ //Con_Printf("\n");
+ //COM_StripExtension (COM_SkipPath(model_precache[1]), mapname);
+ //R_PreMapLoad (mapname);
+
+//
+// now we try to load everything else until a cache allocation fails
+//
+
+ loading_num_step = loading_num_step +nummodels + numsounds;
+
+ //Con_Printf("Loaded Model: ");
+
+ for (i=1 ; imsgtime != cl.mtime[1])
+ forcelink = true; // no previous frame to lerp from
+ else
+ forcelink = false;
+
+ ent->msgtime = cl.mtime[0];
+
+ if (bits & U_MODEL)
+ {
+ modnum = MSG_ReadByte ();
+ if (modnum >= MAX_MODELS)
+ Host_Error ("CL_ParseModel: bad modnum");
+ }
+ else
+ modnum = ent->baseline.modelindex;
+
+ model = cl.model_precache[modnum];
+ if (model != ent->model)
+ {
+ ent->model = model;
+ // automatic animation (torches, etc) can be either all together
+ // or randomized
+ if (model)
+ {
+ if (model->synctype == ST_RAND)
+ ent->syncbase = (float)(rand()&0x7fff) / 0x7fff;
+ else
+ ent->syncbase = 0.0;
+ }
+ else
+ forcelink = true; // hack to make null model players work
+ }
+
+ if (bits & U_FRAME)
+ ent->frame = MSG_ReadShort ();
+ else
+ ent->frame = ent->baseline.frame;
+
+ if (bits & U_COLORMAP)
+ i = MSG_ReadByte();
+ else
+ i = ent->baseline.colormap;
+ if (!i)
+ ent->colormap = vid.colormap;
+ else
+ {
+ if (i > cl.maxclients)
+ Sys_Error ("i >= cl.maxclients");
+ }
+
+ if (bits & U_SKIN)
+ ent->skinnum = MSG_ReadByte();
+ else
+ ent->skinnum = ent->baseline.skin;
+
+ if (bits & U_FRAMETIME)
+ ent->iframetime = MSG_ReadFloat();
+ else
+ ent->iframetime = 0.1;
+
+ if (bits & U_EFFECTS)
+ ent->effects = MSG_ReadShort();
+ else
+ ent->effects = ent->baseline.effects;
+
+// shift the known values for interpolation
+ VectorCopy (ent->msg_origins[0], ent->msg_origins[1]);
+ VectorCopy (ent->msg_angles[0], ent->msg_angles[1]);
+ if (bits & U_ORIGIN1)
+ ent->msg_origins[0][0] = MSG_ReadCoord ();
+ else
+ ent->msg_origins[0][0] = ent->baseline.origin[0];
+ if (bits & U_ANGLE1)
+ ent->msg_angles[0][0] = MSG_ReadAngle();
+ else
+ ent->msg_angles[0][0] = ent->baseline.angles[0];
+
+ if (bits & U_ORIGIN2)
+ ent->msg_origins[0][1] = MSG_ReadCoord ();
+ else
+ ent->msg_origins[0][1] = ent->baseline.origin[1];
+ if (bits & U_ANGLE2)
+ ent->msg_angles[0][1] = MSG_ReadAngle();
+ else
+ ent->msg_angles[0][1] = ent->baseline.angles[1];
+
+ if (bits & U_ORIGIN3)
+ ent->msg_origins[0][2] = MSG_ReadCoord ();
+ else
+ ent->msg_origins[0][2] = ent->baseline.origin[2];
+ if (bits & U_ANGLE3)
+ ent->msg_angles[0][2] = MSG_ReadAngle();
+ else
+ ent->msg_angles[0][2] = ent->baseline.angles[2];
+// Tomaz - QC Alpha Scale Glow Begin
+ if (bits & U_RENDERAMT)
+ ent->renderamt = MSG_ReadFloat();
+ else
+ ent->renderamt = 0;
+
+ if (bits & U_RENDERMODE)
+ ent->rendermode = MSG_ReadFloat();
+ else
+ ent->rendermode = 0;
+
+ if (bits & U_RENDERCOLOR1)
+ ent->rendercolor[0] = MSG_ReadFloat();
+ else
+ ent->rendercolor[0] = 0;
+
+ if (bits & U_RENDERCOLOR2)
+ ent->rendercolor[1] = MSG_ReadFloat();
+ else
+ ent->rendercolor[1] = 0;
+
+ if (bits & U_RENDERCOLOR3)
+ ent->rendercolor[2] = MSG_ReadFloat();
+ else
+ ent->rendercolor[2] = 0;
+// Tomaz - QC Alpha Scale Glow End
+ if ( bits & U_NOLERP )//there's no data for nolerp, it is the value itself
+ ent->forcelink = true;
+
+ if ( forcelink )
+ { // didn't have an update last message
+ VectorCopy (ent->msg_origins[0], ent->msg_origins[1]);
+ VectorCopy (ent->msg_origins[0], ent->origin);
+ VectorCopy (ent->msg_angles[0], ent->msg_angles[1]);
+ VectorCopy (ent->msg_angles[0], ent->angles);
+ ent->forcelink = true;
+ }
+}
+
+/*
+==================
+CL_ParseBaseline
+==================
+*/
+void CL_ParseBaseline (entity_t *ent)
+{
+ int i;
+
+ ent->baseline.modelindex = MSG_ReadByte ();
+ ent->baseline.frame = MSG_ReadByte ();
+ ent->baseline.colormap = MSG_ReadByte();
+ ent->baseline.skin = MSG_ReadByte();
+ for (i=0 ; i<3 ; i++)
+ {
+ ent->baseline.origin[i] = MSG_ReadCoord ();
+ ent->baseline.angles[i] = MSG_ReadAngle ();
+ }
+}
+
+/*
+==================
+CL_ParseClientdata
+
+Server information pertaining to this client only
+==================
+*/
+extern int perk_order[9];
+extern int current_perk_order;
+void CL_ParseClientdata (int bits)
+{
+ int i, s;
+
+ if (bits & SU_VIEWHEIGHT)
+ cl.viewheight = MSG_ReadChar ();
+ else
+ cl.viewheight = DEFAULT_VIEWHEIGHT;
+
+ if (bits & SU_IDEALPITCH)
+ cl.idealpitch = MSG_ReadChar ();
+ else
+ cl.idealpitch = 0;
+
+
+ if (bits & SU_PERKS)
+ i = MSG_ReadLong ();
+ else
+ i = 0;
+ if (cl.perks != i)
+ {
+ if (i & 1 && !(cl.perks & 1))
+ {
+ perk_order[current_perk_order] = 1;
+ current_perk_order++;
+ }
+ if (i & 2 && !(cl.perks & 2))
+ {
+ perk_order[current_perk_order] = 2;
+ current_perk_order++;
+ }
+ if (i & 4 && !(cl.perks & 4))
+ {
+ perk_order[current_perk_order] = 4;
+ current_perk_order++;
+ }
+ if (i & 8 && !(cl.perks & 8))
+ {
+ perk_order[current_perk_order] = 8;
+ current_perk_order++;
+ }
+ if (i & 16 && !(cl.perks & 16))
+ {
+ perk_order[current_perk_order] = 16;
+ current_perk_order++;
+ }
+ if (i & 32 && !(cl.perks & 32))
+ {
+ perk_order[current_perk_order] = 32;
+ current_perk_order++;
+ }
+ if (i & 64 && !(cl.perks & 64))
+ {
+ perk_order[current_perk_order] = 64;
+ current_perk_order++;
+ }
+ if (i & 128 && !(cl.perks & 128))
+ {
+ perk_order[current_perk_order] = 128;
+ current_perk_order++;
+ }
+ if (cl.perks & 1 && !(i & 1))
+ {
+ for (s = 0; s < 9; s++)
+ {
+ if (perk_order[s] == 1)
+ {
+ perk_order[s] = 0;
+ while (perk_order[s+1])
+ {
+ perk_order[s] = perk_order[s+1];
+ perk_order[s+1] = 0;
+ }
+ break;
+ }
+ }
+ current_perk_order--;
+ }
+ if (cl.perks & 2 && !(i & 2))
+ {
+ for (s = 0; s < 9; s++)
+ {
+ if (perk_order[s] == 2)
+ {
+ perk_order[s] = 0;
+ while (perk_order[s+1])
+ {
+ perk_order[s] = perk_order[s+1];
+ perk_order[s+1] = 0;
+ }
+ break;
+ }
+ }
+ current_perk_order--;
+ }
+ if (cl.perks & 4 && !(i & 4))
+ {
+ for (s = 0; s < 9; s++)
+ {
+ if (perk_order[s] == 4)
+ {
+ perk_order[s] = 0;
+ while (perk_order[s+1])
+ {
+ perk_order[s] = perk_order[s+1];
+ perk_order[s+1] = 0;
+ }
+ break;
+ }
+ }
+ current_perk_order--;
+ }
+ if (cl.perks & 8 && !(i & 8))
+ {
+ for (s = 0; s < 9; s++)
+ {
+ if (perk_order[s] == 8)
+ {
+ perk_order[s] = 0;
+ while (perk_order[s+1])
+ {
+ perk_order[s] = perk_order[s+1];
+ perk_order[s+1] = 0;
+ }
+ break;
+ }
+ }
+ current_perk_order--;
+ }
+ if (cl.perks & 16 && !(i & 16))
+ {
+ for (s = 0; s < 9; s++)
+ {
+ if (perk_order[s] == 16)
+ {
+ perk_order[s] = 0;
+ while (perk_order[s+1])
+ {
+ perk_order[s] = perk_order[s+1];
+ perk_order[s+1] = 0;
+ }
+ break;
+ }
+ }
+ current_perk_order--;
+ }
+ if (cl.perks & 32 && !(i & 32))
+ {
+ for (s = 0; s < 9; s++)
+ {
+ if (perk_order[s] == 32)
+ {
+ perk_order[s] = 0;
+ while (perk_order[s+1])
+ {
+ perk_order[s] = perk_order[s+1];
+ perk_order[s+1] = 0;
+ }
+ break;
+ }
+ }
+ current_perk_order--;
+ }
+ if (cl.perks & 64 && !(i & 64))
+ {
+ for (s = 0; s < 9; s++)
+ {
+ if (perk_order[s] == 64)
+ {
+ perk_order[s] = 0;
+ while (perk_order[s+1])
+ {
+ perk_order[s] = perk_order[s+1];
+ perk_order[s+1] = 0;
+ }
+ break;
+ }
+ }
+ current_perk_order--;
+ }
+ if (cl.perks & 128 && !(i & 128))
+ {
+ for (s = 0; s < 9; s++)
+ {
+ if (perk_order[s] == 128)
+ {
+ perk_order[s] = 0;
+ while (perk_order[s+1])
+ {
+ perk_order[s] = perk_order[s+1];
+ perk_order[s+1] = 0;
+ }
+ break;
+ }
+ }
+ current_perk_order--;
+ }
+ cl.perks = i;
+ }
+
+ cl.onground = (bits & SU_ONGROUND) != 0;
+ cl.inwater = (bits & SU_INWATER) != 0;
+
+ VectorCopy (cl.mvelocity[0], cl.mvelocity[1]);
+ for (i=0 ; i<3 ; i++)
+ {
+ if (bits & (SU_PUNCH1<= MAX_STATIC_ENTITIES)
+ Host_Error ("Too many static entities");
+ ent = &cl_static_entities[i];
+ cl.num_statics++;
+ CL_ParseBaseline (ent);
+
+// copy it to the current state
+ ent->model = cl.model_precache[ent->baseline.modelindex];
+ ent->frame = ent->baseline.frame;
+ ent->colormap = vid.colormap;
+ ent->skinnum = ent->baseline.skin;
+ ent->effects = ent->baseline.effects;
+
+ VectorCopy (ent->baseline.origin, ent->origin);
+ VectorCopy (ent->baseline.angles, ent->angles);
+ R_AddEfrags (ent);
+}
+
+/*
+===================
+CL_ParseStaticSound
+===================
+*/
+void CL_ParseStaticSound (void)
+{
+ vec3_t org;
+ int sound_num, vol, atten;
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ org[i] = MSG_ReadCoord ();
+ sound_num = MSG_ReadByte ();
+ vol = MSG_ReadByte ();
+ atten = MSG_ReadByte ();
+
+ S_StaticSound (cl.sound_precache[sound_num], org, vol, atten);
+}
+
+extern double Hitmark_Time;
+extern int crosshair_spread;
+extern double crosshair_spread_time;
+double return_time;
+/*
+===================
+CL_ParseWeaponFire
+===================
+*/
+void CL_ParseWeaponFire (void)
+{
+ vec3_t kick;
+ return_time = (double)6/MSG_ReadLong ();
+ crosshair_spread_time = return_time + sv.time;
+
+ kick[0] = MSG_ReadCoord()/5;
+ kick[1] = MSG_ReadCoord()/5;
+ kick[2] = MSG_ReadCoord()/5;
+
+ if (!(cl.perks & 64)) {
+ cl.gun_kick[0] += kick[0];
+ cl.gun_kick[1] += kick[1];
+ cl.gun_kick[2] += kick[2];
+ }
+}
+/*
+===================
+CL_ParseLimbUpdate
+===================
+*/
+void CL_ParseLimbUpdate (void)
+{
+ int limb = MSG_ReadByte();
+ int zombieent = MSG_ReadShort();
+ int limbent = MSG_ReadShort();
+ switch (limb)
+ {
+ case 0://head
+ cl_entities[zombieent].z_head = limbent;
+ break;
+ case 1://larm
+ cl_entities[zombieent].z_larm = limbent;
+ break;
+ case 2://rarm
+ cl_entities[zombieent].z_rarm = limbent;
+ break;
+
+ }
+}
+
+
+#define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x);
+
+/*
+=====================
+CL_ParseServerMessage
+=====================
+*/
+void CL_ParseServerMessage (void)
+{
+ int cmd;
+ int i;
+
+//
+// if recording demos, copy the message out
+//
+ if (cl_shownet.value == 1)
+ Con_Printf ("%i ",net_message.cursize);
+ else if (cl_shownet.value == 2)
+ Con_Printf ("------------------\n");
+
+ cl.onground = false; // unless the server says otherwise
+//
+// parse the message
+//
+ MSG_BeginReading ();
+
+ while (1)
+ {
+ if (msg_badread)
+ Host_Error ("CL_ParseServerMessage: Bad server message");
+
+ cmd = MSG_ReadByte ();
+
+ if (cmd == -1)
+ {
+ SHOWNET("END OF MESSAGE");
+ return; // end of message
+ }
+
+ // if the high bit of the command byte is set, it is a fast update
+ if (cmd & 128)//checking if it's an entity update, if it is the 7th bit will be on, this is checking for that
+ {
+ SHOWNET("fast update");
+ CL_ParseUpdate (cmd&127);//here we strip the cmd from the value of the 7th (128) bit, to only give the rest of the commands
+ continue;
+ }
+
+ SHOWNET(svc_strings[cmd]);
+
+ // other commands
+ switch (cmd)
+ {
+ default:
+ Host_Error ("CL_ParseServerMessage: Illegible server message (%i)\n", cmd);
+ break;
+
+ case svc_nop:
+ break;
+
+ case svc_time:
+ cl.mtime[1] = cl.mtime[0];
+ cl.mtime[0] = MSG_ReadFloat ();
+ break;
+
+ case svc_clientdata:
+ i = MSG_ReadShort ();
+ CL_ParseClientdata (i);
+ break;
+
+ case svc_version:
+ i = MSG_ReadLong ();
+ if (i != PROTOCOL_VERSION)
+ Host_Error ("CL_ParseServerMessage: Server is protocol %i instead of %i\n", i, PROTOCOL_VERSION);
+ break;
+
+ case svc_disconnect:
+ Host_EndGame ("Server disconnected\n");
+
+ case svc_print:
+ Con_Printf ("%s", MSG_ReadString ());
+ break;
+
+ case svc_centerprint:
+ SCR_CenterPrint (MSG_ReadString ());
+ break;
+
+ case svc_useprint:
+ SCR_UsePrint (MSG_ReadByte (),MSG_ReadShort (),MSG_ReadByte ());
+ break;
+ case svc_maxammo:
+ domaxammo = true;
+ break;
+ case svc_stufftext:
+ Cbuf_AddText (MSG_ReadString ());
+ break;
+ case svc_serverinfo:
+ CL_ParseServerInfo ();
+ vid.recalc_refdef = true; // leave intermission full screen
+ break;
+
+ case svc_setangle:
+ for (i=0 ; i<3 ; i++)
+ cl.viewangles[i] = MSG_ReadAngle ();
+ break;
+
+ case svc_setview:
+ cl.viewentity = MSG_ReadShort ();
+ break;
+
+ case svc_lightstyle:
+ i = MSG_ReadByte ();
+ if (i >= MAX_LIGHTSTYLES)
+ Sys_Error ("svc_lightstyle > MAX_LIGHTSTYLES");
+ Q_strcpy (cl_lightstyle[i].map, MSG_ReadString());
+ cl_lightstyle[i].length = Q_strlen(cl_lightstyle[i].map);
+ break;
+
+ case svc_sound:
+ CL_ParseStartSoundPacket();
+ break;
+
+ case svc_stopsound:
+ i = MSG_ReadShort();
+ S_StopSound(i>>3, i&7);
+ break;
+
+ case svc_updatename:
+ i = MSG_ReadByte ();
+ if (i >= cl.maxclients)
+ Host_Error ("CL_ParseServerMessage: svc_updatename > MAX_SCOREBOARD");
+ strcpy (cl.scores[i].name, MSG_ReadString ());
+ break;
+
+ case svc_updatepoints:
+ i = MSG_ReadByte ();
+ if (i >= cl.maxclients)
+ Host_Error ("CL_ParseServerMessage: svc_updatepoints > MAX_SCOREBOARD");
+ cl.scores[i].points = MSG_ReadLong ();
+ break;
+
+ case svc_updatekills:
+ i = MSG_ReadByte ();
+ if (i >= cl.maxclients)
+ Host_Error ("CL_ParseServerMessage: svc_updatepoints > MAX_SCOREBOARD");
+ cl.scores[i].kills = MSG_ReadShort ();
+ break;
+
+
+ case svc_particle:
+ R_ParseParticleEffect ();
+ break;
+
+ case svc_spawnbaseline:
+ i = MSG_ReadShort ();
+ // must use CL_EntityNum() to force cl.num_entities up
+ CL_ParseBaseline (CL_EntityNum(i));
+ break;
+ case svc_spawnstatic:
+ CL_ParseStatic ();
+ break;
+ case svc_temp_entity:
+ CL_ParseTEnt ();
+ break;
+
+ case svc_setpause:
+ {
+ cl.paused = MSG_ReadByte ();
+
+ if (cl.paused)
+ {
+ CDAudio_Pause ();
+ }
+ else
+ {
+ CDAudio_Resume ();
+ }
+ }
+ break;
+
+ case svc_signonnum:
+ i = MSG_ReadByte ();
+ if (i <= cls.signon)
+ Host_Error ("Received signon %i when at %i", i, cls.signon);
+ cls.signon = i;
+ Con_DPrintf("Signon: %i \n",i);
+ CL_SignonReply ();
+ break;
+
+ case svc_updatestat:
+ i = MSG_ReadByte ();
+ if (i < 0 || i >= MAX_CL_STATS)
+ Sys_Error ("svc_updatestat: %i is invalid", i);
+ cl.stats[i] = MSG_ReadLong ();
+ break;
+
+ case svc_spawnstaticsound:
+ CL_ParseStaticSound ();
+ break;
+
+ case svc_cdtrack:
+ cl.cdtrack = MSG_ReadByte ();
+ cl.looptrack = MSG_ReadByte ();
+
+ if (strcmpi(bgmtype.string,"cd") == 0)
+ {
+ if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) )
+ CDAudio_Play ((byte)cls.forcetrack, true);
+ else
+ CDAudio_Play ((byte)cl.cdtrack, true);
+ }
+ else
+ CDAudio_Stop();
+ break;
+
+ case svc_intermission:
+ cl.intermission = 1;
+ cl.completed_time = cl.time;
+ vid.recalc_refdef = true; // go to full screen
+ break;
+
+ case svc_finale:
+ cl.intermission = 2;
+ cl.completed_time = cl.time;
+ vid.recalc_refdef = true; // go to full screen
+ SCR_CenterPrint (MSG_ReadString ());
+ break;
+
+ case svc_cutscene:
+ cl.intermission = 3;
+ cl.completed_time = cl.time;
+ vid.recalc_refdef = true; // go to full screen
+ SCR_CenterPrint (MSG_ReadString ());
+ break;
+
+ case svc_sellscreen:
+ Cmd_ExecuteString ("help", src_command);
+ break;
+
+ case svc_skybox:
+ Sky_LoadSkyBox(MSG_ReadString());
+ break;
+ case svc_songegg:
+ CDAudio_Track(MSG_ReadString());
+ break;
+ case svc_fog:
+ Fog_ParseServerMessage ();
+ break;
+
+ case svc_achievement:
+ HUD_Parse_Achievement (MSG_ReadByte());
+ break;
+
+ case svc_hitmark:
+ Hitmark_Time = sv.time + 0.2;
+ break;
+
+ case svc_weaponfire:
+ CL_ParseWeaponFire();
+ break;
+
+ case svc_limbupdate:
+ CL_ParseLimbUpdate();
+ break;
+
+ case svc_bspdecal:
+ CL_ParseBSPDecal ();
+ break;
+ }
+ }
+}
+
diff --git a/source/cl_slist.c b/source/cl_slist.c
new file mode 100644
index 0000000..bc5dc97
--- /dev/null
+++ b/source/cl_slist.c
@@ -0,0 +1,196 @@
+/*
+Copyright (C) 1999,2000 contributors of the QuakeForge project
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+#include "cl_slist.h"
+
+server_entry_t slist[MAX_SERVER_LIST];
+//--------------------------------------UTILS-----------------------------------
+char *Cmd_MakeArgs (int start)
+{
+ int i, c;
+
+ static char text[1024];
+
+ text[0] = 0;
+ c = Cmd_Argc();
+ for (i = start; i < c; i++) {
+ if (i > start)
+ strncat (text, " ", sizeof(text) - strlen(text) - 1);
+ strncat (text, Cmd_Argv(i), sizeof(text) - strlen(text) - 1);
+ }
+
+ return text;
+}
+
+char *CreateSpaces(int amount)
+{
+ static char spaces[1024];
+ int size;
+
+ size = bound(1, amount, sizeof(spaces) - 1);
+ memset(spaces, ' ', size);
+ spaces[size] = 0;
+
+ return spaces;
+}
+//------------------------------------------------------------------------------
+void SList_Init (void)
+{
+ memset (&slist, 0, sizeof(slist));
+}
+
+void SList_Shutdown (void)
+{
+ FILE *f;
+
+ // if the first is empty already that means there isn't a single entry
+ if (!slist[0].server)
+ return;
+
+ //if (!(f = fopen("servers.lst", "wt")))
+ if (!(f = fopen(va("%s/servers.lst", com_gamedir), "wt")))
+ {
+ Con_DPrintf ("Couldn't open servers.lst\n");
+ return;
+ }
+ SList_Save (f);
+ fclose (f);
+}
+
+void SList_Set (int i, char *addr, char *desc)
+{
+ if (i >= MAX_SERVER_LIST || i < 0)
+ Sys_Error ("SList_Set: Bad index %d", i);
+
+ if (slist[i].server)
+ Z_Free (slist[i].server);
+ if (slist[i].description)
+ Z_Free (slist[i].description);
+
+ slist[i].server = CopyString (addr);
+ slist[i].description = CopyString (desc);
+}
+
+void SList_Reset_NoFree (int i)
+{
+ if (i >= MAX_SERVER_LIST || i < 0)
+ Sys_Error ("SList_Reset_NoFree: Bad index %d", i);
+
+ slist[i].description = slist[i].server = NULL;
+}
+
+void SList_Reset (int i)
+{
+ if (i >= MAX_SERVER_LIST || i < 0)
+ Sys_Error ("SList_Reset: Bad index %d", i);
+
+ if (slist[i].server)
+ {
+ Z_Free (slist[i].server);
+ slist[i].server = NULL;
+ }
+
+ if (slist[i].description)
+ {
+ Z_Free (slist[i].description);
+ slist[i].description = NULL;
+ }
+}
+
+void SList_Switch (int a, int b)
+{
+ server_entry_t temp;
+
+ if (a >= MAX_SERVER_LIST || a < 0)
+ Sys_Error ("SList_Switch: Bad index %d", a);
+ if (b >= MAX_SERVER_LIST || b < 0)
+ Sys_Error ("SList_Switch: Bad index %d", b);
+
+ memcpy (&temp, &slist[a], sizeof(temp));
+ memcpy (&slist[a], &slist[b], sizeof(temp));
+ memcpy (&slist[b], &temp, sizeof(temp));
+}
+
+int SList_Length (void)
+{
+ int count;
+
+ for (count = 0 ; count < MAX_SERVER_LIST && slist[count].server ; count++)
+ ;
+
+ return count;
+}
+
+void SList_Load (void)
+{
+ int c, len, argc, count;
+ char line[128], *desc, *addr;
+ FILE *f;
+
+ if (!(f = fopen (va("%s/servers.lst", com_gamedir), "rt")))
+ //if (!(f = fopen("servers.lst", "rt")))
+ return;
+
+ count = len = 0;
+ while ((c = getc(f)))
+ {
+ if (c == '\n' || c == '\r' || c == EOF)
+ {
+ if (c == '\r' && (c = getc(f)) != '\n' && c != EOF)
+ ungetc (c, f);
+
+ line[len] = 0;
+ len = 0;
+ Cmd_TokenizeString (line);
+
+ if ((argc = Cmd_Argc()) >= 1)
+ {
+ addr = Cmd_Argv(0);
+ desc = (argc >= 2) ? Cmd_Args() : "Unknown";
+ SList_Set (count, addr, desc);
+ if (++count == MAX_SERVER_LIST)
+ break;
+ }
+ if (c == EOF)
+ break; //just in case an EOF follows a '\r'
+ }
+ else
+ {
+ if (len + 1 < sizeof(line))
+ line[len++] = c;
+ }
+ }
+
+ fclose (f);
+}
+
+void SList_Save (FILE *f)
+{
+ int i;
+
+ for (i=0 ; ientity == ent)
+ {
+ b->entity = ent;
+ b->model = m;
+ b->endtime = cl.time + 0.2;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ return;
+ }
+
+// find a free beam
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ {
+ b->entity = ent;
+ b->model = m;
+ b->endtime = cl.time + 0.2;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ return;
+ }
+ }
+ Con_Printf ("beam list overflow!\n");
+}
+
+enum
+{
+ DECAL_NONE,
+ DECAL_BULLTMRK,
+ DECAL_BLOODMRK1,
+ DECAL_BLOODMRK2,
+ DECAL_BLOODMRK3,
+ DECAL_EXPLOMRK
+};
+//==============================================================================
+extern cvar_t r_decal_bullets;
+extern cvar_t r_decal_explosions;
+
+extern int decal_blood1, decal_blood2, decal_blood3, decal_burn, decal_mark, decal_glow;
+//==============================================================================
+/*
+=================
+CL_ParseTEnt
+=================
+*/
+void CL_ParseTEnt (void)
+{
+ int type;
+ vec3_t pos;
+ dlight_t *dl;
+ //int rnd;
+ int colorStart, colorLength;
+
+ type = MSG_ReadByte ();
+ switch (type)
+ {
+ case TE_WIZSPIKE: // spike hitting wall
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ if (r_part_spikes.value == 2 && !cl_q3gunshot_mod)
+ cl_q3gunshot_mod = Mod_ForName ("progs/bullet.md3", true);
+ R_RunParticleEffect (pos, vec3_origin, 20, 30);
+ //S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
+ break;
+
+ case TE_KNIGHTSPIKE: // spike hitting wall
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ if (r_part_spikes.value == 2 && !cl_q3gunshot_mod)
+ cl_q3gunshot_mod = Mod_ForName ("progs/bullet.md3", true);
+ R_RunParticleEffect (pos, vec3_origin, 226, 20);
+ //S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
+ break;
+
+ case TE_SPIKE: // spike hitting wall
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ if (r_part_spikes.value == 2 && !cl_q3gunshot_mod)
+ cl_q3gunshot_mod = Mod_ForName ("progs/bullet.md3", true);
+ //R00k--start
+ if (r_decal_bullets.value)
+ {
+ R_SpawnDecalStatic(pos, decal_mark, 8);
+ }
+ //R00k--end
+
+ R_RunParticleEffect (pos, vec3_origin, 0, 10);
+
+ break;
+ case TE_SUPERSPIKE: // super spike hitting wall
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ if (r_part_spikes.value == 2 && !cl_q3gunshot_mod)
+ cl_q3gunshot_mod = Mod_ForName ("progs/bullet.md3", true);
+ R_RunParticleEffect (pos, vec3_origin, 0, 20);
+
+ //R00k--start
+ if (r_decal_bullets.value)
+ {
+ R_SpawnDecalStatic(pos, decal_mark, 10);
+ }
+ //R00k--end
+
+ break;
+
+ case TE_GUNSHOT: // bullet hitting wall
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ if (r_part_gunshots.value == 2 && !cl_q3gunshot_mod)
+ cl_q3gunshot_mod = Mod_ForName ("progs/bullet.md3", true);
+
+ //R00k--start
+ if (r_decal_bullets.value)
+ {
+ R_SpawnDecalStatic(pos, decal_mark, 8);
+ }
+ //R00k--end
+ R_RunParticleEffect (pos, vec3_origin, 0, 20);
+ break;
+
+ case TE_EXPLOSION: // rocket explosion
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ R_ParticleExplosion (pos);
+ if (r_decal_explosions.value)
+ {
+ R_SpawnDecalStatic(pos, decal_burn, 100);
+ }
+ dl = CL_AllocDlight (0);
+ VectorCopy (pos, dl->origin);
+ dl->radius = 350;
+ dl->die = cl.time + 0.5;
+ dl->decay = 300;
+ S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 0.5);
+ break;
+ case TE_RAYSPLASHGREEN:
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ dl = CL_AllocDlight (0);
+ VectorCopy (pos, dl->origin);
+ dl->radius = 65;
+ dl->die = cl.time + 0.3;
+ dl->decay = 300;
+ dl->color[0] = 0;
+ dl->color[1] = 255;
+ dl->color[2] = 0;
+ R_RunParticleEffect (pos, vec3_origin, 0, 256);
+ //S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 0.5); // NZPFIXME - add raygun hum
+ break;
+ case TE_RAYSPLASHRED:
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ dl = CL_AllocDlight (0);
+ VectorCopy (pos, dl->origin);
+ dl->radius = 65;
+ dl->die = cl.time + 0.3;
+ dl->decay = 300;
+ dl->color[0] = 255;
+ dl->color[1] = 0;
+ dl->color[2] = 0;
+ R_RunParticleEffect (pos, vec3_origin, 0, 512);
+ //S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 0.5); // NZPFIXME - add raygun hum
+ break;
+ case TE_TAREXPLOSION: // tarbaby explosion
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ R_BlobExplosion (pos);
+ S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+ break;
+
+ case TE_LIGHTNING1: // lightning bolts
+ CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true));
+ break;
+
+ case TE_LIGHTNING2: // lightning bolts
+ CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true));
+ break;
+
+ case TE_LIGHTNING3: // lightning bolts
+ CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true));
+ break;
+
+// PGM 01/21/97
+ case TE_BEAM: // grappling hook beam
+ CL_ParseBeam (Mod_ForName("progs/beam.mdl", true));
+ break;
+// PGM 01/21/97
+
+ case TE_LAVASPLASH:
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ R_LavaSplash (pos);
+ dl = CL_AllocDlight (0);
+ VectorCopy (pos, dl->origin);
+ dl->radius = 150;
+ dl->die = cl.time + 0.75;
+ dl->decay = 200;
+ break;
+
+ case TE_TELEPORT:
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ if (r_part_telesplash.value == 2 && !cl_q3teleport_mod)
+ cl_q3teleport_mod = Mod_ForName ("progs/telep.md3", true);
+ R_TeleportSplash (pos);
+ break;
+
+ case TE_EXPLOSION2: // color mapped explosion
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+ colorStart = MSG_ReadByte ();
+ colorLength = MSG_ReadByte ();
+ R_ParticleExplosion2 (pos, colorStart, colorLength);
+
+ if (r_decal_explosions.value)
+ {
+ R_SpawnDecalStatic(pos, decal_burn, 100);
+ }
+
+ dl = CL_AllocDlight (0);
+ VectorCopy (pos, dl->origin);
+ dl->radius = 350;
+ dl->die = cl.time + 0.5;
+ dl->decay = 300;
+ S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+ break;
+
+ default:
+ Sys_Error ("CL_ParseTEnt: bad type");
+ }
+}
+
+
+/*
+=================
+CL_NewTempEntity
+=================
+*/
+entity_t *CL_NewTempEntity (void)
+{
+ entity_t *ent;
+
+ if (cl_numvisedicts == MAX_VISEDICTS)
+ return NULL;
+ if (num_temp_entities == MAX_TEMP_ENTITIES)
+ return NULL;
+ ent = &cl_temp_entities[num_temp_entities];
+ memset (ent, 0, sizeof(*ent));
+ num_temp_entities++;
+ cl_visedicts[cl_numvisedicts] = ent;
+ cl_numvisedicts++;
+
+ ent->colormap = vid.colormap;
+ return ent;
+}
+
+/*
+=================
+TraceLineN
+=================
+*/
+qboolean TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal)
+{
+ trace_t trace;
+
+ memset (&trace, 0, sizeof(trace));
+ if (!SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, start, end, &trace))
+ {
+ if (trace.fraction < 1)
+ {
+ VectorCopy (trace.endpos, impact);
+ if (normal)
+ VectorCopy (trace.plane.normal, normal);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QMB_Lightning_Splash (vec3_t org);
+extern cvar_t scr_ofsy;
+/*
+=================
+CL_UpdateTEnts
+=================
+*/
+void CL_UpdateTEnts (void)
+{
+ int i;
+ beam_t *b;
+ vec3_t dist, org, beamstart;
+ float d;
+ entity_t *ent;
+ float yaw, pitch;
+ float forward;
+
+ int j;
+ vec3_t beamend;
+// qboolean sparks = false;
+
+ num_temp_entities = 0;
+
+// update lightning
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ continue;
+
+ // if coming from the player, update the start position
+ if (b->entity == cl.viewentity)
+ {
+ VectorCopy (cl_entities[cl.viewentity].origin, b->start);
+
+ b->start[2] += cl.crouch + bound(-7, scr_ofsy.value, 4);
+ b->start[2] += bound(0, cl_lightning_zadjust.value, 20);//progs.dat aims from 20 for traceline
+
+ if (cl_truelightning.value)
+ {
+ vec3_t forward, v, org, ang;
+ float f, delta;
+ trace_t trace;
+
+ f = fmax(0, fmin(1, cl_truelightning.value));
+
+ VectorSubtract (playerbeam_end, cl_entities[cl.viewentity].origin, v);
+ //v[2] -= 22; // adjust for view height
+ v[2] -= cl.crouch; //
+ v[2] -= bound(0, cl_lightning_zadjust.value, 20);
+
+ vectoangles (v, ang);
+
+ // lerp pitch
+ ang[0] = -ang[0];
+ if (ang[0] < -180)
+ ang[0] += 360;
+ ang[0] += (cl.viewangles[0] - ang[0]) * f;
+
+ // lerp yaw
+ delta = cl.viewangles[1] - ang[1];
+ if (delta > 180)
+ delta -= 360;
+ if (delta < -180)
+ delta += 360;
+ ang[1] += delta * f;
+ ang[2] = 0;
+
+ AngleVectors (ang, forward, NULLVEC, NULLVEC);
+ VectorScale(forward, 600, forward);
+ VectorCopy(cl_entities[cl.viewentity].origin, org);
+ org[2] += bound(0, cl_lightning_zadjust.value, 20);//progs.dat aims from 20 for teaceline
+ VectorAdd(org, forward, b->end);
+
+ memset (&trace, 0, sizeof(trace_t));
+ if (!SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, org, b->end, &trace))
+ VectorCopy(trace.endpos, b->end);
+ }
+ }
+
+/*
+ // if coming from the player, update the start position
+ if (b->entity == cl.viewentity)
+ {
+ VectorCopy (cl_entities[cl.viewentity].origin, b->start);
+ }
+*/
+ // calculate pitch and yaw
+ VectorSubtract (b->end, b->start, dist);
+
+ if (dist[1] == 0 && dist[0] == 0)
+ {
+ yaw = 0;
+ if (dist[2] > 0)
+ pitch = 90;
+ else
+ pitch = 270;
+ }
+ else
+ {
+ yaw = (int) (atan2f(dist[1], dist[0]) * 180 / M_PI);
+ if (yaw < 0)
+ yaw += 360;
+
+ forward = sqrtf (dist[0]*dist[0] + dist[1]*dist[1]);
+ pitch = (int) (atan2f(dist[2], forward) * 180 / M_PI);
+ if (pitch < 0)
+ pitch += 360;
+ }
+ // add new entities for the lightning
+ VectorCopy(b->start, org);
+ VectorCopy(b->start, beamstart);
+ d = VectorNormalize (dist);
+ VectorScale (dist, 30, dist);
+
+ if (key_dest == key_game)
+ {
+ for ( ; d > 0 ; d -= 30)
+ {
+ if ((qmb_initialized && r_part_lightning.value) && (!cl.paused))
+ {
+ VectorAdd(org, dist, beamend);
+ for (j=0 ; j<3 ; j++)
+ beamend[j] += ((rand()%10)-5);
+ QMB_LightningBeam (beamstart, beamend);
+ //if ((r_glowlg.value) && (r_dynamic.value))
+ // CL_NewDlight (i, beamstart, 100, 0.1, lt_blue);
+ VectorCopy(beamend, beamstart);
+ }
+ else
+ {
+ if (!(ent = CL_NewTempEntity()))
+ return;
+ VectorCopy(org, ent->origin);
+ ent->model = b->model;
+ ent->angles[0] = pitch;
+ ent->angles[1] = yaw;
+ ent->angles[2] = rand() % 360;
+ }
+ VectorAdd(org, dist, org);
+ }
+ }
+/*
+ // add new entities for the lightning
+ VectorCopy (b->start, org);
+ d = VectorNormalize(dist);
+ while (d > 0)
+ {
+ ent = CL_NewTempEntity ();
+ if (!ent)
+ return;
+ VectorCopy (org, ent->origin);
+ ent->model = b->model;
+ ent->angles[0] = pitch;
+ ent->angles[1] = yaw;
+ ent->angles[2] = rand()%360;
+
+ for (i=0 ; i<3 ; i++)
+ org[i] += dist[i]*30;
+ d -= 30;
+ }
+*/
+ }
+
+}
+
+
diff --git a/source/client.h b/source/client.h
new file mode 100644
index 0000000..bac4374
--- /dev/null
+++ b/source/client.h
@@ -0,0 +1,433 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// client.h
+
+typedef struct
+{
+ vec3_t viewangles;
+
+// intended velocities
+ float forwardmove;
+ float sidemove;
+ float upmove;
+} usercmd_t;
+
+typedef struct
+{
+ int length;
+ char map[MAX_STYLESTRING];
+} lightstyle_t;
+
+typedef struct
+{
+ char name[MAX_SCOREBOARDNAME];
+ float entertime;
+ int points;
+ int maxpoints;
+ int kills;
+ int headshots;
+} scoreboard_t;
+
+typedef struct
+{
+ int destcolor[3];
+ int percent; // 0-256
+} cshift_t;
+
+typedef enum
+{
+ lt_default, lt_muzzleflash, lt_explosion, lt_rocket,
+ lt_red, lt_blue, lt_redblue, lt_green, NUM_DLIGHTTYPES,
+ lt_explosion2, lt_explosion3, lt_rayred, lt_raygreen
+} dlighttype_t;
+
+#define CSHIFT_CONTENTS 0
+#define CSHIFT_DAMAGE 1
+#define CSHIFT_BONUS 2
+#define CSHIFT_POWERUP 3
+#define NUM_CSHIFTS 4
+
+#define NAME_LENGTH 64
+
+//
+// client_state_t should hold all pieces of the client state
+//
+
+#define SIGNONS 4 // signon messages to receive before connected
+
+#define MAX_DLIGHTS 32
+typedef struct
+{
+ vec3_t origin;
+ float radius;
+ float die; // stop lighting after this time
+ float decay; // drop this each second
+ float minlight; // don't add when contributing less
+ int key;
+ qboolean dark; // subtracts light instead of adding
+ vec3_t color; //LordHavoc Lit. Support
+ int type; // color
+} dlight_t;
+
+
+#define MAX_BEAMS 24
+typedef struct
+{
+ int entity;
+ struct model_s *model;
+ float endtime;
+ vec3_t start, end;
+} beam_t;
+
+#define MAX_EFRAGS 640
+
+#define MAX_MAPSTRING 2048
+#define MAX_DEMOS 8
+#define MAX_DEMONAME 16
+
+typedef enum {
+ca_dedicated, // a dedicated server with no ability to start a client
+ca_disconnected, // full screen console with no connection
+ca_connected // valid netcon, talking to a server
+} cactive_t;
+
+//
+// the client_static_t structure is persistant through an arbitrary number
+// of server connections
+//
+typedef struct
+{
+ cactive_t state;
+
+// personalization data sent to server
+ char mapstring[MAX_QPATH];
+ char spawnparms[MAX_MAPSTRING]; // to restart a level
+// demo loop control
+ int demonum; // -1 = don't play demos
+ char demos[MAX_DEMOS][MAX_DEMONAME]; // when not playing
+
+// demo recording info must be here, because record is started before
+// entering a map (and clearing client_state_t)
+ qboolean demorecording;
+ qboolean demoplayback;
+ qboolean timedemo;
+ int forcetrack; // -1 = use normal cd track
+ int demofile;
+ int td_lastframe; // to meter out one message a frame
+ int td_startframe; // host_framecount at start
+ float td_starttime; // realtime at second frame of timedemo
+
+
+// connection information
+ int signon; // 0 to SIGNONS
+ struct qsocket_s *netcon;
+ sizebuf_t message; // writing buffer to send to server
+
+} client_static_t;
+
+extern client_static_t cls;
+
+typedef struct {
+ float lerptime;
+ float framechange; //marks time of last frame change - for halflife model sequencing.
+ float oldframechange;
+ float lerprate; //inverse rate...
+ vec3_t origin;
+ vec3_t angles;
+ //trailstate_t *trailstate; //when to next throw out a trail
+// trailstate_t *emitstate; //when to next emit
+ unsigned short frame;
+} lerpents_t;
+//
+// the client_state_t structure is wiped completely at every
+// server signon
+//
+typedef struct
+{
+ int movemessages; // since connecting to this server
+ // throw out the first couple, so the player
+ // doesn't accidentally do something the
+ // first frame
+ usercmd_t cmd; // last command sent to the server
+
+// information for local display
+ int stats[MAX_CL_STATS]; // health, etc
+ int perks; // Perk icons.
+ int progress_bar; // Perk icons.
+ float item_gettime[32]; // cl.time of aquiring item, for blinking
+ float faceanimtime; // use anim frame if cl.time < this
+
+ cshift_t cshifts[NUM_CSHIFTS]; // color shifts for damage, powerups
+ cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types
+
+// the client maintains its own idea of view angles, which are
+// sent to the server each frame. The server sets punchangle when
+// the view is temporarliy offset, and an angle reset commands at the start
+// of each level and after teleporting.
+ vec3_t mviewangles[2]; // during demo playback viewangles is lerped
+ // between these
+ vec3_t viewangles;
+
+ vec3_t mvelocity[2]; // update by server, used for lean+bob
+ // (0 is newest)
+ vec3_t velocity; // lerped between mvelocity[0] and [1]
+
+ vec3_t punchangle; // temporary offset
+ vec3_t gun_kick; // temporary kick
+
+// pitch drifting vars
+ float idealpitch;
+ float pitchvel;
+ qboolean nodrift;
+ float driftmove;
+ double laststop;
+
+ float viewheight;
+ float crouch; // local amount for smoothing stepups
+
+ qboolean paused; // send over by server
+ qboolean onground;
+ qboolean inwater;
+
+ int intermission; // don't change view angle, full screen, etc
+ int completed_time; // latched at intermission start
+
+ double mtime[2]; // the timestamp of last two messages
+ double time; // clients view of time, should be between
+ // servertime and oldservertime to generate
+ // a lerp point for other data
+ double oldtime; // previous cl.time, time-oldtime is used
+ // to decay light values and smooth step ups
+ double ctime; // joe: copy of cl.time, to avoid incidents caused by rewind
+ double thundertime; // R00k
+ double laser_point_time;
+ float last_received_message; // (realtime) for net trouble icon
+
+//
+// information that is static for the entire time connected to a server
+//
+ struct model_s *model_precache[MAX_MODELS];
+ struct sfx_s *sound_precache[MAX_SOUNDS];
+
+ char levelname[40]; // for display on solo scoreboard
+ int viewentity; // cl_entities[cl.viewentity] = player
+ int maxclients;
+ int gametype;
+
+ lerpents_t *lerpents;
+
+// refresh related state
+ struct model_s *worldmodel; // cl_entities[0].model
+ struct efrag_s *free_efrags;
+ int num_entities; // held in cl_entities array
+ int num_statics; // held in cl_staticentities array
+ entity_t viewent; // the gun model
+ entity_t viewent2; // the second gun model
+
+ int cdtrack, looptrack; // cd audio
+
+// frag scoreboard
+ scoreboard_t *scores; // [cl.maxclients]
+} client_state_t;
+
+
+//
+// cvars
+//
+extern cvar_t cl_name;
+extern cvar_t cl_maxfps; // dr_mabuse1981: maxfps setting
+
+extern cvar_t cl_upspeed;
+extern float cl_forwardspeed;
+extern float cl_backspeed;
+extern float cl_sidespeed;
+
+extern cvar_t cl_movespeedkey;
+
+extern cvar_t cl_yawspeed;
+extern cvar_t cl_pitchspeed;
+extern cvar_t r_hlbsponly;
+extern cvar_t cl_anglespeedkey;
+
+extern cvar_t cl_lightning_zadjust;
+extern cvar_t cl_truelightning;
+
+extern cvar_t cl_autofire;
+
+extern cvar_t cl_shownet;
+extern cvar_t cl_nolerp;
+
+extern cvar_t cl_pitchdriftspeed;
+extern cvar_t lookspring;
+extern cvar_t lookstrafe;
+extern cvar_t in_sensitivity;
+extern cvar_t in_tolerance;
+extern cvar_t in_acceleration;
+extern cvar_t in_analog_strafe;
+extern cvar_t in_x_axis_adjust;
+extern cvar_t in_y_axis_adjust;
+
+extern cvar_t in_mlook; //Heffo - mlook cvar
+
+extern cvar_t in_aimassist;
+
+extern cvar_t m_pitch;
+extern cvar_t m_yaw;
+extern cvar_t m_forward;
+extern cvar_t m_side;
+
+
+#define MAX_TEMP_ENTITIES 64 // lightning bolts, etc
+#define MAX_STATIC_ENTITIES 128 // torches, etc
+
+extern client_state_t cl;
+
+// FIXME, allocate dynamically
+extern efrag_t cl_efrags[MAX_EFRAGS];
+extern entity_t cl_entities[MAX_EDICTS];
+extern entity_t cl_static_entities[MAX_STATIC_ENTITIES];
+extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
+extern dlight_t cl_dlights[MAX_DLIGHTS];
+extern entity_t cl_temp_entities[MAX_TEMP_ENTITIES];
+extern beam_t cl_beams[MAX_BEAMS];
+
+//=============================================================================
+
+//
+// cl_main
+//
+dlight_t *CL_AllocDlight (int key);
+void CL_NewDlight (int key, vec3_t origin, float radius, float time, int type);
+void CL_DecayLights (void);
+
+void CL_Init (void);
+
+void CL_EstablishConnection (char *host);
+void CL_Signon1 (void);
+void CL_Signon2 (void);
+void CL_Signon3 (void);
+void CL_Signon4 (void);
+
+void CL_Disconnect (void);
+void CL_Disconnect_f (void);
+void CL_NextDemo (void);
+
+#define MAX_VISEDICTS 256
+extern int cl_numvisedicts;
+extern entity_t *cl_visedicts[MAX_VISEDICTS];
+
+
+
+extern tagentity_t q3player_body, q3player_head;
+
+// model indexes
+typedef enum modelindex_s
+{
+ mi_player,
+ mi_eyes,
+ mi_flame0,
+ mi_flame1,
+ mi_flame2,
+ mi_q3torso,
+ mi_q3head,
+/*
+ mi_vw_light,
+ mi_vw_nail1,
+ mi_vw_nail2,
+ mi_vw_rock1,
+ mi_vw_rock2,
+ mi_vw_shot1,
+ mi_vw_shot2,
+ mi_vw_player,
+*/
+ NUM_MODELINDEX
+} modelindex_t;
+
+extern modelindex_t cl_modelindex[NUM_MODELINDEX];
+extern char *cl_modelnames[NUM_MODELINDEX];
+
+//
+// cl_input
+//
+typedef struct
+{
+ int down[2]; // key nums holding it down
+ int state; // low bit is down state
+} kbutton_t;
+
+extern kbutton_t in_klook;//Heffo - mlook cvar
+extern kbutton_t in_strafe;
+extern kbutton_t in_speed;
+
+
+
+void CL_InitInput (void);
+void CL_SendCmd (void);
+void CL_SendMove (usercmd_t *cmd);
+
+void CL_ParseTEnt (void);
+void CL_UpdateTEnts (void);
+
+void CL_ClearState (void);
+
+
+int CL_ReadFromServer (void);
+void CL_WriteToServer (usercmd_t *cmd);
+void CL_BaseMove (usercmd_t *cmd);
+
+
+float CL_KeyState (kbutton_t *key);
+char *Key_KeynumToString (int keynum);
+
+//
+// cl_demo.c
+//
+void CL_StopPlayback (void);
+int CL_GetMessage (void);
+
+void CL_Stop_f (void);
+void CL_Record_f (void);
+void CL_PlayDemo_f (void);
+void CL_TimeDemo_f (void);
+
+//
+// cl_parse.c
+//
+void CL_ParseServerMessage (void);
+
+//
+// view
+//
+void V_StartPitchDrift (void);
+void V_StopPitchDrift (void);
+
+void V_RenderView (void);
+void V_UpdatePalette (void);
+void V_Register (void);
+void V_SetContentsColor (int contents);
+
+
+//
+// cl_tent
+//
+void CL_InitTEnts (void);
+void CL_SignonReply (void);
+entity_t *CL_NewTempEntity (void);
+qboolean TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);
diff --git a/source/cmd.c b/source/cmd.c
new file mode 100644
index 0000000..7f6d0b6
--- /dev/null
+++ b/source/cmd.c
@@ -0,0 +1,874 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// cmd.c -- Quake script command processing module
+
+#include "quakedef.h"
+
+void Cmd_ForwardToServer (void);
+
+#define MAX_ALIAS_NAME 32
+
+typedef struct cmdalias_s
+{
+ struct cmdalias_s *next;
+ char name[MAX_ALIAS_NAME];
+ char *value;
+} cmdalias_t;
+
+cmdalias_t *cmd_alias;
+
+int trashtest;
+int *trashspot;
+
+qboolean cmd_wait;
+
+//=============================================================================
+
+/*
+============
+Cmd_Wait_f
+
+Causes execution of the remainder of the command buffer to be delayed until
+next frame. This allows commands like:
+bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
+============
+*/
+void Cmd_Wait_f (void)
+{
+ cmd_wait = true;
+}
+
+/*
+=============================================================================
+
+ COMMAND BUFFER
+
+=============================================================================
+*/
+
+sizebuf_t cmd_text;
+
+/*
+============
+Cbuf_Init
+============
+*/
+void Cbuf_Init (void)
+{
+ SZ_Alloc (&cmd_text, 8192); // space for commands and script files
+}
+
+
+/*
+============
+Cbuf_AddText
+
+Adds command text at the end of the buffer
+============
+*/
+void Cbuf_AddText (char *text)
+{
+ int l;
+
+ l = Q_strlen (text);
+
+ if (cmd_text.cursize + l >= cmd_text.maxsize)
+ {
+ Con_Printf ("Cbuf_AddText: overflow\n");
+ return;
+ }
+
+ SZ_Write (&cmd_text, text, Q_strlen (text));
+}
+
+
+/*
+============
+Cbuf_InsertText
+
+Adds command text immediately after the current command
+Adds a \n to the text
+FIXME: actually change the command buffer to do less copying
+============
+*/
+void Cbuf_InsertText (char *text)
+{
+ char *temp;
+ int templen;
+
+// copy off any commands still remaining in the exec buffer
+ templen = cmd_text.cursize;
+ if (templen)
+ {
+ temp = Z_Malloc (templen);
+ Q_memcpy (temp, cmd_text.data, templen);
+ SZ_Clear (&cmd_text);
+ }
+ else
+ temp = NULL; // shut up compiler
+
+// add the entire text of the file
+ Cbuf_AddText (text);
+
+// add the copied off data
+ if (templen)
+ {
+ SZ_Write (&cmd_text, temp, templen);
+ Z_Free (temp);
+ }
+}
+
+/*
+============
+Cbuf_Execute
+============
+*/
+void Cbuf_Execute (void)
+{
+ int i;
+ char *text;
+ char line[1024];
+ int quotes;
+
+ while (cmd_text.cursize)
+ {
+// find a \n or ; line break
+ text = (char *)cmd_text.data;
+
+ quotes = 0;
+ for (i=0 ; i< cmd_text.cursize ; i++)
+ {
+ if (text[i] == '"')
+ quotes++;
+ if ( !(quotes&1) && text[i] == ';')
+ break; // don't break if inside a quoted string
+ if (text[i] == '\n')
+ break;
+ }
+
+
+ memcpy (line, text, i);
+ line[i] = 0;
+
+// delete the text from the command buffer and move remaining commands down
+// this is necessary because commands (exec, alias) can insert data at the
+// beginning of the text buffer
+
+ if (i == cmd_text.cursize)
+ cmd_text.cursize = 0;
+ else
+ {
+ i++;
+ cmd_text.cursize -= i;
+ Q_memcpy (text, text+i, cmd_text.cursize);
+ }
+
+// execute the command line
+ Cmd_ExecuteString (line, src_command);
+
+ if (cmd_wait)
+ { // skip out while text still remains in buffer, leaving it
+ // for next frame
+ cmd_wait = false;
+ break;
+ }
+ }
+}
+
+/*
+==============================================================================
+
+ SCRIPT COMMANDS
+
+==============================================================================
+*/
+
+/*
+===============
+Cmd_StuffCmds_f
+
+Adds command line parameters as script statements
+Commands lead with a +, and continue until a - or another +
+quake +prog jctest.qp +cmd amlev1
+quake -nosound +cmd amlev1
+===============
+*/
+void Cmd_StuffCmds_f (void)
+{
+ int i, j;
+ int s;
+ char *text, *build, c;
+
+ if (Cmd_Argc () != 1)
+ {
+ Con_Printf ("stuffcmds : execute command line parameters\n");
+ return;
+ }
+
+// build the combined string to parse from
+ s = 0;
+ for (i=1 ; i : execute a script file\n");
+ return;
+ }
+
+ mark = Hunk_LowMark ();
+ f = (char *)COM_LoadHunkFile (Cmd_Argv(1));
+ if (!f)
+ {
+ Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
+ return;
+ }
+ Con_Printf ("execing %s\n",Cmd_Argv(1));
+
+ Cbuf_InsertText (f);
+ Hunk_FreeToLowMark (mark);
+}
+
+
+/*
+===============
+Cmd_Echo_f
+
+Just prints the rest of the line to the console
+===============
+*/
+void Cmd_Echo_f (void)
+{
+ int i;
+
+ for (i=1 ; inext)
+ Con_Printf ("%s : %s\n", a->name, a->value);
+ return;
+ }
+
+ s = Cmd_Argv(1);
+ if (strlen(s) >= MAX_ALIAS_NAME)
+ {
+ Con_Printf ("Alias name is too long\n");
+ return;
+ }
+
+ // if the alias allready exists, reuse it
+ for (a = cmd_alias ; a ; a=a->next)
+ {
+ if (!strcmp(s, a->name))
+ {
+ Z_Free (a->value);
+ break;
+ }
+ }
+
+ if (!a)
+ {
+ a = Z_Malloc (sizeof(cmdalias_t));
+ a->next = cmd_alias;
+ cmd_alias = a;
+ }
+ strcpy (a->name, s);
+
+// copy the rest of the command line
+ cmd[0] = 0; // start out with a null string
+ c = Cmd_Argc();
+ for (i=2 ; i< c ; i++)
+ {
+ strcat (cmd, Cmd_Argv(i));
+ if (i != c)
+ strcat (cmd, " ");
+ }
+ strcat (cmd, "\n");
+
+ a->value = CopyString (cmd);
+}
+
+/*
+===============
+Cmd_Maps_f
+
+Listing maps by Crow_bar(c).
+===============
+*/
+#include
+void Cmd_Maps_f (void)
+{
+ char *s;
+
+ if (Cmd_Argc() < 2)
+ {
+ Con_Printf("Usage: maps \n");
+ Con_Printf("maps * for full listing\n");
+ return;
+ }
+
+ Con_Printf("-------------\n");
+
+ s = Cmd_Argv(1);
+
+ SceUID dir = sceIoDopen(va("%s/maps", com_gamedir));
+
+ if(dir < 0)
+ {
+ return;
+ }
+
+ SceIoDirent dirent;
+
+ memset(&dirent, 0, sizeof(SceIoDirent));
+
+ while(sceIoDread(dir, &dirent) > 0)
+ {
+ if(dirent.d_name[0] == '.')
+ {
+ continue;
+ }
+
+ if(!strcasecmp(COM_FileExtension (dirent.d_name),"bsp"))
+ {
+ if(s[0] == '*')
+ {
+ Con_Printf("%s\n",dirent.d_name);
+ }
+ else
+ {
+ int i = 0;
+ while(1)
+ {
+ if(s[i] == 0)
+ {
+ Con_Printf("%s\n",dirent.d_name);
+ break;
+ }
+#if 0
+ if(toupper(dirent.d_name[i]) != toupper(s[i]))
+#else
+ if(dirent.d_name[i] != s[i])
+#endif
+ {
+ break;
+ }
+ i++;
+ }
+ }
+ }
+ memset(&dirent, 0, sizeof(SceIoDirent));
+ }
+ sceIoDclose(dir);
+
+}
+
+/*
+=============================================================================
+
+ COMMAND EXECUTION
+
+=============================================================================
+*/
+
+typedef struct cmd_function_s
+{
+ struct cmd_function_s *next;
+ char *name;
+ xcommand_t function;
+} cmd_function_t;
+
+
+#define MAX_ARGS 80
+
+static int cmd_argc;
+static char *cmd_argv[MAX_ARGS];
+static char *cmd_null_string = "";
+static char *cmd_args = NULL;
+
+cmd_source_t cmd_source;
+
+
+static cmd_function_t *cmd_functions; // possible commands to execute
+
+// 2000-01-09 CmdList command by Maddes start
+/*
+========
+Cmd_List
+========
+*/
+void Cmd_List_f (void)
+{
+ cmd_function_t *cmd;
+ char *partial;
+ int len;
+ int count;
+
+ if (Cmd_Argc() > 1)
+ {
+ partial = Cmd_Argv (1);
+ len = Q_strlen(partial);
+ }
+ else
+ {
+ partial = NULL;
+ len = 0;
+ }
+
+ count=0;
+ for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+ {
+ if (partial && Q_strncmp (partial,cmd->name, len))
+ {
+ continue;
+ }
+ Con_Printf ("\"%s\"\n", cmd->name);
+ count++;
+ }
+
+ Con_Printf ("%i command(s)", count);
+ if (partial)
+ {
+ Con_Printf (" beginning with \"%s\"", partial);
+ }
+ Con_Printf ("\n");
+}
+// 2000-01-09 CmdList command by Maddes end
+
+// 2000-01-09 CvarList command by Maddes start
+/*
+=========
+Cvar_List
+=========
+*/
+void Cvar_List_f (void)
+{
+ cvar_t *cvar;
+ char *partial;
+ int len;
+ int count;
+
+ if (Cmd_Argc() > 1)
+ {
+ partial = Cmd_Argv (1);
+ len = Q_strlen(partial);
+ }
+ else
+ {
+ partial = NULL;
+ len = 0;
+ }
+
+ count=0;
+ for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
+ {
+ if (partial && Q_strncmp (partial,cvar->name, len))
+ {
+ continue;
+ }
+ Con_Printf ("\"%s\" is \"%s\"\n", cvar->name, cvar->string);
+ count++;
+ }
+
+ Con_Printf ("%i cvar(s)", count);
+ if (partial)
+ {
+ Con_Printf (" beginning with \"%s\"", partial);
+ }
+ Con_Printf ("\n");
+}
+// 2000-01-09 CvarList command by Maddes end
+
+
+/*
+============
+Cmd_Init
+============
+*/
+void Cmd_Init (void)
+{
+//
+// register our commands
+//
+ Cmd_AddCommand ("cmdlist", Cmd_List_f); // 2000-01-09 CmdList command by Maddes
+ Cmd_AddCommand ("cvarlist", Cvar_List_f); // 2000-01-09 CvarList command by Maddes
+ Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f);
+ Cmd_AddCommand ("exec",Cmd_Exec_f);
+ Cmd_AddCommand ("echo",Cmd_Echo_f);
+ Cmd_AddCommand ("alias",Cmd_Alias_f);
+ Cmd_AddCommand ("cmd", Cmd_ForwardToServer);
+ Cmd_AddCommand ("wait", Cmd_Wait_f);
+ Cmd_AddCommand ("maps", Cmd_Maps_f); //Crow_bar
+}
+
+/*
+============
+Cmd_Argc
+============
+*/
+int Cmd_Argc (void)
+{
+ return cmd_argc;
+}
+
+/*
+============
+Cmd_Argv
+============
+*/
+char *Cmd_Argv (int arg)
+{
+ if ( (unsigned)arg >= cmd_argc )
+ return cmd_null_string;
+ return cmd_argv[arg];
+}
+
+/*
+============
+Cmd_Args
+============
+*/
+char *Cmd_Args (void)
+{
+ return cmd_args;
+}
+
+
+/*
+============
+Cmd_TokenizeString
+
+Parses the given string into command line tokens.
+============
+*/
+void Cmd_TokenizeString (char *text)
+{
+ int i;
+
+// clear the args from the last string
+ for (i=0 ; inext)
+ {
+ if (!Q_strcmp (cmd_name, cmd->name))
+ {
+ Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
+ return;
+ }
+ }
+
+ cmd = Hunk_Alloc (sizeof(cmd_function_t));
+ cmd->name = cmd_name;
+ cmd->function = function;
+ cmd->next = cmd_functions;
+ cmd_functions = cmd;
+}
+
+/*
+============
+Cmd_Exists
+============
+*/
+qboolean Cmd_Exists (char *cmd_name)
+{
+ cmd_function_t *cmd;
+
+ for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+ {
+ if (!Q_strcmp (cmd_name,cmd->name))
+ return true;
+ }
+
+ return false;
+}
+
+
+
+/*
+============
+Cmd_CompleteCommand
+============
+*/
+char *Cmd_CompleteCommand (char *partial)
+{
+ cmd_function_t *cmd;
+ int len;
+
+ len = Q_strlen(partial);
+
+ if (!len)
+ return NULL;
+
+// check functions
+ for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+ if (!Q_strncmp (partial,cmd->name, len))
+ return cmd->name;
+
+ return NULL;
+}
+
+//===================================================================
+
+/*
+============
+Cmd_ExecuteString
+
+A complete command line has been parsed, so try to execute it
+FIXME: lookupnoadd the token to speed search?
+============
+*/
+void Cmd_ExecuteString (char *text, cmd_source_t src)
+{
+ cmd_function_t *cmd;
+ cmdalias_t *a;
+
+ cmd_source = src;
+ Cmd_TokenizeString (text);
+
+// execute the command line
+ if (!Cmd_Argc())
+ return; // no tokens
+
+// check functions
+ for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+ {
+ if (!Q_strcasecmp (cmd_argv[0],cmd->name))
+ {
+ cmd->function ();
+ return;
+ }
+ }
+
+// check alias
+ for (a=cmd_alias ; a ; a=a->next)
+ {
+ if (!Q_strcasecmp (cmd_argv[0], a->name))
+ {
+ Cbuf_InsertText (a->value);
+ return;
+ }
+ }
+
+// check cvars
+ if (!Cvar_Command ())
+ Con_Printf ("Command \"%s\" not found, check QC...\n", Cmd_Argv(0));
+
+}
+
+
+/*
+===================
+Cmd_ForwardToServer
+
+Sends the entire command line over to the server
+===================
+*/
+void Cmd_ForwardToServer (void)
+{
+ if (cls.state != ca_connected)
+ {
+ Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
+ return;
+ }
+
+ if (cls.demoplayback)
+ return; // not really connected
+
+ MSG_WriteByte (&cls.message, clc_stringcmd);
+ if (Q_strcasecmp(Cmd_Argv(0), "cmd") != 0)
+ {
+ SZ_Print (&cls.message, Cmd_Argv(0));
+ SZ_Print (&cls.message, " ");
+ }
+ if (Cmd_Argc() > 1)
+ SZ_Print (&cls.message, Cmd_Args());
+ else
+ SZ_Print (&cls.message, "\n");
+}
+
+
+/*
+================
+Cmd_CheckParm
+
+Returns the position (1 to argc-1) in the command's argument list
+where the given parameter apears, or 0 if not present
+================
+*/
+
+int Cmd_CheckParm (char *parm)
+{
+ int i;
+
+ if (!parm)
+ Sys_Error ("Cmd_CheckParm: NULL");
+
+ for (i = 1; i < Cmd_Argc (); i++)
+ if (! Q_strcasecmp (parm, Cmd_Argv (i)))
+ return i;
+
+ return 0;
+}
diff --git a/source/cmd.h b/source/cmd.h
new file mode 100644
index 0000000..1aa3d8d
--- /dev/null
+++ b/source/cmd.h
@@ -0,0 +1,125 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+// cmd.h -- Command buffer and command execution
+
+//===========================================================================
+
+/*
+
+Any number of commands can be added in a frame, from several different sources.
+Most commands come from either keybindings or console line input, but remote
+servers can also send across commands and entire text files can be execed.
+
+The + command line options are also added to the command buffer.
+
+The game starts with a Cbuf_AddText ("exec nzp.rc\n"); Cbuf_Execute ();
+
+*/
+
+
+void Cbuf_Init (void);
+// allocates an initial text buffer that will grow as needed
+
+void Cbuf_AddText (char *text);
+// as new commands are generated from the console or keybindings,
+// the text is added to the end of the command buffer.
+
+void Cbuf_InsertText (char *text);
+// when a command wants to issue other commands immediately, the text is
+// inserted at the beginning of the buffer, before any remaining unexecuted
+// commands.
+
+void Cbuf_Execute (void);
+// Pulls off \n terminated lines of text from the command buffer and sends
+// them through Cmd_ExecuteString. Stops when the buffer is empty.
+// Normally called once per frame, but may be explicitly invoked.
+// Do not call inside a command function!
+
+//===========================================================================
+
+/*
+
+Command execution takes a null terminated string, breaks it into tokens,
+then searches for a command or variable that matches the first token.
+
+Commands can come from three sources, but the handler functions may choose
+to dissallow the action or forward it to a remote server if the source is
+not apropriate.
+
+*/
+
+typedef void (*xcommand_t) (void);
+
+typedef enum
+{
+ src_client, // came in over a net connection as a clc_stringcmd
+ // host_client will be valid during this state.
+ src_command // from the command buffer
+} cmd_source_t;
+
+extern cmd_source_t cmd_source;
+
+#define MAX_FILELENGTH 64
+
+#define MAXCMDLINE 256
+
+void Cmd_Init (void);
+
+void Cmd_AddCommand (char *cmd_name, xcommand_t function);
+// called by the init functions of other parts of the program to
+// register commands and functions to call for them.
+// The cmd_name is referenced later, so it should not be in temp memory
+
+qboolean Cmd_Exists (char *cmd_name);
+// used by the cvar code to check for cvar / command name overlap
+
+char *Cmd_CompleteCommand (char *partial);
+// attempts to match a partial command for automatic command line completion
+// returns NULL if nothing fits
+
+int Cmd_Argc (void);
+char *Cmd_Argv (int arg);
+char *Cmd_Args (void);
+// The functions that execute commands get their parameters with these
+// functions. Cmd_Argv () will return an empty string, not a NULL
+// if arg > argc, so string operations are allways safe.
+
+int Cmd_CheckParm (char *parm);
+// Returns the position (1 to argc-1) in the command's argument list
+// where the given parameter apears, or 0 if not present
+
+void Cmd_TokenizeString (char *text);
+// Takes a null terminated string. Does not need to be /n terminated.
+// breaks the string up into arg tokens.
+
+void Cmd_ExecuteString (char *text, cmd_source_t src);
+// Parses a single line of text into arguments and tries to execute it.
+// The text can come from the command buffer, a remote client, or stdin.
+
+void Cmd_ForwardToServer (void);
+// adds the current command line as a clc_stringcmd to the client message.
+// things like godmode, noclip, etc, are commands directed to the server,
+// so when they are typed in at the console, they will need to be forwarded.
+
+void Cmd_Print (char *text);
+// used by command functions to send output to either the graphics console or
+// passed as a print message to the client
+
diff --git a/source/common.c b/source/common.c
new file mode 100644
index 0000000..4fa4ab0
--- /dev/null
+++ b/source/common.c
@@ -0,0 +1,2040 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// common.c -- misc functions used in client and server
+
+#include "quakedef.h"
+#include
+
+#define NUM_SAFE_ARGVS 7
+
+static char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
+static char *argvdummy = " ";
+
+static char *safeargvs[NUM_SAFE_ARGVS] =
+ {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"};
+
+qboolean msg_suppress_1 = 0;
+
+void COM_InitFilesystem (void);
+
+char com_token[1024];
+int com_argc;
+char **com_argv;
+
+#define CMDLINE_LENGTH 256
+char com_cmdline[CMDLINE_LENGTH];
+
+qboolean user_maps = true;
+// this graphic needs to be in the pak file to use registered features
+unsigned short pop[] =
+{
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000
+,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000
+,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600
+,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563
+,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564
+,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564
+,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563
+,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500
+,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200
+,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000
+,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000
+};
+
+/*
+
+
+All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources.
+
+The "base directory" is the path to the directory holding the quake.exe and all game directories. The sys_* files pass this to host_init in quakeparms_t->basedir. This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory. The base directory is
+only used during filesystem initialization.
+
+The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to. This can be overridden with the "-game" command line parameter. The game directory can never be changed while quake is executing. This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't.
+
+The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory
+specified, when a file is found by the normal search path, it will be mirrored
+into the cache directory, then opened there.
+
+
+
+FIXME:
+The file "parms.txt" will be read out of the game directory and appended to the current command line arguments to allow different games to initialize startup parms differently. This could be used to add a "-sspeed 22050" for the high quality sound edition. Because they are added at the end, they will not override an explicit setting on the original command line.
+
+*/
+
+//============================================================================
+// ClearLink is used for new headnodes
+void ClearLink (link_t *l)
+{
+ l->prev = l->next = l;
+}
+
+void RemoveLink (link_t *l)
+{
+ l->next->prev = l->prev;
+ l->prev->next = l->next;
+}
+
+void InsertLinkBefore (link_t *l, link_t *before)
+{
+ l->next = before;
+ l->prev = before->prev;
+ l->prev->next = l;
+ l->next->prev = l;
+}
+void InsertLinkAfter (link_t *l, link_t *after)
+{
+ l->next = after->next;
+ l->prev = after;
+ l->prev->next = l;
+ l->next->prev = l;
+}
+
+/*
+============================================================================
+
+ LIBRARY REPLACEMENT FUNCTIONS
+
+============================================================================
+*/
+
+void Q_memset (void *dest, int fill, int count)
+{
+ int i;
+
+ if ( (((long)dest | count) & 3) == 0)
+ {
+ count >>= 2;
+ fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
+ for (i=0 ; i>=2;
+ for (i=0 ; i= '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
+// s1++;
+// s2++;
+ }
+
+ return -1;
+}
+
+int Q_strcasecmp (char *s1, char *s2)
+{
+ return Q_strncasecmp (s1, s2, 99999);
+}
+
+int Q_atoi (char *str)
+{
+ int val;
+ int sign;
+ int c;
+
+ if (*str == '-')
+ {
+ sign = -1;
+ str++;
+ }
+ else
+ sign = 1;
+
+ val = 0;
+
+//
+// check for hex
+//
+ if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
+ {
+ str += 2;
+ while (1)
+ {
+ c = *str++;
+ if (c >= '0' && c <= '9')
+ val = (val<<4) + c - '0';
+ else if (c >= 'a' && c <= 'f')
+ val = (val<<4) + c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ val = (val<<4) + c - 'A' + 10;
+ else
+ return val*sign;
+ }
+ }
+
+//
+// check for character
+//
+ if (str[0] == '\'')
+ {
+ return sign * str[1];
+ }
+
+//
+// assume decimal
+//
+ while (1)
+ {
+ c = *str++;
+ if (c <'0' || c > '9')
+ return val*sign;
+ val = val*10 + c - '0';
+ }
+
+ return 0;
+}
+
+
+float Q_atof (char *str)
+{
+ float val;
+ int sign;
+ int c;
+ int decimal, total;
+
+ if (*str == '-')
+ {
+ sign = -1;
+ str++;
+ }
+ else
+ sign = 1;
+
+ val = 0;
+
+//
+// check for hex
+//
+ if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
+ {
+ str += 2;
+ while (1)
+ {
+ c = *str++;
+ if (c >= '0' && c <= '9')
+ val = (val*16) + c - '0';
+ else if (c >= 'a' && c <= 'f')
+ val = (val*16) + c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ val = (val*16) + c - 'A' + 10;
+ else
+ return val*sign;
+ }
+ }
+
+//
+// check for character
+//
+ if (str[0] == '\'')
+ {
+ return sign * str[1];
+ }
+
+//
+// assume decimal
+//
+ decimal = -1;
+ total = 0;
+ while (1)
+ {
+ c = *str++;
+ if (c == '.')
+ {
+ decimal = total;
+ continue;
+ }
+ if (c <'0' || c > '9')
+ break;
+ val = val*10 + c - '0';
+ total++;
+ }
+
+ if (decimal == -1)
+ return val*sign;
+ while (total > decimal)
+ {
+ val /= 10;
+ total--;
+ }
+
+ return val*sign;
+}
+
+void Q_strncpyz (char *dest, char *src, size_t size)
+{
+ strncpy (dest, src, size - 1);
+ dest[size-1] = 0;
+}
+
+void Q_snprintfz (char *dest, size_t size, char *fmt, ...)
+{
+ va_list argptr;
+
+ va_start (argptr, fmt);
+ vsnprintf (dest, size, fmt, argptr);
+ va_end (argptr);
+
+ dest[size-1] = 0;
+}
+
+/*
+============================================================================
+
+ BYTE ORDER FUNCTIONS
+
+============================================================================
+*/
+
+qboolean bigendien;
+
+short (*BigShort) (short l);
+short (*LittleShort) (short l);
+int (*BigLong) (int l);
+int (*LittleLong) (int l);
+float (*BigFloat) (float l);
+float (*LittleFloat) (float l);
+
+short ShortSwap (short l)
+{
+ byte b1,b2;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+
+ return (b1<<8) + b2;
+}
+
+short ShortNoSwap (short l)
+{
+ return l;
+}
+
+int LongSwap (int l)
+{
+ byte b1,b2,b3,b4;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+ b3 = (l>>16)&255;
+ b4 = (l>>24)&255;
+
+ return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int LongNoSwap (int l)
+{
+ return l;
+}
+
+float FloatSwap (float f)
+{
+ union
+ {
+ float f;
+ byte b[4];
+ } dat1, dat2;
+
+
+ dat1.f = f;
+ dat2.b[0] = dat1.b[3];
+ dat2.b[1] = dat1.b[2];
+ dat2.b[2] = dat1.b[1];
+ dat2.b[3] = dat1.b[0];
+ return dat2.f;
+}
+
+float FloatNoSwap (float f)
+{
+ return f;
+}
+
+/*
+==============================================================================
+
+ MESSAGE IO FUNCTIONS
+
+Handles byte ordering and avoids alignment errors
+==============================================================================
+*/
+
+//
+// writing functions
+//
+
+void MSG_WriteChar (sizebuf_t *sb, int c)
+{
+ byte *buf;
+
+
+ buf = SZ_GetSpace (sb, 1);
+ buf[0] = c;
+}
+
+void MSG_WriteByte (sizebuf_t *sb, int c)
+{
+ byte *buf;
+
+ buf = SZ_GetSpace (sb, 1);
+ buf[0] = c;
+}
+
+void MSG_WriteShort (sizebuf_t *sb, int c)
+{
+ byte *buf;
+
+
+ buf = SZ_GetSpace (sb, 2);
+ buf[0] = c&0xff;
+ buf[1] = c>>8;
+}
+
+void MSG_WriteLong (sizebuf_t *sb, int c)
+{
+ byte *buf;
+
+ buf = SZ_GetSpace (sb, 4);
+ buf[0] = c&0xff;
+ buf[1] = (c>>8)&0xff;
+ buf[2] = (c>>16)&0xff;
+ buf[3] = c>>24;
+}
+
+void MSG_WriteFloat (sizebuf_t *sb, float f)
+{
+ union
+ {
+ float f;
+ int l;
+ } dat;
+
+
+ dat.f = f;
+ dat.l = LittleLong (dat.l);
+
+ SZ_Write (sb, &dat.l, 4);
+}
+
+void MSG_WriteString (sizebuf_t *sb, char *s)
+{
+ if (!s)
+ SZ_Write (sb, "", 1);
+ else
+ SZ_Write (sb, s, Q_strlen(s)+1);
+}
+
+void MSG_WriteCoord (sizebuf_t *sb, float f)
+{
+ MSG_WriteShort (sb, (int)(f*8));
+}
+
+void MSG_WriteAngle (sizebuf_t *sb, float f)
+{
+ MSG_WriteByte (sb, ((int)f*256/360) & 255);
+}
+
+//
+// reading functions
+//
+int msg_readcount;
+qboolean msg_badread;
+
+void MSG_BeginReading (void)
+{
+ msg_readcount = 0;
+ msg_badread = false;
+}
+
+// returns -1 and sets msg_badread if no more characters are available
+int MSG_ReadChar (void)
+{
+ int c;
+
+ if (msg_readcount+1 > net_message.cursize)
+ {
+ msg_badread = true;
+ return -1;
+ }
+
+ c = (signed char)net_message.data[msg_readcount];
+ msg_readcount++;
+
+ return c;
+}
+
+int MSG_ReadByte (void)
+{
+ int c;
+
+ if (msg_readcount+1 > net_message.cursize)
+ {
+ msg_badread = true;
+ return -1;
+ }
+
+ c = (unsigned char)net_message.data[msg_readcount];
+ msg_readcount++;
+
+ return c;
+}
+
+int MSG_ReadShort (void)
+{
+ int c;
+
+ if (msg_readcount+2 > net_message.cursize)
+ {
+ msg_badread = true;
+ return -1;
+ }
+
+ c = (short)(net_message.data[msg_readcount]
+ + (net_message.data[msg_readcount+1]<<8));
+
+ msg_readcount += 2;
+
+ return c;
+}
+
+int MSG_ReadLong (void)
+{
+ int c;
+
+ if (msg_readcount+4 > net_message.cursize)
+ {
+ msg_badread = true;
+ return -1;
+ }
+
+ c = net_message.data[msg_readcount]
+ + (net_message.data[msg_readcount+1]<<8)
+ + (net_message.data[msg_readcount+2]<<16)
+ + (net_message.data[msg_readcount+3]<<24);
+
+ msg_readcount += 4;
+
+ return c;
+}
+
+float MSG_ReadFloat (void)
+{
+ union
+ {
+ byte b[4];
+ float f;
+ int l;
+ } dat;
+
+ dat.b[0] = net_message.data[msg_readcount];
+ dat.b[1] = net_message.data[msg_readcount+1];
+ dat.b[2] = net_message.data[msg_readcount+2];
+ dat.b[3] = net_message.data[msg_readcount+3];
+ msg_readcount += 4;
+
+ dat.l = LittleLong (dat.l);
+
+ return dat.f;
+}
+
+char *MSG_ReadString (void)
+{
+ static char string[2048];
+ int l,c;
+
+ l = 0;
+ do
+ {
+ c = MSG_ReadChar ();
+ if (c == -1 || c == 0)
+ break;
+ string[l] = c;
+ l++;
+ } while (l < sizeof(string)-1);
+
+ string[l] = 0;
+
+ return string;
+}
+
+float MSG_ReadCoord (void)
+{
+ return MSG_ReadShort() * (1.0/8);
+}
+
+float MSG_ReadAngle (void)
+{
+ return MSG_ReadChar() * (360.0/256);
+}
+
+
+
+//===========================================================================
+
+void SZ_Alloc (sizebuf_t *buf, int startsize)
+{
+ if (startsize < 256)
+ startsize = 256;
+ //if (startsize < 1024)
+ // startsize = 1024;
+ buf->data = Hunk_AllocName (startsize, "sizebuf");
+ buf->maxsize = startsize;
+ buf->cursize = 0;
+}
+
+
+void SZ_Free (sizebuf_t *buf)
+{
+// Z_Free (buf->data);
+// buf->data = NULL;
+// buf->maxsize = 0;
+ buf->cursize = 0;
+}
+
+void SZ_Clear (sizebuf_t *buf)
+{
+ buf->cursize = 0;
+ //buf->overflowed = false;
+}
+
+void *SZ_GetSpace (sizebuf_t *buf, int length)
+{
+ void *data;
+
+ if (buf->cursize + length > buf->maxsize)
+ {
+ if (!buf->allowoverflow)
+ Sys_Error ("SZ_GetSpace: overflow without allowoverflow set");
+
+ if (length > buf->maxsize)
+ Sys_Error ("SZ_GetSpace: %i is > full buffer size", length);
+
+ buf->overflowed = true;
+ Con_Printf ("SZ_GetSpace: overflow");
+ SZ_Clear (buf);
+ }
+
+ data = buf->data + buf->cursize;
+ buf->cursize += length;
+
+ return data;
+}
+
+void SZ_Write (sizebuf_t *buf, void *data, int length)
+{
+ Q_memcpy (SZ_GetSpace(buf,length),data,length);
+}
+
+void SZ_Print (sizebuf_t *buf, char *data)
+{
+ int len;
+
+ len = Q_strlen(data)+1;
+
+// byte * cast to keep VC++ happy
+ if (buf->data[buf->cursize-1])
+ Q_memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
+ else
+ Q_memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
+}
+
+
+//============================================================================
+
+
+/*
+============
+COM_SkipPath
+============
+*/
+char *COM_SkipPath (char *pathname)
+{
+ char *last;
+
+ last = pathname;
+ while (*pathname)
+ {
+ if (*pathname=='/')
+ last = pathname+1;
+ pathname++;
+ }
+ return last;
+}
+
+/*
+============
+COM_SkipPathWritable
+============
+*/
+char *COM_SkipPathWritable (char *pathname)
+{
+ char *last;
+ char *p;
+
+ last = p = pathname;
+
+ while (*p)
+ {
+ if (*p == '/' || *p == '\\')
+ last = p + 1;
+
+ p++;
+ }
+
+ return last;
+}
+
+/*
+============
+COM_StripExtension
+============
+*/
+void COM_StripExtension (char *in, char *out)
+{
+ while (*in && *in != '.')
+ *out++ = *in++;
+ *out = 0;
+}
+
+/*
+============
+COM_FileExtension
+============
+*/
+char *COM_FileExtension (char *in)
+{
+ static char exten[8];
+ int i;
+
+ while (*in && *in != '.')
+ in++;
+ if (!*in)
+ return "";
+ in++;
+ for (i=0 ; i<7 && *in ; i++,in++)
+ exten[i] = *in;
+ exten[i] = 0;
+ return exten;
+}
+
+/*
+============
+COM_FileBase
+============
+*/
+void COM_FileBase (char *in, char *out)
+{
+ char *s, *s2;
+
+ s = in + strlen(in) - 1;
+
+ while (s != in && *s != '.') {
+ s--;
+ }
+
+ // naievil -- added (s2 != in) in order to prevent progs.dat from returning a
+ // base name of string before the beginning of the original memory pointer!
+ for (s2 = s ; (s2 != in) && (*s2 != '/') ; s2--)
+ ;
+
+ if (s-s2 < 2)
+ strcpy (out,"?model?");
+ else
+ {
+ s--;
+ strncpy (out,s2+1, s-s2);
+ out[s-s2] = 0;
+ }
+}
+
+
+/*
+==================
+COM_DefaultExtension
+==================
+*/
+void COM_DefaultExtension (char *path, char *extension)
+{
+ char *src;
+//
+// if path doesn't have a .EXT, append extension
+// (extension should include the .)
+//
+ src = path + strlen(path) - 1;
+
+ while (*src != '/' && src != path)
+ {
+ if (*src == '.')
+ return; // it has an extension
+ src--;
+ }
+
+ strcat (path, extension);
+}
+
+
+/*
+==============
+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)
+ return NULL; // end of file;
+ data++;
+ }
+
+// skip // comments
+ if (c=='/' && data[1] == '/')
+ {
+ while (*data && *data != '\n')
+ data++;
+ goto skipwhite;
+ }
+
+
+// handle quoted strings specially
+ if (c == '\"')
+ {
+ data++;
+ while (1)
+ {
+ c = *data++;
+ if (c=='\"' || !c)
+ {
+ com_token[len] = 0;
+ return data;
+ }
+ com_token[len] = c;
+ len++;
+ }
+ }
+
+// parse 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;
+}
+
+
+/*
+==============
+COM_Parse
+
+Parse a token out of a string
+==============
+*/
+char *COM_ParseQ2 (char **data_p)
+{
+ int c;
+ int len;
+ char *data;
+
+ data = *data_p;
+ len = 0;
+ com_token[0] = 0;
+
+ if (!data)
+ {
+ *data_p = NULL;
+ return "";
+ }
+
+// skip whitespace
+skipwhite:
+ while ( (c = *data) <= ' ')
+ {
+ if (c == 0)
+ {
+ *data_p = NULL;
+ return "";
+ }
+ data++;
+ }
+
+// skip // comments
+ if (c=='/' && data[1] == '/')
+ {
+ while (*data && *data != '\n')
+ data++;
+ goto skipwhite;
+ }
+
+// handle quoted strings specially
+ if (c == '\"')
+ {
+ data++;
+ while (1)
+ {
+ c = *data++;
+ if (c=='\"' || !c)
+ {
+ com_token[len] = 0;
+ *data_p = data;
+ return com_token;
+ }
+ if (len < MAX_TOKEN_CHARS)
+ {
+ com_token[len] = c;
+ len++;
+ }
+ }
+ }
+
+// parse a regular word
+ do
+ {
+ if (len < MAX_TOKEN_CHARS)
+ {
+ com_token[len] = c;
+ len++;
+ }
+ data++;
+ c = *data;
+ } while (c>32);
+
+ if (len == MAX_TOKEN_CHARS)
+ {
+// Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS);
+ len = 0;
+ }
+ com_token[len] = 0;
+
+ *data_p = data;
+ return com_token;
+}
+
+/*
+================
+COM_CheckParm
+
+Returns the position (1 to argc-1) in the program's argument list
+where the given parameter apears, or 0 if not present
+================
+*/
+int COM_CheckParm (char *parm)
+{
+ int i;
+
+ for (i=1 ; inext)
+ {
+ if (s->pack)
+ {
+ Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
+ }
+ else
+ Con_Printf ("%s\n", s->filename);
+ }
+}
+
+/*
+============
+COM_WriteFile
+
+The filename will be prefixed by the current game directory
+============
+*/
+void COM_WriteFile (char *filename, void *data, int len)
+{
+ int handle;
+ char name[MAX_OSPATH];
+
+ sprintf (name, "%s/%s", com_gamedir, filename);
+ //Q_snprintfz (name, MAX_OSPATH, "%s/%s", com_gamedir, filename);
+
+ handle = Sys_FileOpenWrite (name);
+ if (handle == -1)
+ {
+ Sys_Printf ("COM_WriteFile: failed on %s\n", name);
+ return;
+ }
+
+ Sys_Printf ("COM_WriteFile: %s\n", name);
+ Sys_FileWrite (handle, data, len);
+ Sys_FileClose (handle);
+}
+
+
+/*
+============
+COM_CreatePath
+
+Only used for CopyFile
+============
+*/
+void COM_CreatePath (char *path)
+{
+ char *ofs;
+
+ for (ofs = path+1 ; *ofs ; ofs++)
+ {
+ if (*ofs == '/')
+ { // create the directory
+ *ofs = 0;
+ Sys_mkdir (path);
+ *ofs = '/';
+ }
+ }
+}
+
+
+/*
+===========
+COM_CopyFile
+
+Copies a file over from the net to the local cache, creating any directories
+needed. This is for the convenience of developers using ISDN from home.
+===========
+*/
+void COM_CopyFile (char *netpath, char *cachepath)
+{
+ int in, out;
+ int remaining, count;
+ char buf[4096];
+
+ remaining = Sys_FileOpenRead (netpath, &in);
+ COM_CreatePath (cachepath); // create directories up to the cache file
+ out = Sys_FileOpenWrite (cachepath);
+
+ while (remaining)
+ {
+ if (remaining < sizeof(buf))
+ count = remaining;
+ else
+ count = sizeof(buf);
+ Sys_FileRead (in, buf, count);
+ Sys_FileWrite (out, buf, count);
+ remaining -= count;
+ }
+
+ Sys_FileClose (in);
+ Sys_FileClose (out);
+}
+
+/*
+===========
+COM_FindFile
+
+Finds the file in the search path.
+Sets com_filesize and one of handle or file
+===========
+*/
+int COM_FindFile (char *filename, int *handle, int *file)
+{
+ searchpath_t *search;
+ char netpath[MAX_OSPATH];
+ char cachepath[MAX_OSPATH];
+ pack_t *pak;
+ int i;
+ int findtime, cachetime;
+
+ if (file && handle)
+ Sys_Error ("COM_FindFile: both handle and file set");
+ if (!file && !handle)
+ Sys_Error ("COM_FindFile: neither handle or file set");
+
+//
+// search through the path, one element at a time
+//
+ search = com_searchpaths;
+
+ for ( ; search ; search = search->next)
+ {
+ // is the element a pak file?
+ if (search->pack)
+ {
+ // look through all the pak file elements
+ pak = search->pack;
+ for (i=0 ; inumfiles ; i++)
+ if (!strcmp (pak->files[i].name, filename))
+ { // found it!
+
+ Sys_Printf ("PackFile: %s : %s\n", pak->filename, filename);
+
+ if (developer.value == 2)
+ Sys_Printf("OpenCustomPack: %s : %s\n", pak->filename, filename);
+
+ if (handle)
+ {
+ *handle = pak->handle;
+ Sys_FileSeek (pak->handle, pak->files[i].filepos);
+ }
+ else
+ { // open a new file on the pakfile
+ Sys_FileOpenRead(pak->filename, file);
+ if ((*file) >= 0)
+ Sys_FileSeek(*file, pak->files[i].filepos);
+ }
+ com_filesize = pak->files[i].filelen;
+ return com_filesize;
+ }
+ }
+ else
+ {
+
+ sprintf (netpath, "%s/%s",search->filename, filename);
+
+ findtime = Sys_FileTime (netpath);
+ if (findtime == -1)
+ continue;
+
+ // see if the file needs to be updated in the cache
+ if (!com_cachedir[0])
+ strcpy (cachepath, netpath);
+ else
+ {
+ sprintf (cachepath,"%s%s", com_cachedir, netpath);
+
+ cachetime = Sys_FileTime (cachepath);
+
+ if (cachetime < findtime)
+ COM_CopyFile (netpath, cachepath);
+ strcpy (netpath, cachepath);
+ }
+
+ Sys_Printf ("FindFile: %s\n",netpath);
+
+ if (developer.value == 2)
+ Sys_Printf ("FindFile: %s\n",netpath);
+
+ com_filesize = Sys_FileOpenRead (netpath, &i);
+ if (handle)
+ *handle = i;
+ else
+ {
+ Sys_FileClose (i);
+ Sys_FileOpenRead(netpath, file);
+ }
+ return com_filesize;
+ }
+
+ }
+ Sys_Printf ("FindFile: can't find %s\n", filename);
+
+ if (developer.value == 2)
+ Con_DPrintf ("FindFile: can't find %s\n", filename);
+
+ if (handle)
+ *handle = -1;
+ else
+ *file = -1;
+ com_filesize = -1;
+ return -1;
+}
+
+/*
+===========
+COM_OpenFile
+
+filename never has a leading slash, but may contain directory walks
+returns a handle and a length
+it may actually be inside a pak file
+===========
+*/
+int COM_OpenFile (char *filename, int *handle)
+{
+ return COM_FindFile (filename, handle, NULL);
+}
+
+/*
+===========
+COM_FOpenFile
+
+If the requested file is inside a packfile, a new file will be opened
+into the file.
+===========
+*/
+int COM_FOpenFile (char *filename, int *file)
+{
+ return COM_FindFile (filename, NULL, file);
+}
+
+/*
+=================
+FS_FOpenFile
+
+Finds the file in the search path.
+Sets com_filesize and one of handle or file
+=================
+*/
+
+int COM_filelength (FILE *f)
+{
+ int pos, end;
+
+ pos = ftell (f);
+ fseek (f, 0, SEEK_END);
+ end = ftell (f);
+ fseek (f, pos, SEEK_SET);
+
+ return end;
+}
+
+int FS_FOpenFile (char *filename, FILE **file)
+{
+ searchpath_t *search;
+ pack_t *pak;
+ int i;
+
+ *file = NULL;
+
+ com_filesize = -1;
+ com_netpath[0] = 0;
+
+// search through the path, one element at a time
+ for (search = com_searchpaths ; search ; search = search->next)
+ {
+ // is the element a pak file?
+ if (search->pack)
+ {
+ // look through all the pak file elements
+ pak = search->pack;
+ for (i=0 ; inumfiles ; i++)
+ {
+ if (!strcmp(pak->files[i].name, filename)) // found it!
+ {
+ if (developer.value)
+ Sys_Printf ("PackFile: %s : %s\n", pak->filename, filename);
+ // open a new file on the pakfile
+ if (!(*file = fopen(pak->filename, "rb")))
+ Sys_Error ("Couldn't reopen %s", pak->filename);
+ fseek (*file, pak->files[i].filepos, SEEK_SET);
+ com_filesize = pak->files[i].filelen;
+
+ Q_snprintfz (com_netpath, sizeof(com_netpath), "%s#%i", pak->filename, i);
+ return com_filesize;
+ }
+ }
+ }
+ else
+ {
+ // check a file in the directory tree
+ Q_snprintfz (com_netpath, sizeof(com_netpath), "%s/%s", search->filename, filename);
+
+ if (!(*file = fopen(com_netpath, "rb")))
+ continue;
+
+ if (developer.value)
+ Sys_Printf ("FOpenFile: %s\n", com_netpath);
+
+ com_filesize = COM_filelength (*file);
+ return com_filesize;
+ }
+
+ }
+
+ if (developer.value == 2)
+ Con_Printf ("COM_FOpenFile: can't find %s\n", filename);
+
+ *file = NULL;
+ com_filesize = -1;
+
+ return -1;
+}
+
+/*
+=================
+FS_FindFile
+
+finds files in given path including inside paks as well
+=================
+*/
+qboolean FS_FindFile (char *filename)
+{
+ searchpath_t *search;
+ char netpath[MAX_OSPATH];
+ pack_t *pak;
+ int i;
+
+ for (search = com_searchpaths ; search ; search = search->next)
+ {
+ if (search->pack)
+ {
+ pak = search->pack;
+ for (i=0 ; inumfiles ; i++)
+ if (!strcmp(pak->files[i].name, filename))
+ return true;
+ }
+ else
+ {
+ Q_snprintfz (netpath, sizeof(com_netpath), "%s/%s", search->filename, filename);
+ if (Sys_FileTime(netpath) != -1)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+============
+COM_CloseFile
+
+If it is a pak file handle, don't really close it
+============
+*/
+void COM_CloseFile (int h)
+{
+ searchpath_t *s;
+
+ for (s = com_searchpaths ; s ; s=s->next)
+ if (s->pack && s->pack->handle == h)
+ return;
+
+ Sys_FileClose (h);
+}
+
+/*
+============
+COM_LoadFile
+
+Filename are reletive to the quake directory.
+Allways appends a 0 byte.
+============
+*/
+cache_user_t *loadcache;
+byte *loadbuf;
+int loadsize;
+byte *COM_LoadFile (char *path, int usehunk)
+{
+ int h;
+ byte *buf;
+ char base[32];
+ int len;
+
+ buf = NULL; // quiet compiler warning
+
+// look for it in the filesystem or pack files
+ len = COM_OpenFile (path, &h);
+ if (h == -1)
+ return NULL;
+
+// extract the filename base name for hunk tag
+ COM_FileBase (path, base);
+
+ if (usehunk == 1)
+ buf = Hunk_AllocName (len+1, base);
+ else if (usehunk == 2)
+ buf = Hunk_TempAlloc (len+1);
+ else if (usehunk == 0)
+ buf = Z_Malloc (len+1);
+ else if (usehunk == 3)
+ buf = Cache_Alloc (loadcache, len+1, base);
+ else if (usehunk == 4)
+ {
+ if (len+1 > loadsize)
+ buf = Hunk_TempAlloc (len+1);
+ else
+ buf = loadbuf;
+ }
+ else {
+ Sys_Error ("COM_LoadFile: bad usehunk");
+ }
+
+ if (!buf) {
+ Sys_Error ("COM_LoadFile: not enough space for %s", path);
+ }
+
+ ((byte *)buf)[len] = 0;
+
+ Sys_FileRead (h, buf, len);
+
+ COM_CloseFile (h);
+
+ return buf;
+}
+
+byte *COM_LoadHunkFile (char *path)
+{
+ return COM_LoadFile (path, 1);
+}
+
+byte *COM_LoadTempFile (char *path)
+{
+ return COM_LoadFile (path, 2);
+}
+
+void COM_LoadCacheFile (char *path, struct cache_user_s *cu)
+{
+ loadcache = cu;
+ COM_LoadFile (path, 3);
+}
+
+// uses temp hunk if larger than bufsize
+byte *COM_LoadStackFile (char *path, void *buffer, int bufsize)
+{
+ byte *buf;
+
+ loadbuf = (byte *)buffer;
+ loadsize = bufsize;
+ buf = COM_LoadFile (path, 4);
+
+ return buf;
+}
+
+/*
+=================
+COM_LoadPackFile
+
+Takes an explicit (not game tree related) path to a pak file.
+
+Loads the header and directory, adding the files at the beginning
+of the list so they override previous pack files.
+=================
+*/
+pack_t *COM_LoadPackFile (char *packfile)
+{
+ dpackheader_t header;
+ int i;
+ packfile_t *newfiles;
+ int numpackfiles;
+ pack_t *pack;
+ int packhandle;
+ dpackfile_t *info;//[MAX_FILES_IN_PACK];
+ unsigned short crc;
+
+ info = malloc(sizeof(dpackfile_t) * MAX_FILES_IN_PACK);
+
+ if (Sys_FileOpenRead (packfile, &packhandle) == -1)
+ {
+// Con_Printf ("Couldn't open %s\n", packfile);
+ free(info);
+ return NULL;
+ }
+ Sys_FileRead (packhandle, (void *)&header, sizeof(header));
+ if (header.id[0] != 'P' || header.id[1] != 'A'
+ || header.id[2] != 'C' || header.id[3] != 'K')
+ Sys_Error ("%s is not a packfile", packfile);
+ header.dirofs = LittleLong (header.dirofs);
+ header.dirlen = LittleLong (header.dirlen);
+
+ numpackfiles = header.dirlen / sizeof(dpackfile_t);
+
+ if (numpackfiles > MAX_FILES_IN_PACK)
+ {
+ free(info);
+ Sys_Error ("%s has %i files", packfile, numpackfiles);
+ }
+
+ newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "packfile");
+
+ Sys_FileSeek (packhandle, header.dirofs);
+ Sys_FileRead (packhandle, (void *)info, header.dirlen);
+
+// crc the directory to check for modifications
+ CRC_Init (&crc);
+ for (i=0 ; ifilename, packfile);
+ pack->handle = packhandle;
+ pack->numfiles = numpackfiles;
+ pack->files = newfiles;
+ free(info);
+ Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
+ return pack;
+}
+
+
+/*
+int COM_FileOpenRead (char *path, FILE **hndl)
+{
+ FILE *f;
+
+ if (!(f = fopen(path, "rb"))) {
+ *hndl = NULL;
+ return -1;
+ }
+ *hndl = f;
+
+ return COM_filelength(f);
+}
+
+
+Takes an explicit (not game tree related) path to a pak file.
+
+Loads the header and directory, adding the files at the beginning
+of the list so they override previous pack files.
+
+pack_t *COM_LoadPackFile (char *packfile)
+{
+ dpackheader_t header;
+ int i;
+ packfile_t *newfiles;
+ pack_t *pack;
+ FILE *packhandle;
+ dpackfile_t *info;
+
+ if (COM_FileOpenRead (packfile, &packhandle) == -1)
+ return NULL;
+
+ fread (&header, 1, sizeof(header), packhandle);
+ if (header.id[0] != 'P' || header.id[1] != 'A' || header.id[2] != 'C' || header.id[3] != 'K')
+ Sys_Error ("%s is not a packfile", packfile);
+ header.dirofs = LittleLong (header.dirofs);
+ header.dirlen = LittleLong (header.dirlen);
+
+ pack = malloc (sizeof (pack_t));
+ strcpy (pack->filename, packfile);
+ pack->handle = packhandle;
+ pack->numfiles = header.dirlen / sizeof(dpackfile_t);
+
+ pack->files = newfiles = malloc (pack->numfiles * sizeof(packfile_t));
+ info = malloc (header.dirlen);
+
+ fseek (packhandle, header.dirofs, SEEK_SET);
+ fread (info, 1, header.dirlen, packhandle);
+
+ // parse the directory
+ for (i = 0; i < pack->numfiles; i++) {
+ strcpy (newfiles[i].name, info[i].name);
+ newfiles[i].filepos = LittleLong(info[i].filepos);
+ newfiles[i].filelen = LittleLong(info[i].filelen);
+ }
+
+ free(info);
+ return pack;
+}
+*/
+
+/*
+================
+COM_AddGameDirectory
+
+Sets com_gamedir, adds the directory to the head of the path,
+then loads and adds pak1.pak pak2.pak ...
+================
+*/
+void COM_AddGameDirectory (char *dir)
+{
+ int i;
+ searchpath_t *search;
+ pack_t *pak;
+ char pakfile[MAX_OSPATH];
+
+ strcpy (com_gamedir, dir);
+//
+// add the directory to the search path
+//
+ search = Hunk_Alloc (sizeof(searchpath_t));
+ strcpy (search->filename, dir);
+ search->next = com_searchpaths;
+ com_searchpaths = search;
+
+//
+// add any pak files in the format pak0.pak pak1.pak, ...
+//
+ for (i=0 ; ; i++)
+ {
+ sprintf (pakfile, "%s/pak%i.pak", dir, i);
+ pak = COM_LoadPackFile (pakfile);
+ if (!pak)
+ break;
+ search = Hunk_Alloc (sizeof(searchpath_t));
+ search->pack = pak;
+ search->next = com_searchpaths;
+ com_searchpaths = search;
+ }
+
+//
+// add the contents of the parms.txt file to the end of the command line
+//
+
+}
+
+/*
+================
+COM_InitFilesystem
+================
+*/
+void COM_InitFilesystem (void)
+{
+ int i, j;
+ char basedir[MAX_OSPATH];
+ searchpath_t *search;
+
+//
+// -basedir
+// Overrides the system supplied base directory (under GAMENAME)
+//
+ i = COM_CheckParm ("-basedir");
+ if (i && i < com_argc-1)
+ strcpy (basedir, com_argv[i+1]);
+ else
+ strcpy (basedir, host_parms.basedir);
+
+ j = strlen (basedir);
+
+ if (j > 0)
+ {
+ if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
+ basedir[j-1] = 0;
+ }
+
+//
+// -cachedir
+// Overrides the system supplied cache directory (NULL or /qcache)
+// -cachedir - will disable caching.
+//
+ i = COM_CheckParm ("-cachedir");
+ if (i && i < com_argc-1)
+ {
+ if (com_argv[i+1][0] == '-')
+ com_cachedir[0] = 0;
+ else
+ strcpy (com_cachedir, com_argv[i+1]);
+ }
+ else if (host_parms.cachedir)
+ strcpy (com_cachedir, host_parms.cachedir);
+ else
+ com_cachedir[0] = 0;
+
+//
+// start up with GAMENAME by default (nzp) and DQDIRNAME for DQ extentions. Crow_bar
+//
+ COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
+
+//
+// -game
+// Adds basedir/gamedir as an override game
+//
+ i = COM_CheckParm ("-game");
+ if (i && i < com_argc-1)
+ {
+ COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
+ }
+
+//
+// -path [] ...
+// Fully specifies the exact serach path, overriding the generated one
+//
+ i = COM_CheckParm ("-path");
+ if (i)
+ {
+ com_searchpaths = NULL;
+ while (++i < com_argc)
+ {
+ if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
+ break;
+
+ search = Hunk_Alloc (sizeof(searchpath_t));
+ if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
+ {
+ search->pack = COM_LoadPackFile (com_argv[i]);
+ if (!search->pack)
+ Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
+ }
+ else
+ strcpy (search->filename, com_argv[i]);
+ search->next = com_searchpaths;
+ com_searchpaths = search;
+ }
+ }
+}
+
+
+
diff --git a/source/common.h b/source/common.h
new file mode 100644
index 0000000..2a13989
--- /dev/null
+++ b/source/common.h
@@ -0,0 +1,203 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// comndef.h -- general definitions
+
+#if !defined BYTE_DEFINED
+typedef unsigned char byte;
+#define BYTE_DEFINED 1
+#endif
+
+#undef true
+#undef false
+
+#ifdef __cplusplus
+typedef enum {qfalse, qtrue} qboolean;
+#else
+typedef enum {false, true} qboolean;
+#endif
+
+#ifndef fmin
+#define fmin(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef fmax
+#define fmax(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define bound(a, b, c) ((a) >= (c) ? (a) : (b) < (a) ? (a) : (b) > (c) ? (c) : (b))
+
+//============================================================================
+
+typedef struct sizebuf_s
+{
+ qboolean allowoverflow; // if false, do a Sys_Error
+ qboolean overflowed; // set to true if the buffer size failed
+ byte *data;
+ int maxsize;
+ int cursize;
+} sizebuf_t;
+
+void SZ_Alloc (sizebuf_t *buf, int startsize);
+void SZ_Free (sizebuf_t *buf);
+void SZ_Clear (sizebuf_t *buf);
+void *SZ_GetSpace (sizebuf_t *buf, int length);
+void SZ_Write (sizebuf_t *buf, void *data, int length);
+void SZ_Print (sizebuf_t *buf, char *data); // strcats onto the sizebuf
+
+//============================================================================
+
+typedef struct link_s
+{
+ struct link_s *prev, *next;
+} link_t;
+
+
+void ClearLink (link_t *l);
+void RemoveLink (link_t *l);
+void InsertLinkBefore (link_t *l, link_t *before);
+void InsertLinkAfter (link_t *l, link_t *after);
+
+// (type *)STRUCT_FROM_LINK(link_t *link, type, member)
+// ent = STRUCT_FROM_LINK(link,entity_t,order)
+// FIXME: remove this mess!
+#define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m)))
+
+//============================================================================
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#define Q_MAXCHAR ((char)0x7f)
+#define Q_MAXSHORT ((short)0x7fff)
+#define Q_MAXINT ((int)0x7fffffff)
+#define Q_MAXLONG ((int)0x7fffffff)
+#define Q_MAXFLOAT ((int)0x7fffffff)
+
+#define Q_MINCHAR ((char)0x80)
+#define Q_MINSHORT ((short)0x8000)
+#define Q_MININT ((int)0x80000000)
+#define Q_MINLONG ((int)0x80000000)
+#define Q_MINFLOAT ((int)0x7fffffff)
+
+
+
+#define MAX_TOKEN_CHARS 128 // max length of an individual token
+
+//============================================================================
+
+extern qboolean bigendien;
+
+extern short (*BigShort) (short l);
+extern short (*LittleShort) (short l);
+extern int (*BigLong) (int l);
+extern int (*LittleLong) (int l);
+extern float (*BigFloat) (float l);
+extern float (*LittleFloat) (float l);
+
+//============================================================================
+
+void MSG_WriteChar (sizebuf_t *sb, int c);
+void MSG_WriteByte (sizebuf_t *sb, int c);
+void MSG_WriteShort (sizebuf_t *sb, int c);
+void MSG_WriteLong (sizebuf_t *sb, int c);
+void MSG_WriteFloat (sizebuf_t *sb, float f);
+void MSG_WriteString (sizebuf_t *sb, char *s);
+void MSG_WriteCoord (sizebuf_t *sb, float f);
+void MSG_WriteAngle (sizebuf_t *sb, float f);
+
+extern int msg_readcount;
+extern qboolean msg_badread; // set if a read goes beyond end of message
+
+void MSG_BeginReading (void);
+int MSG_ReadChar (void);
+int MSG_ReadByte (void);
+int MSG_ReadShort (void);
+int MSG_ReadLong (void);
+float MSG_ReadFloat (void);
+char *MSG_ReadString (void);
+
+float MSG_ReadCoord (void);
+float MSG_ReadAngle (void);
+
+//============================================================================
+void Q_strncpyz (char *dest, char *src, size_t size);
+void Q_snprintfz (char *dest, size_t size, char *fmt, ...);
+void Q_memset (void *dest, int fill, int count);
+void Q_memcpy (void *dest, void *src, int count);
+int Q_memcmp (void *m1, void *m2, int count);
+void Q_strcpy (char *dest, char *src);
+void Q_strncpy (char *dest, char *src, int count);
+int Q_strlen (char *str);
+char *Q_strrchr (char *s, char c);
+void Q_strcat (char *dest, char *src);
+int Q_strcmp (char *s1, char *s2);
+int Q_strncmp (char *s1, char *s2, int count);
+int Q_strcasecmp (char *s1, char *s2);
+int Q_strncasecmp (char *s1, char *s2, int n);
+int Q_atoi (char *str);
+float Q_atof (char *str);
+
+extern char com_token[1024];
+extern qboolean com_eof;
+
+char *COM_Parse (char *data);
+char *COM_ParseQ2 (char **data_p);
+
+extern int com_argc;
+extern char **com_argv;
+
+int COM_CheckParm (char *parm);
+void COM_Init (char *path);
+void COM_InitArgv (int argc, char **argv);
+
+char *COM_SkipPath (char *pathname);
+char *COM_SkipPathWritable (char *pathname);
+void COM_StripExtension (char *in, char *out);
+void COM_FileBase (char *in, char *out);
+void COM_DefaultExtension (char *path, char *extension);
+
+char *va(char *format, ...);
+char *CopyString (char *in);
+// does a varargs printf into a temp buffer
+
+extern int com_filesize;
+struct cache_user_s;
+
+extern char com_gamedir[MAX_OSPATH];
+
+void COM_WriteFile (char *filename, void *data, int len);
+int COM_OpenFile (char *filename, int *hndl);
+int COM_FOpenFile (char *filename, int *file);
+void COM_CloseFile (int h);
+void COM_CreatePath (char *path);
+char *COM_FileExtension (char *in);
+byte *COM_LoadStackFile (char *path, void *buffer, int bufsize);
+byte *COM_LoadTempFile (char *path);
+byte *COM_LoadHunkFile (char *path);
+byte *COM_LoadFile (char *path, int usehunk);
+void COM_LoadCacheFile (char *path, struct cache_user_s *cu);
+//============================================================================
+qboolean FS_FindFile (char *filename);
+int FS_FOpenFile (char *filename, FILE **file);
+extern char com_netpath[MAX_OSPATH];
+//============================================================================
+
+extern qboolean user_maps;
diff --git a/source/console.c b/source/console.c
new file mode 100644
index 0000000..72a75c6
--- /dev/null
+++ b/source/console.c
@@ -0,0 +1,679 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// console.c
+
+#ifdef NeXT
+#include
+#endif
+#ifndef _MSC_VER
+#include
+#endif
+#include
+#include "quakedef.h"
+
+int con_linewidth;
+
+float con_cursorspeed = 4;
+
+#define CON_TEXTSIZE 16384
+
+qboolean con_forcedup; // because no entities to refresh
+
+int con_totallines; // total lines in console scrollback
+int con_backscroll; // lines up from bottom to display
+int con_current; // where next message will be printed
+int con_x; // offset in current line for next print
+char *con_text=0;
+
+cvar_t con_notifytime = {"con_notifytime","3"}; //seconds
+
+#define NUM_CON_TIMES 4
+float con_times[NUM_CON_TIMES]; // realtime time the line was generated
+ // for transparent notify lines
+
+int con_vislines;
+
+qboolean con_debuglog;
+
+#define MAXCMDLINE 256
+extern char key_lines[32][MAXCMDLINE];
+extern int edit_line;
+extern int key_linepos;
+
+
+qboolean con_initialized;
+
+int con_notifylines; // scan lines to clear for notify lines
+
+extern void M_Menu_Main_f (void);
+
+void M_OSK_Draw (void);
+void Con_OSK_f (char *input, char *output, int outlen);
+void Con_OSK_Key(int key);
+void Con_DrawOSK(void);
+extern qboolean console_enabled;
+/*
+================
+Con_ToggleConsole_f
+================
+*/
+void Con_ToggleConsole_f (void)
+{
+ if (key_dest == key_console)
+ {
+ if (cls.state == ca_connected)
+ {
+ key_dest = key_game;
+ console_enabled = false;
+ key_lines[edit_line][1] = 0; // clear any typing
+ key_linepos = 1;
+ }
+ else
+ {
+ console_enabled = false;
+ M_Menu_Main_f ();
+ }
+ }
+ else
+ key_dest = key_console;
+
+ SCR_EndLoadingPlaque ();
+ memset (con_times, 0, sizeof(con_times));
+}
+
+/*
+================
+Con_Clear_f
+================
+*/
+void Con_Clear_f (void)
+{
+ if (con_text)
+ Q_memset (con_text, ' ', CON_TEXTSIZE);
+}
+
+
+/*
+================
+Con_ClearNotify
+================
+*/
+void Con_ClearNotify (void)
+{
+ int i;
+
+ for (i=0 ; i> 3) - 2;
+
+ if (width == con_linewidth)
+ return;
+
+ if (width < 1) // video hasn't been initialized yet
+ {
+ width = 38;
+ con_linewidth = width;
+ con_totallines = CON_TEXTSIZE / con_linewidth;
+ Q_memset (con_text, ' ', CON_TEXTSIZE);
+ }
+ else
+ {
+ oldwidth = con_linewidth;
+ con_linewidth = width;
+ oldtotallines = con_totallines;
+ con_totallines = CON_TEXTSIZE / con_linewidth;
+ numlines = oldtotallines;
+
+ if (con_totallines < numlines)
+ numlines = con_totallines;
+
+ numchars = oldwidth;
+
+ if (con_linewidth < numchars)
+ numchars = con_linewidth;
+
+ Q_memcpy (tbuf, con_text, CON_TEXTSIZE);
+ Q_memset (con_text, ' ', CON_TEXTSIZE);
+
+ for (i=0 ; i con_linewidth) )
+ con_x = 0;
+
+ txt++;
+
+ if (cr)
+ {
+ con_current--;
+ cr = false;
+ }
+
+
+ if (!con_x)
+ {
+ Con_Linefeed ();
+ // mark time for transparent overlay
+ if (con_current >= 0)
+ con_times[con_current % NUM_CON_TIMES] = realtime;
+ }
+
+ switch (c)
+ {
+ case '\n':
+ con_x = 0;
+ break;
+
+ case '\r':
+ con_x = 0;
+ cr = 1;
+ break;
+
+ default: // display character and advance
+ y = con_current % con_totallines;
+ con_text[y*con_linewidth+con_x] = c | mask;
+ con_x++;
+ if (con_x >= con_linewidth)
+ con_x = 0;
+ break;
+ }
+
+ }
+}
+
+
+/*
+================
+Con_DebugLog
+================
+*/
+void Con_DebugLog(char *file, char *fmt, ...)
+{
+ va_list argptr;
+ static char data[1024];
+ int fd;
+
+ va_start(argptr, fmt);
+ vsprintf(data, fmt, argptr);
+ va_end(argptr);
+ fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
+ write(fd, data, strlen(data));
+ close(fd);
+}
+
+
+/*
+================
+Con_Printf
+
+Handles cursor positioning, line wrapping, etc
+================
+*/
+#define MAXPRINTMSG 4096
+// FIXME: make a buffer size safe vsprintf?
+void Con_Printf (char *fmt, ...)
+{
+ va_list argptr;
+ char msg[MAXPRINTMSG];
+ static qboolean inupdate;
+
+ va_start (argptr,fmt);
+ vsprintf (msg,fmt,argptr);
+ va_end (argptr);
+
+// also echo to debugging console
+ Sys_Printf ("%s", msg); // also echo to debugging console
+
+// log all messages to file
+ if (con_debuglog)
+ Con_DebugLog(va("%s/condebug.log",com_gamedir), "%s", msg);
+
+ if (!con_initialized)
+ return;
+
+ if (cls.state == ca_dedicated)
+ return; // no graphics mode
+
+// write it to the scrollable buffer
+ Con_Print (msg);
+
+// update the screen if the console is displayed
+ if (cls.signon != SIGNONS && !scr_disabled_for_loading )
+ {
+ // protect against infinite loop if something in SCR_UpdateScreen calls
+ // Con_Printd
+ if (!inupdate)
+ {
+ inupdate = true;
+ SCR_UpdateScreen ();
+ inupdate = false;
+ }
+ }
+}
+
+/*
+================
+Con_DPrintf
+
+A Con_Printf that only shows up if the "developer" cvar is set
+================
+*/
+void Con_DPrintf (char *fmt, ...)
+{
+ va_list argptr;
+ char msg[MAXPRINTMSG];
+
+ if (!developer.value)
+ return; // don't confuse non-developers with techie stuff...
+
+ va_start (argptr,fmt);
+ vsprintf (msg,fmt,argptr);
+ va_end (argptr);
+
+ Con_Printf ("%s", msg);
+}
+
+
+/*
+==================
+Con_SafePrintf
+
+Okay to call even when the screen can't be updated
+==================
+*/
+void Con_SafePrintf (char *fmt, ...)
+{
+ va_list argptr;
+ char msg[1024];
+ int temp;
+
+ va_start (argptr,fmt);
+ vsprintf (msg,fmt,argptr);
+ va_end (argptr);
+
+ temp = scr_disabled_for_loading;
+ scr_disabled_for_loading = true;
+ Con_Printf ("%s", msg);
+ scr_disabled_for_loading = temp;
+}
+
+
+/*
+==============================================================================
+
+DRAWING
+
+==============================================================================
+*/
+
+
+/*
+================
+Con_DrawInput
+
+The input line scrolls horizontally if typing goes beyond the right edge
+================
+*/
+void Con_DrawInput (void)
+{
+ int y;
+ int i;
+ char *text;
+
+ if (key_dest != key_console && !con_forcedup)
+ return; // don't draw anything
+
+ text = key_lines[edit_line];
+
+// add the cursor frame
+ text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1);
+
+// fill out remainder with spaces
+ for (i=key_linepos+1 ; i< con_linewidth ; i++)
+ text[i] = ' ';
+
+// prestep if horizontally scrolling
+ if (key_linepos >= con_linewidth)
+ text += 1 + key_linepos - con_linewidth;
+
+// draw it
+ y = con_vislines-16;
+
+ for (i=0 ; i con_notifytime.value)
+ continue;
+ text = con_text + (i % con_totallines)*con_linewidth;
+
+ clearnotify = 0;
+ scr_copytop = 1;
+
+ for (x = 0 ; x < con_linewidth ; x++)
+ Draw_Character ( (x+1)<<3, v, text[x]);
+
+ v += 8;
+ }
+
+
+ if (key_dest == key_message)
+ {
+ clearnotify = 0;
+ scr_copytop = 1;
+
+ x = 0;
+
+ Draw_String (8, v, "say:");
+ while(chat_buffer[x])
+ {
+ Draw_Character ( (x+5)<<3, v, chat_buffer[x]);
+ x++;
+ }
+ Draw_Character ( (x+5)<<3, v, 10+((int)(realtime*con_cursorspeed)&1));
+ v += 8;
+ }
+
+ if (v > con_notifylines)
+ con_notifylines = v;
+}
+
+/*
+================
+Con_DrawConsole
+
+Draws the console with the solid background
+The typing input line at the bottom should only be drawn if typing is allowed
+================
+*/
+qboolean console_enabled;
+void Con_DrawConsole (int lines, qboolean drawinput)
+{
+ int i, x, y;
+ int rows;
+ char *text;
+ int j;
+
+ if (lines <= 0)
+ return;
+
+// draw the background
+ Draw_ConsoleBackground (lines);
+ if (!console_enabled && !developer.value)
+ return;
+
+// draw the text
+ con_vislines = lines;
+
+ rows = (lines-16)>>3; // rows of text to draw
+ y = lines - 16 - (rows<<3); // may start slightly negative
+
+ for (i= con_current - rows + 1 ; i<=con_current ; i++, y+=8 )
+ {
+ j = i - con_backscroll;
+ if (j<0)
+ j = 0;
+ text = con_text + (j % con_totallines)*con_linewidth;
+
+ for (x=0 ; x> 8) ^ data];
+}
+
+unsigned short CRC_Value(unsigned short crcvalue)
+{
+ return crcvalue ^ CRC_XOR_VALUE;
+}
diff --git a/source/crc.h b/source/crc.h
new file mode 100644
index 0000000..cad9772
--- /dev/null
+++ b/source/crc.h
@@ -0,0 +1,24 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+/* crc.h */
+
+void CRC_Init(unsigned short *crcvalue);
+void CRC_ProcessByte(unsigned short *crcvalue, byte data);
+unsigned short CRC_Value(unsigned short crcvalue);
diff --git a/source/crypter.c b/source/crypter.c
new file mode 100644
index 0000000..68a8bb2
--- /dev/null
+++ b/source/crypter.c
@@ -0,0 +1,65 @@
+/*
+ DATA Decrypt
+*/
+#include
+#include
+char rotate(char c, int key)
+{
+ int l = 'Z' - 'A';
+
+ c += key % l;
+
+ if(c < 'A')
+ c += l;
+
+ if(c > 'Z')
+ c -= l;
+
+ return c;
+}
+
+char encrypt(char c, int key)
+{
+ if(c >= 'a' && c <= 'z')
+ c = toupper(c);
+
+ if(c >= 'A' && c <= 'Z')
+ c = rotate(c, key);
+
+ return c;
+}
+
+char decrypt(char c, int key)
+{
+
+ if(c < 'A' || c > 'Z')
+ return c;
+ else
+ return rotate(c, key);
+
+}
+
+char *strencrypt(char *s, int key, int len)
+{
+ int i;
+ char *result = malloc(len);
+ for(i = 0; i < len; i++)
+ {
+ result[i] = encrypt(s[i], key);
+
+ }
+ return result;
+
+}
+
+char *strdecrypt(char *s, int key, int len)
+{
+ int i;
+ char *result = malloc(len);
+ for(i = 0; i < len; i++)
+ {
+ result[i] = decrypt(s[i], -key);
+ }
+ return result;
+}
+
diff --git a/source/crypter.h b/source/crypter.h
new file mode 100644
index 0000000..55fc911
--- /dev/null
+++ b/source/crypter.h
@@ -0,0 +1,12 @@
+/*
+ RSA DATA Decrypt
+*/
+
+#ifndef RSA_H
+#define RSA_H
+
+char *strencrypt(char *s, int key, int len);
+char *strdecrypt(char *s, int key, int len);
+
+#endif
+
diff --git a/source/cvar.c b/source/cvar.c
new file mode 100644
index 0000000..293b0c0
--- /dev/null
+++ b/source/cvar.c
@@ -0,0 +1,223 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// cvar.c -- dynamic variable tracking
+
+#include "quakedef.h"
+
+cvar_t *cvar_vars;
+char *cvar_null_string = "";
+
+/*
+============
+Cvar_FindVar
+============
+*/
+cvar_t *Cvar_FindVar (char *var_name)
+{
+ cvar_t *var;
+
+ for (var=cvar_vars ; var ; var=var->next)
+ if (!Q_strcmp (var_name, var->name))
+ return var;
+
+ return NULL;
+}
+
+/*
+============
+Cvar_VariableValue
+============
+*/
+float Cvar_VariableValue (char *var_name)
+{
+ cvar_t *var;
+
+ var = Cvar_FindVar (var_name);
+ if (!var)
+ return 0;
+ return Q_atof (var->string);
+}
+
+
+/*
+============
+Cvar_VariableString
+============
+*/
+char *Cvar_VariableString (char *var_name)
+{
+ cvar_t *var;
+
+ var = Cvar_FindVar (var_name);
+ if (!var)
+ return cvar_null_string;
+ return var->string;
+}
+
+
+/*
+============
+Cvar_CompleteVariable
+============
+*/
+char *Cvar_CompleteVariable (char *partial)
+{
+ cvar_t *cvar;
+ int len;
+
+ len = Q_strlen(partial);
+
+ if (!len)
+ return NULL;
+
+// check functions
+ for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
+ if (!Q_strncmp (partial,cvar->name, len))
+ return cvar->name;
+
+ return NULL;
+}
+
+
+/*
+============
+Cvar_Set
+============
+*/
+void Cvar_Set (char *var_name, char *value)
+{
+ cvar_t *var;
+ qboolean changed;
+
+ var = Cvar_FindVar (var_name);
+ if (!var)
+ { // there is an error in C code if this happens
+ Con_Printf ("Cvar_Set: variable %s not found\n", var_name);
+ return;
+ }
+
+ changed = Q_strcmp(var->string, value);
+
+ Z_Free (var->string); // free the old value string
+
+ var->string = Z_Malloc (Q_strlen(value)+1);
+ Q_strcpy (var->string, value);
+ var->value = Q_atof (var->string);
+ if (var->server && changed)
+ {
+ if (sv.active)
+ SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string);
+ }
+}
+
+/*
+============
+Cvar_SetValue
+============
+*/
+void Cvar_SetValue (char *var_name, float value)
+{
+ char val[32];
+
+ sprintf (val, "%f",value);
+ Cvar_Set (var_name, val);
+}
+
+/*
+============
+Cvar_RegisterVariable
+
+Adds a freestanding variable to the variable list.
+============
+*/
+void Cvar_RegisterVariable (cvar_t *variable)
+{
+ char *oldstr;
+
+// first check to see if it has allready been defined
+ if (Cvar_FindVar (variable->name))
+ {
+ Con_Printf ("Can't register variable %s, allready defined\n", variable->name);
+ return;
+ }
+
+// check for overlap with a command
+ if (Cmd_Exists (variable->name))
+ {
+ Con_Printf ("Cvar_RegisterVariable: %s is a command\n", variable->name);
+ return;
+ }
+
+// copy the value off, because future sets will Z_Free it
+ oldstr = variable->string;
+ variable->string = Z_Malloc (Q_strlen(variable->string)+1);
+ Q_strcpy (variable->string, oldstr);
+ variable->value = Q_atof (variable->string);
+
+// link the variable in
+ variable->next = cvar_vars;
+ cvar_vars = variable;
+}
+
+/*
+============
+Cvar_Command
+
+Handles variable inspection and changing from the console
+============
+*/
+qboolean Cvar_Command (void)
+{
+ cvar_t *v;
+
+// check variables
+ v = Cvar_FindVar (Cmd_Argv(0));
+ if (!v)
+ return false;
+
+// perform a variable print or set
+ if (Cmd_Argc() == 1)
+ {
+ Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string);
+ return true;
+ }
+
+ Cvar_Set (v->name, Cmd_Argv(1));
+ return true;
+}
+
+
+/*
+============
+Cvar_WriteVariables
+
+Writes lines containing "set variable value" for all variables
+with the archive flag set to true.
+============
+*/
+void Cvar_WriteVariables (FILE *f)
+{
+ cvar_t *var;
+
+ for (var = cvar_vars ; var ; var = var->next)
+ if (var->archive)
+ fprintf (f, "%s \"%s\"\n", var->name, var->string);
+}
+
diff --git a/source/cvar.h b/source/cvar.h
new file mode 100644
index 0000000..138fda8
--- /dev/null
+++ b/source/cvar.h
@@ -0,0 +1,96 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// cvar.h
+
+/*
+
+cvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly
+in C code.
+
+it is sufficient to initialize a cvar_t with just the first two fields, or
+you can add a ,true flag for variables that you want saved to the configuration
+file when the game is quit:
+
+cvar_t r_draworder = {"r_draworder","1"};
+cvar_t scr_screensize = {"screensize","1",true};
+
+Cvars must be registered before use, or they will have a 0 value instead of the float interpretation of the string. Generally, all cvar_t declarations should be registered in the apropriate init function before any console commands are executed:
+Cvar_RegisterVariable (&host_framerate);
+
+
+C code usually just references a cvar in place:
+if ( r_draworder.value )
+
+It could optionally ask for the value to be looked up for a string name:
+if (Cvar_VariableValue ("r_draworder"))
+
+Interpreted prog code can access cvars with the cvar(name) or
+cvar_set (name, value) internal functions:
+teamplay = cvar("teamplay");
+
+The user can access cvars from the console in two ways:
+r_draworder prints the current value
+r_draworder 0 sets the current value to 0
+Cvars are restricted from having the same names as commands to keep this
+interface from being ambiguous.
+*/
+
+typedef struct cvar_s
+{
+ char *name;
+ char *string;
+ qboolean archive; // set to true to cause it to be saved to vars.rc
+ qboolean server; // notifies players when changed
+ float value;
+ struct cvar_s *next;
+} cvar_t;
+
+void Cvar_RegisterVariable (cvar_t *variable);
+// registers a cvar that allready has the name, string, and optionally the
+// archive elements set.
+
+void Cvar_Set (char *var_name, char *value);
+// equivelant to " " typed at the console
+
+void Cvar_SetValue (char *var_name, float value);
+// expands value to a string and calls Cvar_Set
+
+float Cvar_VariableValue (char *var_name);
+// returns 0 if not defined or non numeric
+
+char *Cvar_VariableString (char *var_name);
+// returns an empty string if not defined
+
+char *Cvar_CompleteVariable (char *partial);
+// attempts to match a partial variable name for command line completion
+// returns NULL if nothing fits
+
+qboolean Cvar_Command (void);
+// called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known
+// command. Returns true if the command was a variable reference that
+// was handled. (print or change)
+
+void Cvar_WriteVariables (FILE *f);
+// Writes lines containing "set variable value" for all variables
+// with the archive flag set to true.
+
+cvar_t *Cvar_FindVar (char *var_name);
+
+extern cvar_t *cvar_vars;
diff --git a/source/draw.h b/source/draw.h
new file mode 100644
index 0000000..c6169ee
--- /dev/null
+++ b/source/draw.h
@@ -0,0 +1,51 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+// draw.h -- these are the only functions outside the refresh allowed
+// to touch the vid buffer
+
+void Draw_Init (void);
+void Draw_Character (int x, int y, int num);
+void Draw_DebugChar (char num);
+void Draw_Pic (int x, int y, qpic_t *pic);
+void Draw_StretchPic (int x, int y, qpic_t *pic, int x_value, int y_value);
+void Draw_ColorPic (int x, int y, qpic_t *pic, float r, float g , float b, float a);
+void Draw_ColoredString (int x, int y, char *text, float r, float g, float b, float a, int scale);
+void Draw_TransPic (int x, int y, qpic_t *pic);
+void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation);
+void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha);
+void Draw_ConsoleBackground (int lines);
+void Draw_LoadingFill(void);
+void Draw_Fill (int x, int y, int w, int h, int c);
+void Draw_FillByColor (int x, int y, int w, int h, unsigned int c);
+void Draw_FadeScreen (void);
+void Draw_String (int x, int y, char *str);
+
+//other
+void Clear_LoadingFill (void);
+byte *StringToRGB (char *s);
+
+extern float loading_cur_step;
+extern char loading_name[32];
+extern float loading_num_step;
+
+qpic_t *Draw_PicFromWad (char *name);
+qpic_t *Draw_CachePic (char *path);
+qpic_t *Draw_CacheImg (char *path);
diff --git a/source/gnu.txt b/source/gnu.txt
new file mode 100644
index 0000000..bf96377
--- /dev/null
+++ b/source/gnu.txt
@@ -0,0 +1,87 @@
+GNU GENERAL PUBLIC LICENSE
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+Preamble
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+
+a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+
+END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/source/host.c b/source/host.c
new file mode 100644
index 0000000..c7e533f
--- /dev/null
+++ b/source/host.c
@@ -0,0 +1,976 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// host.c -- coordinates spawning and killing of local servers
+
+#include "quakedef.h"
+#include "thread.h"
+#include "psp/module.h"
+#include
+#include
+
+/*
+
+A server can allways be started, even if the system started out as a client
+to a remote system.
+A client can NOT be started if the system started as a dedicated server.
+Memory is cleared / released when a server or client begins, not when they end.
+*/
+
+quakeparms_t host_parms;
+
+qboolean host_initialized; // true if into command execution
+
+double host_frametime;
+double host_time;
+double realtime; // without any filtering or bounding
+double oldrealtime; // last frame run
+int host_framecount;
+
+int host_hunklevel;
+
+int minimum_memory;
+
+client_t *host_client; // current client
+
+jmp_buf host_abortserver;
+
+byte *host_basepal;
+byte *host_colormap;
+byte *host_q2pal;
+byte *host_h2pal;
+
+cvar_t host_framerate = {"host_framerate","0"}; // set for slow motion
+cvar_t host_speeds = {"host_speeds","0"}; // set for running times
+
+cvar_t sys_ticrate = {"sys_ticrate","0.05"};
+cvar_t serverprofile = {"serverprofile","0"};
+
+cvar_t fraglimit = {"fraglimit","0",false,true};
+cvar_t timelimit = {"timelimit","0",false,true};
+cvar_t teamplay = {"teamplay","0",false,true};
+
+cvar_t samelevel = {"samelevel","0"};
+
+cvar_t show_fps = {"show_fps","0", true}; // set for running times - muff
+cvar_t cl_maxfps = {"cl_maxfps", "30", true}; // dr_mabuse1981: maxfps setting
+cvar_t show_bat = {"show_bat","0"}; // test
+int fps_count;
+
+cvar_t developer = {"developer","0"};
+
+cvar_t skill = {"skill","1"}; // 0 - 3
+cvar_t deathmatch = {"deathmatch","0"}; // 0, 1, or 2
+cvar_t coop = {"coop","0"}; // 0 or 1
+
+cvar_t pausable = {"pausable","1"};
+
+cvar_t temp1 = {"temp1","0"};
+
+qboolean bmg_type_changed = false;
+
+
+/*
+================
+Host_EndGame
+================
+*/
+void Host_EndGame (char *message, ...)
+{
+ va_list argptr;
+ char string[1024];
+
+ va_start (argptr,message);
+ vsprintf (string,message,argptr);
+ va_end (argptr);
+ Con_DPrintf ("Host_EndGame: %s\n",string);
+
+ if (sv.active)
+ Host_ShutdownServer (false);
+
+ if (cls.state == ca_dedicated)
+ Sys_Error ("Host_EndGame: %s\n",string); // dedicated servers exit
+
+ if (cls.demonum != -1)
+ CL_NextDemo ();
+ else
+ CL_Disconnect ();
+
+ Clear_LoadingFill ();
+
+ longjmp (host_abortserver, 1);
+}
+
+/*
+================
+Host_Error
+
+This shuts down both the client and server
+================
+*/
+void Host_Error (char *error, ...)
+{
+ va_list argptr;
+ char string[1024];
+ static qboolean inerror = false;
+
+ if (inerror)
+ Sys_Error ("Host_Error: recursively entered");
+ inerror = true;
+
+ SCR_EndLoadingPlaque (); // reenable screen updates
+
+ va_start (argptr,error);
+ vsprintf (string,error,argptr);
+ va_end (argptr);
+ Con_Printf ("Host_Error: %s\n",string);
+
+ if (sv.active)
+ Host_ShutdownServer (false);
+
+ if (cls.state == ca_dedicated)
+ Sys_Error ("Host_Error: %s\n",string); // dedicated servers exit
+
+ CL_Disconnect ();
+ cls.demonum = -1;
+
+ Clear_LoadingFill ();
+
+ inerror = false;
+
+ longjmp (host_abortserver, 1);
+}
+
+/*
+================
+Host_FindMaxClients
+================
+*/
+void Host_FindMaxClients (void)
+{
+ int i;
+
+ svs.maxclients = 1;
+
+ i = COM_CheckParm ("-dedicated");
+ if (i)
+ {
+ cls.state = ca_dedicated;
+ if (i != (com_argc - 1))
+ {
+ svs.maxclients = Q_atoi (com_argv[i+1]);
+ }
+ else
+ svs.maxclients = 8;
+ }
+ else
+ cls.state = ca_disconnected;
+
+ i = COM_CheckParm ("-listen");
+ if (i)
+ {
+ if (cls.state == ca_dedicated)
+ Sys_Error ("Only one of -dedicated or -listen can be specified");
+ if (i != (com_argc - 1))
+ svs.maxclients = Q_atoi (com_argv[i+1]);
+ else
+ svs.maxclients = 8;
+ }
+ if (svs.maxclients < 1)
+ svs.maxclients = 8;
+ else if (svs.maxclients > MAX_SCOREBOARD)
+ svs.maxclients = MAX_SCOREBOARD;
+
+ svs.maxclientslimit = svs.maxclients;
+ if (svs.maxclientslimit < 4)
+ svs.maxclientslimit = 4;
+ svs.clients = Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients");
+
+ if (svs.maxclients > 1)
+ Cvar_SetValue ("deathmatch", 1.0);
+ else
+ Cvar_SetValue ("deathmatch", 0.0);
+}
+
+
+/*
+=======================
+Host_InitLocal
+======================
+*/
+void Host_InitLocal (void)
+{
+ Host_InitCommands ();
+
+ Cvar_RegisterVariable (&host_framerate);
+ Cvar_RegisterVariable (&host_speeds);
+
+ Cvar_RegisterVariable (&sys_ticrate);
+ Cvar_RegisterVariable (&serverprofile);
+
+ Cvar_RegisterVariable (&show_bat); // Crow_bar battery info
+ Cvar_RegisterVariable (&show_fps); // muff
+ Cvar_RegisterVariable (&cl_maxfps); // dr_mabuse1981: maxfps setting
+ Cvar_RegisterVariable (&fraglimit);
+ Cvar_RegisterVariable (&timelimit);
+ Cvar_RegisterVariable (&teamplay);
+ Cvar_RegisterVariable (&samelevel);
+ Cvar_RegisterVariable (&skill);
+ Cvar_RegisterVariable (&developer);
+ Cvar_RegisterVariable (&deathmatch);
+ Cvar_RegisterVariable (&coop);
+
+ Cvar_RegisterVariable (&pausable);
+
+ Cvar_RegisterVariable (&temp1);
+
+ Host_FindMaxClients ();
+
+ host_time = 1.0; // so a think at time 0 won't get called
+}
+
+
+/*
+===============
+Host_WriteConfiguration
+
+Writes key bindings and archived cvars to config.cfg
+===============
+*/
+void Host_WriteConfiguration (void)
+{
+ FILE *f;
+
+// dedicated servers initialize the host but don't parse and set the
+// config.cfg cvars
+ if (host_initialized & !isDedicated)
+ {
+ f = fopen (va("%s/config.cfg",com_gamedir), "w");
+ if (!f)
+ {
+ Con_Printf ("Couldn't write config.cfg.\n");
+ return;
+ }
+
+ Key_WriteBindings (f);
+ Key_WriteDTBindings (f);
+ Cvar_WriteVariables (f);
+
+ fclose (f);
+ }
+}
+
+
+/*
+=================
+SV_ClientPrintf
+
+Sends text across to be displayed
+FIXME: make this just a stuffed echo?
+=================
+*/
+void SV_ClientPrintf (char *fmt, ...)
+{
+ va_list argptr;
+ char string[1024];
+
+ va_start (argptr,fmt);
+ vsprintf (string, fmt,argptr);
+ va_end (argptr);
+
+ MSG_WriteByte (&host_client->message, svc_print);
+ MSG_WriteString (&host_client->message, string);
+}
+
+/*
+=================
+SV_BroadcastPrintf
+
+Sends text to all active clients
+=================
+*/
+void SV_BroadcastPrintf (char *fmt, ...)
+{
+ va_list argptr;
+ char string[1024];
+ int i;
+
+ va_start (argptr,fmt);
+ vsprintf (string, fmt,argptr);
+ va_end (argptr);
+
+ for (i=0 ; imessage, svc_stufftext);
+ MSG_WriteString (&host_client->message, string);
+}
+
+/*
+=====================
+SV_DropClient
+
+Called when the player is getting totally kicked off the host
+if (crash = true), don't bother sending signofs
+=====================
+*/
+void SV_DropClient (qboolean crash)
+{
+ int saveSelf;
+ int i;
+ client_t *client;
+
+ if (!crash)
+ {
+ // send any final messages (don't check for errors)
+ if (NET_CanSendMessage (host_client->netconnection))
+ {
+ MSG_WriteByte (&host_client->message, svc_disconnect);
+ NET_SendMessage (host_client->netconnection, &host_client->message);
+ }
+
+ if (host_client->edict && host_client->spawned)
+ {
+ // call the prog function for removing a client
+ // this will set the body to a dead frame, among other things
+ saveSelf = pr_global_struct->self;
+ pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
+ PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
+ pr_global_struct->self = saveSelf;
+ }
+
+ Sys_Printf ("Client %s removed\n",host_client->name);
+ }
+
+// break the net connection
+ NET_Close (host_client->netconnection);
+ host_client->netconnection = NULL;
+
+// free the client (the body stays around)
+ host_client->active = false;
+ host_client->name[0] = 0;
+ host_client->old_points = -999999;
+ host_client->old_kills = -999999;
+ net_activeconnections--;
+
+// send notification to all clients
+ for (i=0, client = svs.clients ; iactive)
+ continue;
+ MSG_WriteByte (&client->message, svc_updatename);
+ MSG_WriteByte (&client->message, host_client - svs.clients);
+ MSG_WriteString (&client->message, "");
+ MSG_WriteByte (&client->message, svc_updatepoints);
+ MSG_WriteByte (&client->message, host_client - svs.clients);
+ MSG_WriteLong (&client->message, 0);
+ MSG_WriteByte (&client->message, svc_updatekills);
+ MSG_WriteByte (&client->message, host_client - svs.clients);
+ MSG_WriteShort (&client->message, 0);
+ }
+}
+
+/*
+==================
+Host_ShutdownServer
+
+This only happens at the end of a game, not between levels
+==================
+*/
+void Host_ShutdownServer(qboolean crash)
+{
+ int i;
+ int count;
+ sizebuf_t buf;
+ char message[4];
+ double start;
+
+ if (!sv.active)
+ return;
+
+ sv.active = false;
+
+// stop all client sounds immediately
+ if (cls.state == ca_connected)
+ CL_Disconnect ();
+
+// flush any pending messages - like the score!!!
+ start = Sys_FloatTime();
+ do
+ {
+ count = 0;
+ for (i=0, host_client = svs.clients ; iactive && host_client->message.cursize)
+ {
+ if (NET_CanSendMessage (host_client->netconnection))
+ {
+ NET_SendMessage(host_client->netconnection, &host_client->message);
+ SZ_Clear (&host_client->message);
+ }
+ else
+ {
+ NET_GetMessage(host_client->netconnection);
+ count++;
+ }
+ }
+ }
+ if ((Sys_FloatTime() - start) > 3.0)
+ break;
+ }
+ while (count);
+
+// make sure all the clients know we're disconnecting
+ buf.data = (byte *)message;
+ buf.maxsize = 4;
+ buf.cursize = 0;
+ MSG_WriteByte(&buf, svc_disconnect);
+ count = NET_SendToAll(&buf, 5);
+ if (count)
+ Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count);
+
+ for (i=0, host_client = svs.clients ; iactive)
+ SV_DropClient(crash);
+
+//
+// clear structures
+//
+ memset (&sv, 0, sizeof(sv));
+ memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t));
+}
+
+
+/*
+================
+Host_ClearMemory
+
+This clears all the memory used by both the client and server, but does
+not reinitialize anything.
+================
+*/
+extern int perk_order[8];
+extern int current_perk_order;
+extern double Hitmark_Time, crosshair_spread_time;
+extern float cur_spread;
+extern float crosshair_offset_step;
+void Host_ClearMemory (void)
+{
+ Con_DPrintf ("Clearing memory\n");
+
+ D_FlushCaches ();
+
+ Mod_ClearAll ();
+
+ if (host_hunklevel)
+ Hunk_FreeToLowMark (host_hunklevel);
+
+ cls.signon = 0;
+ memset (&sv, 0, sizeof(sv));
+ memset (&cl, 0, sizeof(cl));
+ perk_order[0] = 0;
+ perk_order[1] = 0;
+ perk_order[2] = 0;
+ perk_order[3] = 0;
+ perk_order[4] = 0;
+ perk_order[5] = 0;
+ perk_order[6] = 0;
+ perk_order[7] = 0;
+ cl.perks = 0;
+ current_perk_order = 0;
+ crosshair_spread_time = 0;
+ crosshair_offset_step = 0;
+ cur_spread = 0;
+ Hitmark_Time = 0;
+
+}
+
+
+//============================================================================
+
+
+/*
+===================
+Host_FilterTime
+
+Returns false if the time is too short to run a frame
+===================
+*/
+qboolean Host_FilterTime (float time)
+{
+ realtime += time;
+
+/* dr_mabuse1981: old dquake values commented out
+ if (!cls.timedemo && realtime - oldrealtime < 1.0/250.0)
+ return false; // framerate is too high
+*/
+
+ if (cl_maxfps.value < 1) Cvar_SetValue("cl_maxfps", 30);
+ if (!cls.timedemo && realtime - oldrealtime < 1.0/cl_maxfps.value)
+ return false; // framerate is too high
+
+ host_frametime = realtime - oldrealtime;
+ oldrealtime = realtime;
+
+ if (host_framerate.value > 0)
+ host_frametime = host_framerate.value;
+ else
+ { // don't allow really long or short frames
+ if (host_frametime > 0.1)
+ host_frametime = 0.1;
+ if (host_frametime < 0.001)
+ host_frametime = 0.001;
+ }
+
+ return true;
+}
+
+
+/*
+===================
+Host_GetConsoleCommands
+
+Add them exactly as if they had been typed at the console
+===================
+*/
+void Host_GetConsoleCommands (void)
+{
+ char *cmd;
+
+ while (1)
+ {
+ cmd = Sys_ConsoleInput ();
+ if (!cmd)
+ break;
+ Cbuf_AddText (cmd);
+ }
+}
+
+
+/*
+==================
+Host_ServerFrame
+
+==================
+*/
+void Host_ServerFrame (void)
+{
+// run the world state
+ pr_global_struct->frametime = host_frametime;
+
+// set the time and clear the general datagram
+ SV_ClearDatagram ();
+
+// check for new clients
+ SV_CheckForNewClients ();
+
+// read client messages
+ SV_RunClients ();
+
+// move things around and think
+// always pause in single player if in console or menus
+ if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
+ SV_Physics ();
+
+// send all messages to the clients
+ SV_SendClientMessages ();
+}
+
+
+/*
+==================
+Host_Frame
+
+Runs all active servers
+==================
+*/
+void _Host_Frame (float time)
+{
+ static double time1 = 0;
+ static double time2 = 0;
+ static double time3 = 0;
+ int pass1, pass2, pass3;
+
+ if (setjmp (host_abortserver) )
+ {
+ return; // something bad happened, or the server disconnected
+ }
+
+// keep the random time dependent
+ rand ();
+
+// decide the simulation time
+ if (!Host_FilterTime (time))
+ {
+ return; // don't run too fast, or packets will flood out
+ }
+
+// get new key events
+ Sys_SendKeyEvents ();
+
+// allow mice or other external controllers to add commands
+ IN_Commands ();
+
+// process console commands
+ Cbuf_Execute ();
+
+ NET_Poll();
+
+// if running the server locally, make intentions now
+ if (sv.active)
+ CL_SendCmd ();
+
+//-------------------
+//
+// server operations
+//
+//-------------------
+
+// check for commands typed to the host
+ Host_GetConsoleCommands ();
+ if (sv.active)
+ Host_ServerFrame ();
+//-------------------
+//
+// client operations
+//
+//-------------------
+
+// if running the server remotely, send intentions now after
+// the incoming messages have been read
+ if (!sv.active)
+ CL_SendCmd ();
+
+ host_time += host_frametime;
+
+// fetch results from server
+ if (cls.state == ca_connected)
+ {
+ CL_ReadFromServer ();
+ }
+// update video
+ if (host_speeds.value)
+ time1 = Sys_FloatTime ();
+ SCR_UpdateScreen ();
+ if (host_speeds.value)
+ time2 = Sys_FloatTime ();
+// update audio
+ if (cls.signon == SIGNONS)
+ {
+ Thread_UpdateSound(r_origin, vpn, vright, vup);
+ //S_Update (r_origin, vpn, vright, vup);
+ CL_DecayLights ();
+ }
+ else
+ Thread_UpdateSound(vec3_origin, vec3_origin, vec3_origin, vec3_origin);
+ //S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
+
+ //if (bmg_type_changed == true) {
+ CDAudio_Update();
+ // bmg_type_changed = false;
+ //}
+
+ if (host_speeds.value)
+ {
+ pass1 = (time1 - time3)*1000;
+ time3 = Sys_FloatTime ();
+ pass2 = (time2 - time1)*1000;
+ pass3 = (time3 - time2)*1000;
+ Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
+ pass1+pass2+pass3, pass1, pass2, pass3);
+ }
+
+ //frame speed counter
+ fps_count++;//muff
+ host_framecount++;
+}
+
+void Host_Frame (float time)
+{
+ double time1, time2;
+ static double timetotal;
+ static int timecount;
+ int i, c, m;
+
+ if (!serverprofile.value)
+ {
+ _Host_Frame (time);
+ return;
+ }
+ time1 = Sys_FloatTime ();
+ _Host_Frame (time);
+ time2 = Sys_FloatTime ();
+
+ timetotal += time2 - time1;
+ timecount++;
+
+ if (timecount < 1000)
+ {
+ return;
+ }
+ m = timetotal*1000/timecount;
+ timecount = 0;
+ timetotal = 0;
+ c = 0;
+ for (i=0 ; iargv[0];
+ for (i = 0; i < com_argc; i++)
+ {
+ Sys_FileRead (vcrFile, &len, sizeof(int));
+ p = malloc(len);
+ Sys_FileRead (vcrFile, p, len);
+ com_argv[i+1] = p;
+ }
+ com_argc++; /* add one for arg[0] */
+ parms->argc = com_argc;
+ parms->argv = com_argv;
+ }
+
+ if ( (n = COM_CheckParm("-record")) != 0)
+ {
+ vcrFile = Sys_FileOpenWrite("quake.vcr");
+
+ i = VCR_SIGNATURE;
+ Sys_FileWrite(vcrFile, &i, sizeof(int));
+ i = com_argc - 1;
+ Sys_FileWrite(vcrFile, &i, sizeof(int));
+ for (i = 1; i < com_argc; i++)
+ {
+ if (i == n)
+ {
+ len = 10;
+ Sys_FileWrite(vcrFile, &len, sizeof(int));
+ Sys_FileWrite(vcrFile, "-playback", len);
+ continue;
+ }
+ len = Q_strlen(com_argv[i]) + 1;
+ Sys_FileWrite(vcrFile, &len, sizeof(int));
+ Sys_FileWrite(vcrFile, com_argv[i], len);
+ }
+ }
+
+}
+
+void Preload (void)
+{
+ Mod_ForName ("models/player.mdl", true);
+
+ Mod_ForName ("progs/ai/zb#.mdl",true);
+ Mod_ForName ("progs/ai/zal(.mdl",true);
+ Mod_ForName ("progs/ai/zar(.mdl",true);
+ Mod_ForName ("progs/ai/zh^.mdl",true);
+ Mod_ForName ("progs/ai/zbc#.mdl",true);
+ Mod_ForName ("progs/ai/zalc(.mdl",true);
+ Mod_ForName ("progs/ai/zarc(.mdl",true);
+ Mod_ForName ("progs/ai/zhc^.mdl",true);
+
+ Mod_ForName ("progs/ai/zfull.mdl",true);
+ Mod_ForName ("progs/ai/zcfull.mdl",true);
+
+ Mod_ForName ("progs/VModels/v_knife.mdl", true);
+ Mod_ForName ("progs/VModels/v_colt.mdl", true);
+ Mod_ForName ("progs/Misc/instakill!.mdl", true);
+ Mod_ForName ("progs/Misc/maxammo!.mdl", true);
+ Mod_ForName ("progs/Misc/nuke!.mdl", true);
+ Mod_ForName ("progs/Misc/carpenter!.mdl", true);
+ Mod_ForName ("progs/Misc/x2!.mdl", true);
+}
+/*
+====================
+Host_Init
+====================
+*/
+#include "cl_slist.h"
+
+void M_Start_Menu_f (void);
+void Host_Init (quakeparms_t *parms)
+{
+
+ minimum_memory = MINIMUM_MEMORY;
+
+ if (COM_CheckParm ("-minmemory"))
+ parms->memsize = minimum_memory;
+
+ host_parms = *parms;
+
+ if (parms->memsize < minimum_memory)
+ Sys_Error ("Only %4.1f megs of memory available, can't execute game", parms->memsize / (float)0x100000);
+
+ com_argc = parms->argc;
+ com_argv = parms->argv;
+
+ Memory_Init (parms->membase, parms->memsize);
+ Cbuf_Init ();
+ Cmd_Init ();
+ V_Init ();
+ Chase_Init ();
+ Host_InitVCR (parms);
+ COM_Init (parms->basedir);
+ Host_InitLocal ();
+ Key_Init ();
+ Con_Init ();
+ M_Init ();
+ PR_Init ();
+ Mod_Init ();
+ NET_Init ();
+ SV_Init ();
+ Con_Printf ("PSP NZP v%4.1f (PBP: "__TIME__" "__DATE__")\n", (float)(VERSION));
+ Con_Printf ("%4.1f megabyte heap \n",parms->memsize/ (1024*1024.0));
+ Con_Printf ("%4.1f megabyte PSP application heap \n",1.0f*PSP_HEAP_SIZE_MB);
+ Con_Printf ("PSP Model: %s\n", Sys_GetPSPModel());
+ Con_Printf ("VRAM Size: %i bytes\n", sceGeEdramGetSize());
+
+ R_InitTextures (); // needed even for dedicated servers
+ if (cls.state != ca_dedicated)
+ {
+ host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp");
+ if (!host_basepal)
+ Sys_Error ("Couldn't load gfx/palette.lmp");
+
+ host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp");
+ if (!host_colormap)
+ Sys_Error ("Couldn't load gfx/colormap.lmp");
+
+ host_q2pal = (byte *)COM_LoadHunkFile ("gfx/q2pal.lmp");
+ if (!host_q2pal)
+ Sys_Error ("Couldn't load gfx/q2pal.lmp");
+
+ host_h2pal = (byte *)COM_LoadHunkFile ("gfx/h2pal.lmp");
+ if (!host_h2pal)
+ Sys_Error ("Couldn't load gfx/h2pal.lmp");
+
+ IN_Init ();
+ VID_Init (host_basepal);
+ Draw_Init ();
+ SCR_Init ();
+ R_Init ();
+ S_Init ();
+ CDAudio_Init ();
+ HUD_Init ();
+ CL_Init ();
+ }
+ Preload();
+ Cbuf_InsertText ("exec nzp.rc\n");
+
+ Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
+ host_hunklevel = Hunk_LowMark ();
+
+ host_initialized = true;
+ M_Start_Menu_f();
+ Sys_Printf ("========Quake Initialized=========\n");
+ Con_Printf ("==========NZP Initialized=========\n");
+}
+
+
+/*
+===============
+Host_Shutdown
+
+FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better
+to run quit through here before the final handoff to the sys code.
+===============
+*/
+void Host_Shutdown(void)
+{
+ static qboolean isdown = false;
+
+ if (isdown)
+ {
+ return;
+ }
+ isdown = true;
+
+// keep Con_Printf from trying to update the screen
+ scr_disabled_for_loading = true;
+
+ Clear_LoadingFill ();
+
+ SList_Shutdown();
+
+ Host_WriteConfiguration ();
+
+ if (con_initialized)
+ History_Shutdown ();
+
+ CDAudio_Shutdown ();
+ NET_Shutdown ();
+ S_Shutdown();
+ IN_Shutdown ();
+
+ if (cls.state != ca_dedicated)
+ {
+ VID_Shutdown();
+ }
+}
+
diff --git a/source/host_cmd.c b/source/host_cmd.c
new file mode 100644
index 0000000..8390f9d
--- /dev/null
+++ b/source/host_cmd.c
@@ -0,0 +1,1454 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+extern cvar_t pausable;
+
+int current_skill;
+
+void Mod_Print (void);
+
+/*
+==================
+Host_Quit_f
+==================
+*/
+
+extern void M_Menu_Quit_f (void);
+
+void Host_Quit_f (void)
+{
+ if (key_dest != key_console && cls.state != ca_dedicated)
+ {
+ M_Menu_Quit_f ();
+ return;
+ }
+ CL_Disconnect ();
+ Host_ShutdownServer(false);
+
+ Sys_Quit ();
+}
+
+
+/*
+==================
+Host_Status_f
+==================
+*/
+void Host_Status_f (void)
+{
+ client_t *client;
+ int seconds;
+ int minutes;
+ int hours = 0;
+ int j;
+ void (*print) (char *fmt, ...);
+
+ if (cmd_source == src_command)
+ {
+ if (!sv.active)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+ print = Con_Printf;
+ }
+ else
+ print = SV_ClientPrintf;
+
+ print ("host: %s\n", Cvar_VariableString ("hostname"));
+ print ("version: %4.2f\n", VERSION);
+ if (tcpipAvailable)
+ print ("tcp/ip: %s\n", my_tcpip_address);
+ if (ipxAvailable)
+ print ("ipx: %s\n", my_ipx_address);
+ print ("map: %s\n", sv.name);
+ print ("players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients);
+ for (j=0, client = svs.clients ; jactive)
+ continue;
+ seconds = (int)(net_time - client->netconnection->connecttime);
+ minutes = seconds / 60;
+ if (minutes)
+ {
+ seconds -= (minutes * 60);
+ hours = minutes / 60;
+ if (hours)
+ minutes -= (hours * 60);
+ }
+ else
+ hours = 0;
+ print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->v.points, hours, minutes, seconds);
+ print (" %s\n", client->netconnection->address);
+ print(" SignOn State: %i\n",cls.signon);
+ }
+}
+
+
+/*
+==================
+Host_God_f
+
+Sets client to godmode
+==================
+*/
+void Host_God_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE;
+ if (!((int)sv_player->v.flags & FL_GODMODE) )
+ SV_ClientPrintf ("godmode OFF\n");
+ else
+ SV_ClientPrintf ("godmode ON\n");
+}
+
+void Host_Notarget_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET;
+ if (!((int)sv_player->v.flags & FL_NOTARGET) )
+ SV_ClientPrintf ("notarget OFF\n");
+ else
+ SV_ClientPrintf ("notarget ON\n");
+}
+
+qboolean noclip_anglehack;
+
+void Host_Noclip_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ if (sv_player->v.movetype != MOVETYPE_NOCLIP)
+ {
+ noclip_anglehack = true;
+ sv_player->v.movetype = MOVETYPE_NOCLIP;
+ SV_ClientPrintf ("noclip ON\n");
+ }
+ else
+ {
+ noclip_anglehack = false;
+ sv_player->v.movetype = MOVETYPE_WALK;
+ SV_ClientPrintf ("noclip OFF\n");
+ }
+}
+
+/*
+==================
+Host_Fly_f
+
+Sets client to flymode
+==================
+*/
+void Host_Fly_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ if (sv_player->v.movetype != MOVETYPE_FLY)
+ {
+ sv_player->v.movetype = MOVETYPE_FLY;
+ SV_ClientPrintf ("flymode ON\n");
+ }
+ else
+ {
+ sv_player->v.movetype = MOVETYPE_WALK;
+ SV_ClientPrintf ("flymode OFF\n");
+ }
+}
+
+
+/*
+==================
+Host_Ping_f
+
+==================
+*/
+void Host_Ping_f (void)
+{
+ int i, j;
+ float total;
+ client_t *client;
+
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ SV_ClientPrintf ("Client ping times:\n");
+ for (i=0, client = svs.clients ; iactive)
+ continue;
+ total = 0;
+ for (j=0 ; jping_times[j];
+ total /= NUM_PING_TIMES;
+ SV_ClientPrintf ("%4i %s\n", (int)(total*1000), client->name);
+ }
+}
+
+/*
+===============================================================================
+
+SERVER TRANSITIONS
+
+===============================================================================
+*/
+
+
+/*
+======================
+Host_Map_f
+
+handle a
+map
+command from the console. Active clients are kicked off.
+======================
+*/
+void Host_Map_f (void)
+{
+ int i;
+ char name[MAX_QPATH];
+
+ if (cmd_source != src_command)
+ return;
+
+ cls.demonum = -1; // stop demo loop in case this fails
+
+ CL_Disconnect ();
+ Host_ShutdownServer(false);
+
+ key_dest = key_game; // remove console or menu
+ SCR_BeginLoadingPlaque ();
+
+ cls.mapstring[0] = 0;
+ for (i=0 ; i : continue game on a new level\n");
+ return;
+ }
+ if (!sv.active || cls.demoplayback)
+ {
+ Con_Printf ("Only the server may changelevel\n");
+ return;
+ }
+ SV_SaveSpawnparms ();
+ strcpy (level, Cmd_Argv(1));
+ SV_SpawnServer (level);
+}
+
+/*
+==================
+Host_Restart_f
+
+Restarts the current server for a dead player
+==================
+*/
+void Host_Restart_f (void)
+{
+ char mapname[MAX_QPATH];
+
+ if (cls.demoplayback || !sv.active)
+ return;
+
+ if (cmd_source != src_command)
+ return;
+ strcpy (mapname, sv.name); // must copy out, because it gets cleared
+ // in sv_spawnserver
+ SV_SpawnServer (mapname);
+}
+
+/*
+==================
+Host_Reconnect_f
+
+This command causes the client to wait for the signon messages again.
+This is sent just before a server changes levels
+==================
+*/
+void Host_Reconnect_f (void)
+{
+ SCR_BeginLoadingPlaque ();
+ cls.signon = 0; // need new connection messages
+}
+
+/*
+=====================
+Host_Connect_f
+
+User command to connect to server
+=====================
+*/
+void Host_Connect_f (void)
+{
+ char name[MAX_QPATH];
+
+ cls.demonum = -1; // stop demo loop in case this fails
+ if (cls.demoplayback)
+ {
+ CL_StopPlayback ();
+ CL_Disconnect ();
+ }
+ strcpy (name, Cmd_Argv(1));
+ CL_EstablishConnection (name);
+ Host_Reconnect_f ();
+}
+
+
+/*
+===============================================================================
+
+LOAD / SAVE GAME
+
+===============================================================================
+*/
+
+#define SAVEGAME_VERSION 5
+
+/*
+===============
+Host_SavegameComment
+
+Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current
+===============
+*/
+void Host_SavegameComment (char *text)
+{
+ int i;
+ char kills[20];
+
+ for (i=0 ; i : save a game\n");
+ return;
+ }
+
+ if (strstr(Cmd_Argv(1), ".."))
+ {
+ Con_Printf ("Relative pathnames are not allowed.\n");
+ return;
+ }
+
+ for (i=0 ; iv.health <= 0) )
+ {
+ Con_Printf ("Can't savegame with a dead player\n");
+ return;
+ }
+ }
+
+ sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
+ COM_DefaultExtension (name, ".sav");
+
+ Con_Printf ("Saving game to %s...\n", name);
+ f = fopen (name, "w");
+ if (!f)
+ {
+ Con_Printf ("ERROR: couldn't open save file for writing.\n");
+ return;
+ }
+
+ fprintf (f, "%i\n", SAVEGAME_VERSION);
+ Host_SavegameComment (comment);
+ fprintf (f, "%s\n", comment);
+ for (i=0 ; ispawn_parms[i]);
+ fprintf (f, "%d\n", current_skill);
+ fprintf (f, "%s\n", sv.name);
+ fprintf (f, "%f\n",sv.time);
+
+// write the light styles
+
+ for (i=0 ; i : load a game\n");
+ return;
+ }
+
+ cls.demonum = -1; // stop demo loop in case this fails
+
+ sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
+ COM_DefaultExtension (name, ".sav");
+
+// we can't call SCR_BeginLoadingPlaque, because too much stack space has
+// been used. The menu calls it before stuffing loadgame command
+// SCR_BeginLoadingPlaque ();
+
+ Con_Printf ("Loading game from %s...\n", name);
+ f = fopen (name, "r");
+ if (!f)
+ {
+ Con_Printf ("ERROR: couldn't open save file for reading.\n");
+ return;
+ }
+
+ fscanf (f, "%i\n", &version);
+ if (version != SAVEGAME_VERSION)
+ {
+ fclose (f);
+ Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
+ return;
+ }
+ fscanf (f, "%s\n", str);
+ for (i=0 ; iv, 0, progs->entityfields * 4);
+ ent->free = false;
+ ED_ParseEdict (start, ent);
+
+ // link it into the bsp tree
+ if (!ent->free)
+ SV_LinkEdict (ent, false);
+ }
+
+ entnum++;
+ }
+
+ sv.num_edicts = entnum;
+ sv.time = time;
+
+ fclose (f);
+
+ for (i=0 ; ispawn_parms[i] = spawn_parms[i];
+
+ if (cls.state != ca_dedicated)
+ {
+ CL_EstablishConnection ("local");
+ Host_Reconnect_f ();
+ }
+}
+
+
+
+//============================================================================
+
+/*
+======================
+Host_Name_f
+======================
+*/
+void Host_Name_f (void)
+{
+ char *newName;
+
+ if (Cmd_Argc () == 1)
+ {
+ Con_Printf ("\"name\" is \"%s\"\n", cl_name.string);
+ return;
+ }
+ if (Cmd_Argc () == 2)
+ newName = Cmd_Argv(1);
+ else
+ newName = Cmd_Args();
+ newName[15] = 0;
+
+ if (cmd_source == src_command)
+ {
+ if (Q_strcmp(cl_name.string, newName) == 0)
+ return;
+ Cvar_Set ("_cl_name", newName);
+ if (cls.state == ca_connected)
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (host_client->name[0] && strcmp(host_client->name, "unconnected") )
+ if (Q_strcmp(host_client->name, newName) != 0)
+ Con_Printf ("%s renamed to %s\n", host_client->name, newName);
+ Q_strcpy (host_client->name, newName);
+ host_client->edict->v.netname = host_client->name - pr_strings;
+
+// send notification to all clients
+
+ MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
+ MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+ MSG_WriteString (&sv.reliable_datagram, host_client->name);
+}
+
+
+void Host_Version_f (void)
+{
+ Con_Printf ("Version %4.2f\n", VERSION);
+ Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
+}
+
+
+
+void Host_Say(qboolean teamonly)
+{
+ client_t *client;
+ client_t *save;
+ int j;
+ char *p;
+ char text[64];
+ qboolean fromServer = false;
+
+ if (cmd_source == src_command)
+ {
+ if (cls.state == ca_dedicated)
+ {
+ fromServer = true;
+ teamonly = false;
+ }
+ else
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+ }
+
+ if (Cmd_Argc () < 2)
+ return;
+
+ save = host_client;
+
+ p = Cmd_Args();
+// remove quotes if present
+ if (*p == '"')
+ {
+ p++;
+ p[Q_strlen(p)-1] = 0;
+ }
+
+// turn on color set 1
+ if (!fromServer)
+ sprintf (text, "%c%s: ", 1, save->name);
+ else
+ sprintf (text, "%c<%s> ", 1, hostname.string);
+
+ j = sizeof(text) - 2 - Q_strlen(text); // -2 for /n and null terminator
+ if (Q_strlen(p) > j)
+ p[j] = 0;
+
+ strcat (text, p);
+ strcat (text, "\n");
+
+ for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
+ {
+ if (!client || !client->active || !client->spawned)
+ continue;
+ if (teamplay.value && teamonly && client->edict->v.team != save->edict->v.team)
+ continue;
+ host_client = client;
+ SV_ClientPrintf("%s", text);
+ }
+ host_client = save;
+
+ Sys_Printf("%s", &text[1]);
+}
+
+
+void Host_Say_f(void)
+{
+ Host_Say(false);
+}
+
+
+void Host_Say_Team_f(void)
+{
+ Host_Say(true);
+}
+
+
+void Host_Tell_f(void)
+{
+ client_t *client;
+ client_t *save;
+ int j;
+ char *p;
+ char text[64];
+
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (Cmd_Argc () < 3)
+ return;
+
+ Q_strcpy(text, host_client->name);
+ Q_strcat(text, ": ");
+
+ p = Cmd_Args();
+
+// remove quotes if present
+ if (*p == '"')
+ {
+ p++;
+ p[Q_strlen(p)-1] = 0;
+ }
+
+// check length & truncate if necessary
+ j = sizeof(text) - 2 - Q_strlen(text); // -2 for /n and null terminator
+ if (Q_strlen(p) > j)
+ p[j] = 0;
+
+ strcat (text, p);
+ strcat (text, "\n");
+
+ save = host_client;
+ for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
+ {
+ if (!client->active || !client->spawned)
+ continue;
+ if (Q_strcasecmp(client->name, Cmd_Argv(1)))
+ continue;
+ host_client = client;
+ SV_ClientPrintf("%s", text);
+ break;
+ }
+ host_client = save;
+}
+
+/*
+==================
+Host_Kill_f
+==================
+*/
+void Host_Kill_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (sv_player->v.health <= 0)
+ {
+ SV_ClientPrintf ("Can't suicide -- allready dead!\n");
+ return;
+ }
+
+ pr_global_struct->time = sv.time;
+ pr_global_struct->self = EDICT_TO_PROG(sv_player);
+ PR_ExecuteProgram (pr_global_struct->ClientKill);
+}
+
+
+/*
+==================
+Host_Pause_f
+==================
+*/
+void Host_Pause_f (void)
+{
+
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+ if (!pausable.value)
+ SV_ClientPrintf ("Pause not allowed.\n");
+ else
+ {
+ sv.paused ^= 1;
+
+ if (sv.paused)
+ {
+ SV_BroadcastPrintf ("%s paused the game\n", pr_strings + sv_player->v.netname);
+ }
+ else
+ {
+ SV_BroadcastPrintf ("%s unpaused the game\n",pr_strings + sv_player->v.netname);
+ }
+
+ // send notification to all clients
+ MSG_WriteByte (&sv.reliable_datagram, svc_setpause);
+ MSG_WriteByte (&sv.reliable_datagram, sv.paused);
+ }
+}
+
+//===========================================================================
+
+
+/*
+==================
+Host_PreSpawn_f
+==================
+*/
+void Host_PreSpawn_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Con_Printf ("prespawn is not valid from the console\n");
+ return;
+ }
+
+ if (host_client->spawned)
+ {
+ Con_Printf ("prespawn not valid -- allready spawned\n");
+ return;
+ }
+
+ SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize);
+ MSG_WriteByte (&host_client->message, svc_signonnum);
+ MSG_WriteByte (&host_client->message, 2);
+ host_client->sendsignon = true;
+}
+
+/*
+==================
+Host_Spawn_f
+==================
+*/
+void Host_Spawn_f (void)
+{
+ int i;
+ client_t *client;
+ edict_t *ent;
+
+
+ if (cmd_source == src_command)
+ {
+ Con_Printf ("spawn is not valid from the console\n");
+ return;
+ }
+
+ if (host_client->spawned)
+ {
+ Con_Printf ("Spawn not valid -- allready spawned\n");
+ return;
+ }
+
+ host_client->nomap = false;
+
+
+// run the entrance script
+ if (sv.loadgame)
+ { // loaded games are fully inited allready
+ // if this is the last client to be connected, unpause
+ sv.paused = false;
+ }
+ else
+ {
+ // set up the edict
+ ent = host_client->edict;
+
+
+
+
+ memset (&ent->v, 0, progs->entityfields * 4);
+
+ ent->v.colormap = NUM_FOR_EDICT(ent);
+ ent->v.netname = host_client->name - pr_strings;
+
+ // copy spawn parms out of the client_t
+
+ for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
+ (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i];
+
+ // call the spawn function
+
+ pr_global_struct->time = sv.time;
+ pr_global_struct->self = EDICT_TO_PROG(sv_player);
+ PR_ExecuteProgram (pr_global_struct->ClientConnect);
+
+ if ((Sys_FloatTime() - host_client->netconnection->connecttime) <= sv.time)
+ Sys_Printf ("%s entered the game\n", host_client->name);
+
+ PR_ExecuteProgram (pr_global_struct->PutClientInServer);
+ S_LocalSound ("sounds/rounds/splash.wav"); // since this won't execute in progs...
+ }
+
+// send all current names, colors, and frag counts
+ SZ_Clear (&host_client->message);
+
+// send time of update
+ MSG_WriteByte (&host_client->message, svc_time);
+ MSG_WriteFloat (&host_client->message, sv.time);
+
+ for (i=0, client = svs.clients ; imessage, svc_updatename);
+ MSG_WriteByte (&host_client->message, i);
+ MSG_WriteString (&host_client->message, client->name);
+ MSG_WriteByte (&host_client->message, svc_updatepoints);
+ MSG_WriteByte (&host_client->message, i);
+ MSG_WriteLong (&host_client->message, client->old_points);
+ MSG_WriteByte (&host_client->message, svc_updatekills);
+ MSG_WriteByte (&host_client->message, i);
+ MSG_WriteShort (&host_client->message, client->old_kills);
+ }
+
+// send all current light styles
+ for (i=0 ; imessage, svc_lightstyle);
+ MSG_WriteByte (&host_client->message, (char)i);
+ MSG_WriteString (&host_client->message, sv.lightstyles[i]);
+ }
+
+//
+// send some stats
+//
+ MSG_WriteByte (&host_client->message, svc_updatestat);
+ MSG_WriteByte (&host_client->message, STAT_ROUNDS);
+ MSG_WriteByte (&host_client->message, pr_global_struct->rounds);
+
+ MSG_WriteByte (&host_client->message, svc_updatestat);
+ MSG_WriteByte (&host_client->message, STAT_ROUNDCHANGE);
+ MSG_WriteByte (&host_client->message, pr_global_struct->rounds_change);
+
+ MSG_WriteByte (&host_client->message, svc_updatestat);
+ MSG_WriteByte (&host_client->message, STAT_X2);
+ MSG_WriteByte (&host_client->message, sv_player->v.x2_icon);
+
+ MSG_WriteByte (&host_client->message, svc_updatestat);
+ MSG_WriteByte (&host_client->message, STAT_INSTA);
+ MSG_WriteByte (&host_client->message, sv_player->v.insta_icon);
+
+//
+// send a fixangle
+// Never send a roll angle, because savegames can catch the server
+// in a state where it is expecting the client to correct the angle
+// and it won't happen if the game was just loaded, so you wind up
+// with a permanent head tilt
+ ent = EDICT_NUM( 1 + (host_client - svs.clients) );
+ MSG_WriteByte (&host_client->message, svc_setangle);
+ for (i=0 ; i < 2 ; i++)
+ MSG_WriteAngle (&host_client->message, ent->v.angles[i] );
+ MSG_WriteAngle (&host_client->message, 0 );
+
+ SV_WriteClientdataToMessage (sv_player, &host_client->message);
+
+ MSG_WriteByte (&host_client->message, svc_signonnum);
+ MSG_WriteByte (&host_client->message, 3);
+ host_client->sendsignon = true;
+}
+
+/*
+==================
+Host_Begin_f
+==================
+*/
+void Host_Begin_f (void)
+{
+ if (cmd_source == src_command)
+ {
+ Con_Printf ("begin is not valid from the console\n");
+ return;
+ }
+
+ host_client->spawned = true;
+}
+
+//===========================================================================
+
+
+/*
+==================
+Host_Kick_f
+
+Kicks a user off of the server
+==================
+*/
+void Host_Kick_f (void)
+{
+ char *who;
+ char *message = NULL;
+ client_t *save;
+ int i;
+ qboolean byNumber = false;
+
+ if (cmd_source == src_command)
+ {
+ if (!sv.active)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+ }
+ else if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ save = host_client;
+
+ if (Cmd_Argc() > 2 && Q_strcmp(Cmd_Argv(1), "#") == 0)
+ {
+ i = Q_atof(Cmd_Argv(2)) - 1;
+ if (i < 0 || i >= svs.maxclients)
+ return;
+ if (!svs.clients[i].active)
+ return;
+ host_client = &svs.clients[i];
+ byNumber = true;
+ }
+ else
+ {
+ for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
+ {
+ if (!host_client->active)
+ continue;
+ if (Q_strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
+ break;
+ }
+ }
+
+ if (i < svs.maxclients)
+ {
+ if (cmd_source == src_command)
+ if (cls.state == ca_dedicated)
+ who = "Console";
+ else
+ who = cl_name.string;
+ else
+ who = save->name;
+
+ // can't kick yourself!
+ if (host_client == save)
+ return;
+
+ if (Cmd_Argc() > 2)
+ {
+ message = COM_Parse(Cmd_Args());
+ if (byNumber)
+ {
+ message++; // skip the #
+ while (*message == ' ') // skip white space
+ message++;
+ message += Q_strlen(Cmd_Argv(2)); // skip the number
+ }
+ while (*message && *message == ' ')
+ message++;
+ }
+ if (message)
+ SV_ClientPrintf ("Kicked by %s: %s\n", who, message);
+ else
+ SV_ClientPrintf ("Kicked by %s\n", who);
+ SV_DropClient (false);
+ }
+
+ host_client = save;
+}
+
+/*
+===============================================================================
+
+DEBUGGING TOOLS
+
+===============================================================================
+*/
+
+/*
+==================
+Host_Give_f
+==================
+*/
+void Host_Give_f (void)
+{
+ char *t;
+ int v;
+
+ if (cmd_source == src_command)
+ {
+ Cmd_ForwardToServer ();
+ return;
+ }
+
+ if (pr_global_struct->deathmatch && !host_client->privileged)
+ return;
+
+ t = Cmd_Argv(1);
+ v = atoi (Cmd_Argv(2));
+
+ switch (t[0])
+ {
+ case 'h':
+ sv_player->v.health = v;
+ break;
+ }
+}
+
+edict_t *FindViewthing (void)
+{
+ int i;
+ edict_t *e;
+
+ for (i=0 ; iv.classname, "viewthing") )
+ return e;
+ }
+ Con_Printf ("No viewthing on map\n");
+ return NULL;
+}
+
+/*
+==================
+Host_Viewmodel_f
+==================
+*/
+void Host_Viewmodel_f (void)
+{
+ edict_t *e;
+ model_t *m;
+
+ e = FindViewthing ();
+ if (!e)
+ return;
+
+ m = Mod_ForName (Cmd_Argv(1), false);
+ if (!m)
+ {
+ Con_Printf ("Can't load %s\n", Cmd_Argv(1));
+ return;
+ }
+
+ e->v.frame = 0;
+ cl.model_precache[(int)e->v.modelindex] = m;
+}
+
+/*
+==================
+Host_Viewframe_f
+==================
+*/
+void Host_Viewframe_f (void)
+{
+ edict_t *e;
+ int f;
+ model_t *m;
+
+ e = FindViewthing ();
+ if (!e)
+ return;
+ m = cl.model_precache[(int)e->v.modelindex];
+
+ f = atoi(Cmd_Argv(1));
+ if (f >= m->numframes)
+ f = m->numframes-1;
+
+ e->v.frame = f;
+}
+
+
+void PrintFrameName (model_t *m, int frame)
+{
+ aliashdr_t *hdr;
+ maliasframedesc_t *pframedesc;
+
+ hdr = (aliashdr_t *)Mod_Extradata (m);
+ if (!hdr)
+ return;
+ pframedesc = &hdr->frames[frame];
+
+ Con_Printf ("frame %i: %s\n", frame, pframedesc->name);
+}
+
+/*
+==================
+Host_Viewnext_f
+==================
+*/
+void Host_Viewnext_f (void)
+{
+ edict_t *e;
+ model_t *m;
+
+ e = FindViewthing ();
+ if (!e)
+ return;
+ m = cl.model_precache[(int)e->v.modelindex];
+
+ e->v.frame = e->v.frame + 1;
+ if (e->v.frame >= m->numframes)
+ e->v.frame = m->numframes - 1;
+
+ PrintFrameName (m, e->v.frame);
+}
+
+/*
+==================
+Host_Viewprev_f
+==================
+*/
+void Host_Viewprev_f (void)
+{
+ edict_t *e;
+ model_t *m;
+
+ e = FindViewthing ();
+ if (!e)
+ return;
+
+ m = cl.model_precache[(int)e->v.modelindex];
+
+ e->v.frame = e->v.frame - 1;
+ if (e->v.frame < 0)
+ e->v.frame = 0;
+
+ PrintFrameName (m, e->v.frame);
+}
+
+/*
+===============================================================================
+
+DEMO LOOP CONTROL
+
+===============================================================================
+*/
+
+
+/*
+==================
+Host_Startdemos_f
+==================
+*/
+void Host_Startdemos_f (void)
+{
+ int i, c;
+
+ if (cls.state == ca_dedicated)
+ {
+ if (!sv.active)
+ Cbuf_AddText ("map start\n");
+ return;
+ }
+
+ c = Cmd_Argc() - 1;
+ if (c > MAX_DEMOS)
+ {
+ Con_Printf ("Max %i demos in demoloop\n", MAX_DEMOS);
+ c = MAX_DEMOS;
+ }
+ Con_Printf ("%i demo(s) in loop\n", c);
+
+ for (i=1 ; i 1, it is autorepeating
+qboolean keydown[256];
+
+void Con_OSK_f (char *input, char *output, int outlen);
+void Con_SetOSKActive(qboolean active);
+qboolean Con_isSetOSKActive(void);
+void Con_OSK_Key (int key);
+
+typedef struct
+{
+ char *name;
+ int keynum;
+} keyname_t;
+
+keyname_t keynames[] =
+{
+ {"TAB", K_TAB},
+ {"ENTER", K_ENTER},
+ {"ESCAPE", K_ESCAPE},
+ {"SPACE", K_SPACE},
+ {"BACKSPACE", K_BACKSPACE},
+ {"UPARROW", K_UPARROW},
+ {"DOWNARROW", K_DOWNARROW},
+ {"LEFTARROW", K_LEFTARROW},
+ {"RIGHTARROW", K_RIGHTARROW},
+ {"SELECT", K_SELECT},
+
+ {"ALT", K_ALT},
+ {"CTRL", K_CTRL},
+ {"SHIFT", K_SHIFT},
+
+ {"F1", K_F1},
+ {"F2", K_F2},
+ {"F3", K_F3},
+ {"F4", K_F4},
+ {"F5", K_F5},
+ {"F6", K_F6},
+ {"F7", K_F7},
+ {"F8", K_F8},
+ {"F9", K_F9},
+ {"F10", K_F10},
+ {"F11", K_F11},
+ {"F12", K_F12},
+
+ {"INS", K_INS},
+ {"DEL", K_DEL},
+ {"PGDN", K_PGDN},
+ {"PGUP", K_PGUP},
+ {"HOME", K_HOME},
+ {"NOTE", K_NOTE},
+ {"SCREEN", K_SCREEN},
+ {"END", K_END},
+
+ {"MOUSE1", K_MOUSE1},
+ {"MOUSE2", K_MOUSE2},
+ {"MOUSE3", K_MOUSE3},
+
+ {"TRIANGLE", K_JOY1},
+ {"CIRCLE", K_JOY2},
+ {"CROSS", K_JOY3},
+ {"SQUARE", K_JOY4},
+ {"LTRIGGER", K_AUX1},
+ {"RTRIGGER", K_AUX2},
+ {"AUX3", K_AUX3},
+ {"AUX4", K_AUX4},
+ {"AUX5", K_AUX5},
+ {"AUX6", K_AUX6},
+ {"AUX7", K_AUX7},
+ {"AUX8", K_AUX8},
+ {"AUX9", K_AUX9},
+ {"AUX10", K_AUX10},
+ {"AUX11", K_AUX11},
+ {"AUX12", K_AUX12},
+ {"AUX13", K_AUX13},
+ {"AUX14", K_AUX14},
+ {"AUX15", K_AUX15},
+ {"AUX16", K_AUX16},
+ {"AUX17", K_AUX17},
+ {"AUX18", K_AUX18},
+ {"AUX19", K_AUX19},
+ {"AUX20", K_AUX20},
+ {"AUX21", K_AUX21},
+ {"AUX22", K_AUX22},
+ {"AUX23", K_AUX23},
+ {"AUX24", K_AUX24},
+ {"AUX25", K_AUX25},
+ {"AUX26", K_AUX26},
+ {"AUX27", K_AUX27},
+ {"AUX28", K_AUX28},
+ {"AUX29", K_AUX29},
+ {"AUX30", K_AUX30},
+ {"AUX31", K_AUX31},
+ {"AUX32", K_AUX32},
+
+ {"PAUSE", K_PAUSE},
+
+ {"MWHEELUP", K_MWHEELUP},
+ {"MWHEELDOWN", K_MWHEELDOWN},
+
+ {"SEMICOLON", ';'}, // because a raw semicolon seperates commands
+
+ {NULL,0}
+};
+
+char consoleInput[MAXCMDLINE];
+qboolean consoleOskDone = false;
+
+/*
+==============================================================================
+
+ LINE TYPING INTO THE CONSOLE
+
+==============================================================================
+*/
+
+
+/*
+====================
+Key_Console
+
+Interactive line editing and console scrollback
+====================
+*/
+extern qboolean console_enabled;
+void Key_Console (int key)
+{
+ char *cmd;
+
+ if (Con_isSetOSKActive())
+ {
+ if (key == K_JOY1)
+ Con_OSK_Key (K_DEL);
+ else if (key == K_JOY2)
+ Con_OSK_Key (K_ESCAPE);
+ else if (key == K_JOY3)
+ Con_OSK_Key (K_ENTER);
+ else if (key == K_JOY4)
+ Con_OSK_Key (K_INS);
+ else
+ Con_OSK_Key (key);
+ return;
+ }
+
+ if ( key == K_JOY4 || key == K_INS)
+ {
+ consoleOskDone = false;
+ Con_SetOSKActive(true);
+ Con_OSK_f(key_lines[edit_line]+1, consoleInput, 72);
+ return;
+ }
+
+ if (key == K_JOY3 || key == K_ENTER)
+ {
+ Cbuf_AddText (key_lines[edit_line]+1); // skip the >
+ Cbuf_AddText ("\n");
+ Con_Printf ("%s\n",key_lines[edit_line]);
+ edit_line = (edit_line + 1) & 31;
+ history_line = edit_line;
+ key_lines[edit_line][0] = ']';
+ key_linepos = 1;
+ if (cls.state == ca_disconnected)
+ SCR_UpdateScreen (); // force an update, because the command
+ // may take some time
+ // for clientside cmds
+ if (cls.state == ca_connected){
+ pr_global_struct->CMD_STRING = (key_lines[edit_line-1]+1 - pr_strings);
+ PR_ExecuteProgram (pr_global_struct->ParseClientCommand);
+ }
+ return;
+ }
+
+
+
+ if (key == K_TAB || key == K_RIGHTARROW)
+ { // command completion
+ cmd = Cmd_CompleteCommand (key_lines[edit_line]+1);
+ if (!cmd)
+ cmd = Cvar_CompleteVariable (key_lines[edit_line]+1);
+ if (cmd)
+ {
+ Q_strcpy (key_lines[edit_line]+1, cmd);
+ key_linepos = Q_strlen(cmd)+1;
+ key_lines[edit_line][key_linepos] = ' ';
+ key_linepos++;
+ key_lines[edit_line][key_linepos] = 0;
+ return;
+ }
+ }
+
+ if (key == K_BACKSPACE || key == K_LEFTARROW)
+ {
+ if (key_linepos > 1)
+ key_linepos--;
+ return;
+ }
+
+ if (key == K_UPARROW)
+ {
+ do
+ {
+ history_line = (history_line - 1) & 31;
+ } while (history_line != edit_line
+ && !key_lines[history_line][1]);
+ if (history_line == edit_line)
+ history_line = (edit_line+1)&31;
+ Q_strcpy(key_lines[edit_line], key_lines[history_line]);
+ key_linepos = Q_strlen(key_lines[edit_line]);
+ return;
+ }
+
+ if (key == K_DOWNARROW)
+ {
+ if (history_line == edit_line) return;
+ do
+ {
+ history_line = (history_line + 1) & 31;
+ }
+ while (history_line != edit_line
+ && !key_lines[history_line][1]);
+ if (history_line == edit_line)
+ {
+ key_lines[edit_line][0] = ']';
+ key_linepos = 1;
+ }
+ else
+ {
+ Q_strcpy(key_lines[edit_line], key_lines[history_line]);
+ key_linepos = Q_strlen(key_lines[edit_line]);
+ }
+ return;
+ }
+
+ if (key == K_PGUP || key==K_MWHEELUP)
+ {
+ con_backscroll += 2;
+ if (con_backscroll > con_totallines - (vid.height>>3) - 1)
+ con_backscroll = con_totallines - (vid.height>>3) - 1;
+ return;
+ }
+
+ if (key == K_PGDN || key==K_MWHEELDOWN)
+ {
+ con_backscroll -= 2;
+ if (con_backscroll < 0)
+ con_backscroll = 0;
+ return;
+ }
+
+ if (key == K_HOME)
+ {
+ con_backscroll = con_totallines - (vid.height>>3) - 1;
+ return;
+ }
+
+ if (key == K_END)
+ {
+ console_enabled = false;
+ con_backscroll = 0;
+ return;
+ }
+
+ if (key < 32 || key > 127)
+ return; // non printable
+
+ if (key_linepos < MAXCMDLINE-1)
+ {
+ key_lines[edit_line][key_linepos] = key;
+ key_linepos++;
+ key_lines[edit_line][key_linepos] = 0;
+ }
+
+}
+
+//============================================================================
+
+char chat_buffer[32];
+qboolean team_message = false;
+
+void Key_Message (int key)
+{
+ static int chat_bufferlen = 0;
+
+ if (key == K_ENTER)
+ {
+ if (team_message)
+ Cbuf_AddText ("say_team \"");
+ else
+ Cbuf_AddText ("say \"");
+ Cbuf_AddText(chat_buffer);
+ Cbuf_AddText("\"\n");
+
+ key_dest = key_game;
+ chat_bufferlen = 0;
+ chat_buffer[0] = 0;
+ return;
+ }
+
+ if (key == K_ESCAPE)
+ {
+ console_enabled = false;
+ key_dest = key_game;
+ chat_bufferlen = 0;
+ chat_buffer[0] = 0;
+ return;
+ }
+
+ if (key < 32 || key > 127)
+ return; // non printable
+
+ if (key == K_BACKSPACE)
+ {
+ if (chat_bufferlen)
+ {
+ chat_bufferlen--;
+ chat_buffer[chat_bufferlen] = 0;
+ }
+ return;
+ }
+
+ if (chat_bufferlen == 31)
+ return; // all full
+
+ chat_buffer[chat_bufferlen++] = key;
+ chat_buffer[chat_bufferlen] = 0;
+}
+
+//============================================================================
+
+
+/*
+===================
+Key_StringToKeynum
+
+Returns a key number to be used to index keybindings[] by looking at
+the given string. Single ascii characters return themselves, while
+the K_* names are matched up.
+===================
+*/
+int Key_StringToKeynum (char *str)
+{
+ keyname_t *kn;
+
+ if (!str || !str[0])
+ return -1;
+ if (!str[1])
+ return str[0];
+
+ for (kn=keynames ; kn->name ; kn++)
+ {
+ if (!Q_strcasecmp(str,kn->name))
+ return kn->keynum;
+ }
+ return -1;
+}
+
+/*
+===================
+Key_KeynumToString
+
+Returns a string (either a single ascii char, or a K_* name) for the
+given keynum.
+FIXME: handle quote special (general escape sequence?)
+===================
+*/
+char *Key_KeynumToString (int keynum)
+{
+ keyname_t *kn;
+ static char tinystr[2];
+
+ if (keynum == -1)
+ return "";
+ if (keynum > 32 && keynum < 127)
+ { // printable ascii
+ tinystr[0] = keynum;
+ tinystr[1] = 0;
+ return tinystr;
+ }
+
+ for (kn=keynames ; kn->name ; kn++)
+ if (keynum == kn->keynum)
+ return kn->name;
+
+ return "";
+}
+
+
+/*
+===================
+Key_SetBinding
+===================
+*/
+void Key_SetBinding (int keynum, char *binding)
+{
+ char *new;
+ int l;
+
+ if (keynum == -1)
+ return;
+
+// free old bindings
+ if (keybindings[keynum])
+ {
+ Z_Free (keybindings[keynum]);
+ keybindings[keynum] = NULL;
+ }
+
+// allocate memory for new binding
+ l = Q_strlen (binding);
+ new = Z_Malloc (l+1);
+ Q_strcpy (new, binding);
+ new[l] = 0;
+ keybindings[keynum] = new;
+}
+
+
+/*
+===================
+Key_SetDTBinding
+===================
+*/
+void Key_SetDTBinding (int keynum, char *binding)
+{
+ char *new;
+ int l;
+
+ if (keynum == -1)
+ return;
+
+// free old bindings
+ if (dtbindings[keynum])
+ {
+ Z_Free (dtbindings[keynum]);
+ dtbindings[keynum] = NULL;
+ }
+
+// allocate memory for new binding
+ l = Q_strlen (binding);
+ new = Z_Malloc (l+1);
+ Q_strcpy (new, binding);
+ new[l] = 0;
+ dtbindings[keynum] = new;
+}
+
+/*
+===================
+Key_Unbind_f
+===================
+*/
+void Key_Unbind_f (void)
+{
+ int b;
+
+ if (Cmd_Argc() != 2)
+ {
+ Con_Printf ("unbind : remove commands from a key\n");
+ return;
+ }
+
+ b = Key_StringToKeynum (Cmd_Argv(1));
+ if (b==-1)
+ {
+ Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
+ return;
+ }
+
+ Key_SetBinding (b, "");
+}
+
+void Key_Unbindall_f (void)
+{
+ int i;
+
+ for (i=0 ; i<256 ; i++)
+ if (keybindings[i])
+ Key_SetBinding (i, "");
+}
+
+
+/*
+===================
+Key_Bind_f
+===================
+*/
+void Key_Bind_f (void)
+{
+ int i, c, b;
+ char cmd[1024];
+
+ c = Cmd_Argc();
+
+ if (c != 2 && c != 3)
+ {
+ Con_Printf ("bind [command] : attach a command to a key\n");
+ return;
+ }
+ b = Key_StringToKeynum (Cmd_Argv(1));
+ if (b==-1)
+ {
+ Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
+ return;
+ }
+
+ if (c == 2)
+ {
+ if (keybindings[b])
+ Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] );
+ else
+ Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) );
+ return;
+ }
+
+// copy the rest of the command line
+ cmd[0] = 0; // start out with a null string
+ for (i=2 ; i< c ; i++)
+ {
+ if (i > 2)
+ strcat (cmd, " ");
+ strcat (cmd, Cmd_Argv(i));
+ }
+
+ Key_SetBinding (b, cmd);
+}
+/*
+===================
+Key_Binddt_f
+===================
+*/
+void Key_Binddt_f (void)
+{
+ int i, c, b;
+ char cmd[1024];
+
+ c = Cmd_Argc();
+
+ if (c != 2 && c != 3)
+ {
+ Con_Printf ("binddt [command] : attach a command to a double tap key\n");
+ return;
+ }
+ b = Key_StringToKeynum (Cmd_Argv(1));
+ if (b==-1)
+ {
+ Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
+ return;
+ }
+
+ if (c == 2)
+ {
+ if (dtbindings[b])
+ Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), dtbindings[b] );
+ else
+ Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) );
+ return;
+ }
+
+// copy the rest of the command line
+ cmd[0] = 0; // start out with a null string
+ for (i=2 ; i< c ; i++)
+ {
+ if (i > 2)
+ strcat (cmd, " ");
+ strcat (cmd, Cmd_Argv(i));
+ }
+
+ Key_SetDTBinding (b, cmd);
+}
+
+/*
+============
+Key_WriteBindings
+
+Writes lines containing "bind key value"
+============
+*/
+void Key_WriteBindings (FILE *f)
+{
+ int i;
+
+ for (i=0 ; i<256 ; i++)
+ if (keybindings[i])
+ if (*keybindings[i])
+ fprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
+}
+
+/*
+============
+Key_WriteDTBindings
+
+Writes lines containing "binddt key value"
+============
+*/
+void Key_WriteDTBindings (FILE *f)
+{
+ int i;
+
+ for (i=0 ; i<256 ; i++)
+ if (dtbindings[i])
+ if (*dtbindings[i])
+ fprintf (f, "binddt \"%s\" \"%s\"\n", Key_KeynumToString(i), dtbindings[i]);
+}
+
+// Added by dr_mabuse1981 {
+void History_Init (void)
+{
+ int i, c;
+ FILE *hf;
+
+ for (i = 0; i < CMDLINES; i++) {
+ key_lines[i][0] = ']';
+ key_lines[i][1] = 0;
+ }
+ key_linepos = 1;
+
+// if (cl_savehistory.value)
+ if ((hf = fopen(HISTORY_FILE_NAME, "rt")))
+ {
+ do
+ {
+ i = 1;
+ do
+ {
+ c = fgetc(hf);
+ key_lines[edit_line][i++] = c;
+ } while (c != '\n' && c != EOF && i < MAXCMDLINE);
+ key_lines[edit_line][i - 1] = 0;
+ edit_line = (edit_line + 1) & (CMDLINES - 1);
+ } while (c != EOF && edit_line < CMDLINES);
+ fclose(hf);
+
+ history_line = edit_line = (edit_line - 1) & (CMDLINES - 1);
+ key_lines[edit_line][0] = ']';
+ key_lines[edit_line][1] = 0;
+ }
+}
+
+void History_Shutdown (void)
+{
+ int i;
+ FILE *hf;
+
+// if (cl_savehistory.value)
+ if ((hf = fopen(HISTORY_FILE_NAME, "wt")))
+ {
+ i = edit_line;
+ do
+ {
+ i = (i + 1) & (CMDLINES - 1);
+ } while (i != edit_line && !key_lines[i][1]);
+
+ do
+ {
+ // fprintf(hf, "%s\n", wcs2str(key_lines[i] + 1)); // Baker: I commented this line out because byte colored text isn't a feature in most ordinary engines
+ fprintf(hf, "%s\n", key_lines[i] + 1);
+ i = (i + 1) & (CMDLINES - 1);
+ } while (i != edit_line && key_lines[i][1]);
+ fclose(hf);
+ }
+}
+// } Added by dr_mabuse1981
+
+
+/*
+===================
+Key_Init
+===================
+*/
+void Key_Init (void)
+{
+ int i;
+
+ History_Init ();
+
+ #if 0 // This section of code is now done in History_Init
+ for (i=0 ; i<32 ; i++)
+ {
+ key_lines[i][0] = ']';
+ key_lines[i][1] = 0;
+ }
+ key_linepos = 1;
+ #endif
+//
+// init ascii characters in console mode
+//
+ for (i=32 ; i<128 ; i++)
+ consolekeys[i] = true;
+ consolekeys[K_ENTER] = true;
+ consolekeys[K_TAB] = true;
+ consolekeys[K_LEFTARROW] = true;
+ consolekeys[K_RIGHTARROW] = true;
+ consolekeys[K_UPARROW] = true;
+ consolekeys[K_DOWNARROW] = true;
+ consolekeys[K_BACKSPACE] = true;
+ consolekeys[K_PGUP] = true;
+ consolekeys[K_PGDN] = true;
+ consolekeys[K_SHIFT] = true;
+ consolekeys[K_MWHEELUP] = true;
+ consolekeys[K_MWHEELDOWN] = true;
+ consolekeys['`'] = false;
+ consolekeys['~'] = false;
+ consolekeys[K_INS] = true;
+ consolekeys[K_JOY1] = true;
+ consolekeys[K_JOY2] = true;
+ consolekeys[K_JOY3] = true;
+ consolekeys[K_JOY4] = true;
+
+ for (i=0 ; i<256 ; i++)
+ keyshift[i] = i;
+ for (i='a' ; i<='z' ; i++)
+ keyshift[i] = i - 'a' + 'A';
+ keyshift['1'] = '!';
+ keyshift['2'] = '@';
+ keyshift['3'] = '#';
+ keyshift['4'] = '$';
+ keyshift['5'] = '%';
+ keyshift['6'] = '^';
+ keyshift['7'] = '&';
+ keyshift['8'] = '*';
+ keyshift['9'] = '(';
+ keyshift['0'] = ')';
+ keyshift['-'] = '_';
+ keyshift['='] = '+';
+ keyshift[','] = '<';
+ keyshift['.'] = '>';
+ keyshift['/'] = '?';
+ keyshift[';'] = ':';
+ keyshift['\''] = '"';
+ keyshift['['] = '{';
+ keyshift[']'] = '}';
+ keyshift['`'] = '~';
+ keyshift['\\'] = '|';
+
+ menubound[K_ESCAPE] = true;
+ for (i=0 ; i<12 ; i++)
+ menubound[K_F1+i] = true;
+
+//
+// register our functions
+//
+ Cmd_AddCommand ("bind",Key_Bind_f);
+ Cmd_AddCommand ("binddt",Key_Binddt_f);
+ Cmd_AddCommand ("unbind",Key_Unbind_f);
+ Cmd_AddCommand ("unbindall",Key_Unbindall_f);
+
+
+}
+
+/*
+===================
+Key_Event
+
+Called by the system between frames for both key up and key down events
+Should NOT be called during an interrupt!
+===================
+*/
+int lastkey;
+double lastkeytime;
+int oldkey;
+double oldkeytime;
+void Key_Event (int key, qboolean down)
+{
+ char *kb;
+ char cmd[1024];
+
+ oldkey = lastkey;
+ keydown[key] = down;
+ lastkey = key;
+ if (Con_isSetOSKActive() && down)
+ {
+ if (key == K_JOY1)
+ Con_OSK_Key (K_DEL);
+ else if (key == K_JOY2)
+ Con_OSK_Key (K_ESCAPE);
+ else if (key == K_JOY3)
+ Con_OSK_Key (K_ENTER);
+ else if (key == K_JOY4)
+ Con_OSK_Key (K_INS);
+ else
+ Con_OSK_Key (key);
+
+ if (!Con_isSetOSKActive())
+ {
+ consoleOskDone = true;
+ strcpy(key_lines[edit_line]+1, consoleInput);
+ key_linepos = Q_strlen(key_lines[edit_line]);
+ consoleOskDone = false;
+ consoleInput[0] = 0;
+ return;
+ }
+ else
+ {
+ return;
+ }
+ }
+ if (!down)
+ key_repeats[key] = 0;
+
+ key_lastpress = key;
+ key_count++;
+ if (key_count <= 0)
+ {
+ return; // just catching keys for Con_NotifyBox
+ }
+
+// update auto-repeat status
+ if (down)
+ {
+ oldkeytime = lastkeytime;
+ lastkeytime = Sys_FloatTime();
+ key_repeats[key]++;
+ if (key != K_BACKSPACE && key != K_PAUSE && key_repeats[key] > 1)
+ {
+ return; // ignore most autorepeats
+ }
+
+ if (key >= 200 && !keybindings[key])
+ Con_Printf ("%s is unbound, hit START to set.\n", Key_KeynumToString (key) );
+ }
+
+ if (key == K_SHIFT)
+ shift_down = down;
+
+//
+// handle escape specialy, so the user can never unbind it
+//
+ if (key == K_ESCAPE)
+ {
+ if (!down)
+ return;
+ switch (key_dest)
+ {
+ case key_message:
+ Key_Message (key);
+ break;
+ case key_menu:
+ case key_menu_pause:
+ M_Keydown (key);
+ break;
+ case key_game:
+ case key_console:
+ console_enabled = false;
+ M_ToggleMenu_f ();
+ break;
+ default:
+ Sys_Error ("Bad key_dest");
+ }
+ return;
+ }
+
+//
+// key up events only generate commands if the game key binding is
+// a button command (leading + sign). These will occur even in console mode,
+// to keep the character from continuing an action started before a console
+// switch. Button commands include the kenum as a parameter, so multiple
+// downs can be matched with ups
+//
+ if (!down)
+ {
+ kb = keybindings[key];
+ if (kb && kb[0] == '+')
+ {
+ sprintf (cmd, "-%s %i\n", kb+1, key);
+ Cbuf_AddText (cmd);
+ }
+ if (keyshift[key] != key)
+ {
+ kb = keybindings[keyshift[key]];
+ if (kb && kb[0] == '+')
+ {
+ sprintf (cmd, "-%s %i\n", kb+1, key);
+ Cbuf_AddText (cmd);
+ }
+ }
+ return;
+ }
+
+//
+// during demo playback, most keys bring up the main menu
+//
+ if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game)
+ {
+ M_ToggleMenu_f ();
+ return;
+ }
+
+//
+// if not a consolekey, send to the interpreter no matter what mode is
+//
+ if ( ((key_dest == key_menu || key_dest == key_menu_pause) && menubound[key])
+ || (key_dest == key_console && !consolekeys[key])
+ || (key_dest == key_game && ( !con_forcedup || !consolekeys[key] ) ) )
+ {
+ if (oldkey == key && ((oldkeytime + 0.3) > lastkeytime))
+ {
+ kb = dtbindings[key];
+ if (kb)
+ {
+ if (kb[0] == '+')
+ { // button commands add keynum as a parm
+ sprintf (cmd, kb, key);
+ Cbuf_AddText (cmd);
+ }
+ else
+ {
+ Cbuf_AddText (kb);
+ Cbuf_AddText ("\n");
+ }
+ }
+ oldkey = 0;
+ oldkeytime = 0;
+ lastkeytime = 0;
+ lastkey = 0;
+ }
+
+ kb = keybindings[key];
+ if (kb)
+ {
+ if (kb[0] == '+')
+ { // button commands add keynum as a parm
+ sprintf (cmd, "%s %i\n", kb, key);
+ Cbuf_AddText (cmd);
+ }
+ else
+ {
+ Cbuf_AddText (kb);
+ Cbuf_AddText ("\n");
+ }
+ }
+ return;
+ }
+
+ if (!down)
+ return; // other systems only care about key down events
+
+ if (shift_down)
+ {
+ key = keyshift[key];
+ }
+
+ switch (key_dest)
+ {
+ case key_message:
+ Key_Message (key);
+ break;
+ case key_menu:
+ case key_menu_pause:
+ M_Keydown (key);
+ break;
+
+ case key_game:
+ case key_console:
+ Key_Console (key);
+ break;
+ default:
+ Sys_Error ("Bad key_dest");
+ }
+}
+
+
+/*
+===================
+Key_ClearStates
+===================
+*/
+void Key_ClearStates (void)
+{
+ int i;
+
+ for (i=0 ; i<256 ; i++)
+ {
+ keydown[i] = false;
+ key_repeats[i] = 0;
+ }
+}
+
diff --git a/source/keys.h b/source/keys.h
new file mode 100644
index 0000000..7dc3f22
--- /dev/null
+++ b/source/keys.h
@@ -0,0 +1,140 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+//
+// these are the key numbers that should be passed to Key_Event
+//
+#define K_TAB 9
+#define K_ENTER 13
+#define K_ESCAPE 27
+#define K_SPACE 32
+
+// normal keys should be passed as lowercased ascii
+
+#define K_BACKSPACE 127
+#define K_UPARROW 128
+#define K_DOWNARROW 129
+#define K_LEFTARROW 130
+#define K_RIGHTARROW 131
+
+#define K_ALT 132
+#define K_CTRL 133
+#define K_SHIFT 134
+#define K_F1 135
+#define K_F2 136
+#define K_F3 137
+#define K_F4 138
+#define K_F5 139
+#define K_F6 140
+#define K_F7 141
+#define K_F8 142
+#define K_F9 143
+#define K_F10 144
+#define K_F11 145
+#define K_F12 146
+#define K_INS 147
+#define K_DEL 148
+#define K_PGDN 149
+#define K_PGUP 150
+#define K_HOME 151
+#define K_END 152
+#define K_SELECT 153
+#define K_NOTE 154
+#define K_SCREEN 155
+
+#define K_PAUSE 255
+
+//
+// mouse buttons generate virtual keys
+//
+#define K_MOUSE1 200
+#define K_MOUSE2 201
+#define K_MOUSE3 202
+
+//
+// joystick buttons
+//
+#define K_JOY1 203
+#define K_JOY2 204
+#define K_JOY3 205
+#define K_JOY4 206
+
+//
+// aux keys are for multi-buttoned joysticks to generate so they can use
+// the normal binding process
+//
+#define K_AUX1 207
+#define K_AUX2 208
+#define K_AUX3 209
+#define K_AUX4 210
+#define K_AUX5 211
+#define K_AUX6 212
+#define K_AUX7 213
+#define K_AUX8 214
+#define K_AUX9 215
+#define K_AUX10 216
+#define K_AUX11 217
+#define K_AUX12 218
+#define K_AUX13 219
+#define K_AUX14 220
+#define K_AUX15 221
+#define K_AUX16 222
+#define K_AUX17 223
+#define K_AUX18 224
+#define K_AUX19 225
+#define K_AUX20 226
+#define K_AUX21 227
+#define K_AUX22 228
+#define K_AUX23 229
+#define K_AUX24 230
+#define K_AUX25 231
+#define K_AUX26 232
+#define K_AUX27 233
+#define K_AUX28 234
+#define K_AUX29 235
+#define K_AUX30 236
+#define K_AUX31 237
+#define K_AUX32 238
+
+// JACK: Intellimouse(c) Mouse Wheel Support
+
+#define K_MWHEELUP 239
+#define K_MWHEELDOWN 240
+
+
+
+typedef enum {key_game, key_console, key_message, key_menu, key_menu_pause} keydest_t;
+
+extern keydest_t key_dest;
+extern char *keybindings[256];
+extern char *dtbindings[256];
+extern int key_repeats[256];
+extern int key_count; // incremented every key event
+extern int key_lastpress;
+extern qboolean keydown[256];
+void Key_Event (int key, qboolean down);
+void Key_Init (void);
+void Key_WriteBindings (FILE *f);
+void Key_WriteDTBindings (FILE *f);
+void Key_SetBinding (int keynum, char *binding);
+void Key_ClearStates (void);
+
+void History_Shutdown (void);
+
diff --git a/source/libpspmath/Makefile b/source/libpspmath/Makefile
new file mode 100644
index 0000000..cb5c443
--- /dev/null
+++ b/source/libpspmath/Makefile
@@ -0,0 +1,62 @@
+TARGET_LIB = libpspmath.a
+OBJS = \
+ printMatrixFloat.o \
+ vfpu_srand.o \
+ vfpu_randf.o \
+ vfpu_rand_8888.o \
+ vfpu_identity_matrix.o \
+ vfpu_translate_matrix.o \
+ vfpu_perspective_matrix.o \
+ vfpu_ortho_matrix.o \
+ vfpu_sinf.o \
+ vfpu_cosf.o \
+ vfpu_tanf.o \
+ vfpu_asinf.o \
+ vfpu_acosf.o \
+ vfpu_atanf.o \
+ vfpu_sinhf.o \
+ vfpu_coshf.o \
+ vfpu_tanhf.o \
+ vfpu_sincos.o \
+ vfpu_expf.o \
+ vfpu_logf.o \
+ vfpu_fabsf.o \
+ vfpu_sqrtf.o \
+ vfpu_powf.o \
+ vfpu_fmodf.o \
+ vfpu_fminf.o \
+ vfpu_fmaxf.o \
+ vfpu_ease_in_out.o \
+ vfpu_normalize_vector.o \
+ vfpu_zero_vector.o \
+ vfpu_scale_vector.o \
+ vfpu_add_vector.o \
+ vfpu_envmap_matrix.o \
+ vfpu_sphere_to_cartesian.o \
+ vfpu_quaternion_identity.o \
+ vfpu_quaternion_copy.o \
+ vfpu_quaternion_multiply.o \
+ vfpu_quaternion_normalize.o \
+ vfpu_quaternion_exp.o \
+ vfpu_quaternion_ln.o \
+ vfpu_quaternion_sample_linear.o \
+ vfpu_quaternion_from_euler.o \
+ vfpu_quaternion_to_matrix.o \
+ vfpu_quaternion_sample_hermite.o \
+ vfpu_quaternion_hermite_tangent.o
+
+INCDIR =
+CFLAGS = -O2 -G0 -Wall
+CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
+ASFLAGS = $(CFLAGS)
+
+LIBDIR =
+LDFLAGS =
+LIBS=
+
+PSPSDK=$(shell psp-config --pspsdk-path)
+include $(PSPSDK)/lib/build.mak
+
+install:
+ cp $(TARGET_LIB) $(PSPSDK)/lib
+ cp pspmath.h $(PSPSDK)/include
\ No newline at end of file
diff --git a/source/libpspmath/README.txt b/source/libpspmath/README.txt
new file mode 100644
index 0000000..67e7f83
--- /dev/null
+++ b/source/libpspmath/README.txt
@@ -0,0 +1,5 @@
+this is a PSP VFPU accelerated math library required if the PSP_VFPU Dflag
+is set. original library by mrmrice, made standalone by tufty
+https://github.com/tufty/libpspmath
+
+- motolegacy (2/3/2021)
diff --git a/source/libpspmath/libpspmath.a b/source/libpspmath/libpspmath.a
new file mode 100644
index 0000000..8c19b40
Binary files /dev/null and b/source/libpspmath/libpspmath.a differ
diff --git a/source/libpspmath/printMatrixFloat.c b/source/libpspmath/printMatrixFloat.c
new file mode 100644
index 0000000..c383a7b
--- /dev/null
+++ b/source/libpspmath/printMatrixFloat.c
@@ -0,0 +1,31 @@
+#include
+#include "pspmath.h"
+
+void printMatrixFloat(int matid) {
+ float m[16];
+
+ #define SV(N) \
+ asm("usv.q R"#N"00, 0 + %0\n" \
+ "usv.q R"#N"01, 16 + %0\n" \
+ "usv.q R"#N"02, 32 + %0\n" \
+ "usv.q R"#N"03, 48 + %0\n" \
+ : "=m"(m))
+
+ switch (matid) {
+ case 0: SV(0); break;
+ case 1: SV(1); break;
+ case 2: SV(2); break;
+ case 3: SV(3); break;
+ case 4: SV(4); break;
+ case 5: SV(5); break;
+ case 6: SV(6); break;
+ case 7: SV(7); break;
+ }
+
+ printf("\n\n");
+ printf(" C%d00 C%d10 C%d20 C%d30\n", matid, matid, matid, matid);
+ printf("R%d00: %0.6f %0.6f %0.6f %0.6f\n", matid, m[0], m[1], m[2], m[3]);
+ printf("R%d01: %0.6f %0.6f %0.6f %0.6f\n", matid, m[4], m[5], m[6], m[7]);
+ printf("R%d02: %0.6f %0.6f %0.6f %0.6f\n", matid, m[8], m[9], m[10], m[11]);
+ printf("R%d03: %0.6f %0.6f %0.6f %0.6f\n", matid, m[12], m[13], m[14], m[15]);
+}
diff --git a/source/libpspmath/pspmath.h b/source/libpspmath/pspmath.h
new file mode 100644
index 0000000..6fe1ab5
--- /dev/null
+++ b/source/libpspmath/pspmath.h
@@ -0,0 +1,445 @@
+/*
+ * PSP Software Development Kit - http://www.pspdev.org
+ * -----------------------------------------------------------------------
+ * Licensed under the BSD license, see LICENSE in PSPSDK root for details.
+ *
+ * Copyright (c) 2007 Steve Galatis
+ * Copyright (c) 2007 Christophe Avoinme
+ *
+ * General purpose math library using vfpu optimized instructions.
+ * Most of the routines here run at least 5 to 20 times faster than the equivalent
+ * routines in libm
+ *
+ */
+
+#ifndef _pspmath_h_
+#define _pspmath_h_
+
+#include
+
+/** @defgroup VFPUMATH VFPU Math Library
+ *
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct quat {
+ float x, y, z, w;
+} __attribute__((aligned(16))) ScePspQuatMatrix;
+
+/** @addtogroup VFPUMATH */
+/*@{*/
+
+/**
+ * Print a formatted block of 16 vfpu registers
+ *
+ * @param blockid - register block id (0 to 7)
+ *
+**/
+void printVFPURegisters(int blockid);
+
+/**
+ * Set vfpu random generator seed
+ *
+ * @param x - seed value
+ *
+**/
+void vfpu_srand(unsigned int x);
+
+/**
+ * Return random float value
+ *
+ * @param min - minimum value to return
+ * @param max - maximum value to return
+ *
+**/
+float vfpu_randf(float min, float max);
+
+/**
+ * Return random color value in 8888 format
+ * This always sets the alpha channel value to 0xFF
+ *
+ * @param min - minimum value for each color channel (0..255)
+ * @param max - maximum value for each color channel (0..255)
+ *
+**/
+unsigned int vfpu_rand_8888(int min, int max);
+
+/**
+ * Generate an identity matrix
+ *
+ * @param m - pointer to matrix to set
+ *
+**/
+void vfpu_identity_matrix(ScePspFMatrix4 *m);
+
+/**
+ * Generate a translation matrix
+ *
+ * @param m - pointer to matrix
+ * @param x - translation on x axis
+ * @param y - translation on y axis
+ * @param z - translation on z axis
+ *
+**/
+void vfpu_translate_matrix(ScePspFMatrix4 *m, float x, float y, float z);
+
+/**
+ * Generate a perspective projection matrix
+ *
+ * @param m - pointer to matrix
+ * @param fovy - vertical field of view
+ * @param aspect - aspect ratio of viewport
+ * @param near - near clipping plane
+ * @param far - far clipping plane
+ *
+**/
+void vfpu_perspective_matrix(ScePspFMatrix4 *m, float fovy, float aspect, float near, float far);
+
+/**
+ * Generate an orthogonal projection matrix
+ *
+ * @param m - pointer to matrix
+ * @param left - coordinate for left edge of viewport
+ * @param right - coordinate for right edge of viewport
+ * @param bottom - coordinate for bottom edge of viewport
+ * @param top - coordinate for top edge of viewport
+ * @param near - near clipping plane
+ * @param far - far clipping plane
+ *
+**/
+void vfpu_ortho_matrix(ScePspFMatrix4 *m, float left, float right, float bottom, float top, float near, float far);
+
+/**
+ * Calculate sine
+ *
+ * @param x - input in radians
+ *
+**/
+float vfpu_sinf(float x);
+
+/**
+ * Calculate cosine
+ *
+ * @param x - input in radians
+ *
+**/
+float vfpu_cosf(float x);
+
+/**
+ * Calculate tangent
+ *
+ * @param x - input in radians
+ *
+**/
+float vfpu_tanf(float x);
+
+/**
+ * Calculate inverse sine (arcsin)
+ *
+ * @param x - input
+ *
+**/
+float vfpu_asinf(float x);
+
+/**
+ * Calculate inverse cosine (arccos)
+ *
+ * @param x - input
+ *
+**/
+float vfpu_acosf(float x);
+
+/**
+ * Calculate inverse tangent (arctan)
+ *
+ * @param x - input
+ *
+**/
+float vfpu_atanf(float x);
+
+/**
+ * Calculate inverse tangent, with proper quadrant fixup
+ *
+ * @param x - input
+ *
+**/
+float vfpu_atan2f(float x, float y);
+
+/**
+ * Calculate hyperbolic sine
+ *
+ * @param x - input
+ *
+**/
+float vfpu_sinhf(float x);
+
+/**
+ * Calculate hyperbolic cosine
+ *
+ * @param x - input
+ *
+**/
+float vfpu_coshf(float x);
+
+/**
+ * Calculate hyperbolic tangent
+ *
+ * @param x - input
+ *
+**/
+float vfpu_tanhf(float x);
+
+/**
+ * Calculate sine and cosine
+ *
+ * @param r - input in radians
+ * @param s - pointer to float for sin
+ * @param c - pointer to float for cos
+**/
+void vfpu_sincos(float r, float *s, float *c);
+
+/**
+ * Calculate exponent of x
+ *
+ * @param x - input
+ *
+**/
+float vfpu_expf(float x);
+
+/**
+ * Calculate logarithm of x
+ *
+ * @param x - input
+ *
+**/
+float vfpu_logf(float x);
+
+/**
+ * Calculate x raised to the power of y
+ *
+ * @param x - number to raise power of
+ * @param y - power to raise x by
+ *
+**/
+float vfpu_powf(float x, float y);
+
+/**
+ * Calculate floating point remainder of x/y
+ *
+ * @param x - input
+ * @param y - input
+ *
+**/
+float vfpu_fmodf(float x, float y);
+
+/**
+ * Calculate absolute floating point of x
+ *
+ * @param x - input
+ *
+**/
+float vfpu_fabsf(float x);
+
+/**
+ * Calculate floating square root point of x
+ *
+ * @param x - input
+ *
+**/
+float vfpu_sqrtf(float x);
+
+/**
+ * Perform a smooth acceleration/deceleration curve based on the input time value
+ * returns 0 to 1
+ *
+ * @param t - input (0 to 1 float)
+ *
+**/
+float vfpu_ease_in_out(float t);
+
+
+/**
+ * Normalize a 3d vector, returning a unit vector of length = 1
+ *
+ * @param v - pointer to vector to normalize
+ *
+**/
+void vfpu_normalize_vector(ScePspFVector4 *v);
+
+/**
+ * Zero a 3d vector
+ *
+ * @param v - pointer to vector
+ *
+**/
+void vfpu_zero_vector(ScePspFVector4 *v);
+
+/**
+ * Scale a 3d vector
+ *
+ * @param vout - pointer to result vector
+ * @param vin - pointer to vector to scale
+ * @param scale - float value to scale vector by
+ *
+**/
+void vfpu_scale_vector(ScePspFVector4 *vout, ScePspFVector4 *vin, float scale);
+
+/**
+ * Add 2 3d vectors
+ *
+ * @param vout - pointer to result vector
+ * @param va - pointer to first vector to add
+ * @param vb - pointer to second vector to add
+**/
+void vfpu_add_vector(ScePspFVector4 *vout, ScePspFVector4 *va, ScePspFVector4 *vb);
+
+
+/**
+ * Generate rotation matrix for environment map.
+ *
+ * @param envmat - pointer to array of 2 vectors to store envmap matrix
+ * @param r - angle to rotate in radians
+**/
+void vfpu_envmap_matrix(ScePspFVector4 *envmat, float r);
+
+/**
+ * Transform 3d vector by 4x4 matrix
+ *
+ * @param m - pointer to transformation matrix
+ * @param vin - pointer to vector to transform
+ * @param vout - pointer to result vector
+**/
+void vfpu_transform_vector(ScePspFMatrix4 *m, ScePspFVector4 *vin, ScePspFVector4 *vout);
+
+/**
+ * Convert input sphere coordinates to cartesian coordinates
+ *
+ * @param az - azimuth angle (0 to PI*2)
+ * @param ze - zenith angle (0 to PI)
+ * @param rad - sphere radius
+ * @param x - pointer to float for x coordinate
+ * @param y - pointer to float for y coordinate
+ * @param z - pointer to float for z coordinate
+ *
+**/
+void vfpu_sphere_to_cartesian(float az, float ze, float rad, float *x, float *y, float *z);
+
+/**
+ * Generate an identity quaternion
+ *
+ * @param q - pointer to quaternion
+ *
+ * this will set the quaternion's components to 0,0,0,1
+ *
+**/
+void vfpu_quaternion_identity(ScePspQuatMatrix *q);
+
+/**
+ * Copy a quaternion
+ *
+ * @param dst - pointer to quaternion to copy to
+ * @param src - pointer to quaternion to copy from
+ *
+**/
+void vfpu_quaternion_copy(ScePspQuatMatrix *dst, ScePspQuatMatrix *src);
+
+/**
+ * Normalize a quaternion
+ *
+ * @param res - pointer to quaternion to normalize
+ *
+**/
+void vfpu_quaternion_normalize(ScePspQuatMatrix *res);
+
+/**
+ * Multiply 2 quaternions
+ *
+ * @param qout - pointer to result quaternion
+ * @param a - pointer to first quaternion to multiply
+ * @param b - pointer to second quaternion to multiply
+ *
+**/
+void vfpu_quaternion_multiply(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b);
+
+/**
+ * Make a quaternion from euler angles
+ *
+ * @param qout - pointer to output quaternion
+ * @param rx - rotation on x axis, in degrees
+ * @param ry - rotation on y axis, in degrees
+ * @param rz - rotation on z axis, in degrees
+**/
+void vfpu_quaternion_from_euler(ScePspQuatMatrix *qout, float rx, float ry, float rz);
+
+/**
+ * Calculate exponent of a quaternion
+ *
+ * @param qout - pointer to output quaternion
+ * @param qin - pointer to input quaternion
+ *
+**/
+void vfpu_quaternion_exp(ScePspQuatMatrix *qout, ScePspQuatMatrix *qin);
+
+/**
+ * Calculate logarithm of a quaternion
+ *
+ * @param qout - pointer to output quaternion
+ * @param qin - pointer to input quaternion
+ *
+**/
+void vfpu_quaternion_ln(ScePspQuatMatrix *qout, ScePspQuatMatrix *qin);
+
+/**
+ * Return a sample from a linear interpolation of 2 quaternions
+ *
+ * @param qout - pointer to output quaternion
+ * @param a - pointer to starting quaternion
+ * @param b - pointer to ending quaternion
+ * @param t - time value to sample, from 0 to 1
+ *
+**/
+void vfpu_quaternion_sample_linear(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b, float t);
+
+/**
+ * Return a sample from a hermite spline interpolation
+ *
+ * @param qout - pointer to output quaternion
+ * @param a - pointer to start quaternion
+ * @param b - pointer to end quaternion
+ * @param at - pointer to tangent point for quaternion a
+ * @param bt - pointer to tangent point for quaternion b
+ * @param t - time value to sample, from 0 to 1
+ *
+**/
+void vfpu_quaternion_sample_hermite(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b, ScePspQuatMatrix *at, ScePspQuatMatrix *bt, float t);
+
+/**
+ * Return a tangent point for hermite spline interpolation
+ *
+ * @param qout - pointer to output quaternion
+ * @param p1 - pointer to p-1 on spline curve for tangent
+ * @param p2 - pointer to p+1 on spline curve for tangent
+ * @param bias - value to scale difference between endpoints.
+ * for example, 0.5 results in a catmull-rom spline tangent
+ *
+**/
+
+void vfpu_quaternion_hermite_tangent(ScePspQuatMatrix *qout, ScePspQuatMatrix *p1, ScePspQuatMatrix *p2, float bias);
+
+/**
+ * Convert quaternion to rotation matrix
+ *
+ * @param q - pointer to input quaternion
+ * @param m - pointer to output matrix
+ *
+**/
+void vfpu_quaternion_to_matrix(ScePspQuatMatrix *q, ScePspFMatrix4 *m);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/source/libpspmath/vfpu_acosf.c b/source/libpspmath/vfpu_acosf.c
new file mode 100644
index 0000000..dc86193
--- /dev/null
+++ b/source/libpspmath/vfpu_acosf.c
@@ -0,0 +1,14 @@
+#include "pspmath.h"
+
+float vfpu_acosf(float x) {
+ float result;
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "vcst.s S001, VFPU_PI_2\n"
+ "vasin.s S000, S000\n"
+ "vocp.s S000, S000\n"
+ "vmul.s S000, S000, S001\n"
+ "mfv %0, S000\n"
+ : "=r"(result) : "r"(x));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_add_vector.c b/source/libpspmath/vfpu_add_vector.c
new file mode 100644
index 0000000..c563e63
--- /dev/null
+++ b/source/libpspmath/vfpu_add_vector.c
@@ -0,0 +1,10 @@
+#include "pspmath.h"
+
+void vfpu_add_vector(ScePspFVector4 *vout, ScePspFVector4 *va, ScePspFVector4 *vb) {
+ __asm__ volatile (
+ "lv.q C000, %1\n"
+ "lv.q C010, %2\n"
+ "vadd.t C020, C000, C010\n"
+ "sv.q C020, %0\n"
+ : "+m"(*vout): "m"(*va), "m"(*vb));
+}
diff --git a/source/libpspmath/vfpu_asinf.c b/source/libpspmath/vfpu_asinf.c
new file mode 100644
index 0000000..b636a1b
--- /dev/null
+++ b/source/libpspmath/vfpu_asinf.c
@@ -0,0 +1,13 @@
+#include "pspmath.h"
+
+float vfpu_asinf(float x) {
+ float result;
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "vcst.s S001, VFPU_PI_2\n"
+ "vasin.s S000, S000\n"
+ "vmul.s S000, S000, S001\n"
+ "mfv %0, S000\n"
+ : "=r"(result) : "r"(x));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_atanf.c b/source/libpspmath/vfpu_atanf.c
new file mode 100644
index 0000000..18c85b0
--- /dev/null
+++ b/source/libpspmath/vfpu_atanf.c
@@ -0,0 +1,57 @@
+#include "pspmath.h"
+
+float vfpu_atanf(float x) {
+ float result;
+ // result = asinf(x/sqrt(x*x+1))
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "vmul.s S001, S000, S000\n"
+ "vadd.s S001, S001, S001[1]\n"
+ "vrsq.s S001, S001\n"
+ "vmul.s S000, S000, S001\n"
+ "vasin.s S000, S000\n"
+ "vcst.s S001, VFPU_PI_2\n"
+ "vmul.s S000, S000, S001\n"
+ "mfv %0, S000\n"
+ : "=r"(result) : "r"(x));
+ return result;
+}
+
+
+#define PI 3.14159265358979f
+#define PI_2 1.57079632679489f
+
+inline static float fabsf(float x) {
+ float r;
+ __asm__ volatile( "abs.s %0, %1" : "=f"(r) :"f"(x):"memory");
+ return r;
+}
+
+/* double a;
+
+ if (fabs(x) >= fabs(y)) {
+ a = atan(y/x) ;
+ if (x < 0.0) {
+ if (y >= 0.0) a += _pi ;
+ else a -= _pi ;
+ }
+ }
+ else {
+ a = -atan(x/y) ;
+ if (y < 0.0) a -= _halfpi ;
+ else a += _halfpi ;
+ }
+ return a ;*/
+
+float vfpu_atan2f(float y, float x) {
+ float r;
+
+ if (fabsf(x) >= fabsf(y)) {
+ r = vfpu_atanf(y/x);
+ if (x < 0.0f) r += (y>=0.0f ? PI : -PI);
+ } else {
+ r = -vfpu_atanf(x/y);
+ r += (y < 0.0f ? -PI_2 : PI_2);
+ }
+ return r;
+}
diff --git a/source/libpspmath/vfpu_cosf.c b/source/libpspmath/vfpu_cosf.c
new file mode 100644
index 0000000..ab4da46
--- /dev/null
+++ b/source/libpspmath/vfpu_cosf.c
@@ -0,0 +1,13 @@
+#include "pspmath.h"
+
+float vfpu_cosf(float rad) {
+ float result;
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "vcst.s S001, VFPU_2_PI\n"
+ "vmul.s S000, S000, S001\n"
+ "vcos.s S000, S000\n"
+ "mfv %0, S000\n"
+ : "=r"(result) : "r"(rad));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_coshf.c b/source/libpspmath/vfpu_coshf.c
new file mode 100644
index 0000000..5396114
--- /dev/null
+++ b/source/libpspmath/vfpu_coshf.c
@@ -0,0 +1,18 @@
+#include "pspmath.h"
+
+float vfpu_coshf(float x) {
+ float result;
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "vcst.s S001, VFPU_LN2\n"
+ "vrcp.s S001, S001\n"
+ "vmov.s S002, S000[|x|]\n"
+ "vmul.s S002, S001, S002\n"
+ "vexp2.s S002, S002\n"
+ "vrcp.s S003, S002\n"
+ "vadd.s S002, S002, S003\n"
+ "vmul.s S002, S002, S002[1/2]\n"
+ "mfv %0, S002\n"
+ : "=r"(result) : "r"(x));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_ease_in_out.c b/source/libpspmath/vfpu_ease_in_out.c
new file mode 100644
index 0000000..ceafb8a
--- /dev/null
+++ b/source/libpspmath/vfpu_ease_in_out.c
@@ -0,0 +1,19 @@
+#include "pspmath.h"
+
+// simple ease in/out function
+// input will be clamped to range of 0 < t < 1
+
+float vfpu_ease_in_out(float t) {
+ float r;
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "vmov.q C000[0:1,,,], C000[x, 2, 1, 1/2]\n" // C000 = [0x.x = 2.0 / dx
+ "vmul.s S111, S111[2], S021\n" // S110 = m->y.y = 2.0 / dy
+ "vmul.s S122, S122[2], S022[-x]\n" // S122 = m->z.z = -2.0 / dz
+ "vsub.t C130, C000[-x,-y,-z], C010\n" // C130 = m->w[x, y, z] = [-(right+left), -(top+bottom), -(far+near)]
+ // we do vsub here since -(a+b) => (-1*a) + (-1*b) => -a - b
+ "vmul.t C130, C130, C020\n" // C130 = [-(right+left)/dx, -(top+bottom)/dy, -(far+near)/dz]
+ "sv.q C100, 0 + %0\n"
+ "sv.q C110, 16 + %0\n"
+ "sv.q C120, 32 + %0\n"
+ "sv.q C130, 48 + %0\n"
+ :"=m"(*m) : "r"(left), "r"(right), "r"(bottom), "r"(top), "r"(near), "r"(far));
+}
+
+
+void vfpu_transform_vector(ScePspFMatrix4 *m, ScePspFVector4 *vin, ScePspFVector4 *vout) {
+ __asm__ volatile (
+ "lv.q C000, 0 + %1\n"
+ "lv.q C010, 16 + %1\n"
+ "lv.q C020, 32 + %1\n"
+ "lv.q C030, 48 + %1\n"
+ "lv.q C100, %2\n"
+ "vtfm4.q C110, M000, C100\n"
+ "sv.q C110, %0\n"
+ :"+m"(*vout): "m"(*m), "m"(*vin));
+}
diff --git a/source/libpspmath/vfpu_perspective_matrix.c b/source/libpspmath/vfpu_perspective_matrix.c
new file mode 100644
index 0000000..b71494d
--- /dev/null
+++ b/source/libpspmath/vfpu_perspective_matrix.c
@@ -0,0 +1,31 @@
+#include "pspmath.h"
+
+void vfpu_perspective_matrix(ScePspFMatrix4 *m, float fovy, float aspect, float near, float far) {
+ __asm__ volatile (
+ "vmzero.q M100\n" // set M100 to all zeros
+ "mtv %1, S000\n" // S000 = fovy
+ "viim.s S001, 90\n" // S002 = 90.0f
+ "vrcp.s S001, S001\n" // S002 = 1/90
+ "vmul.s S000, S000, S000[1/2]\n" // S000 = fovy * 0.5 = fovy/2
+ "vmul.s S000, S000, S001\n" // S000 = (fovy/2)/90
+ "vrot.p C002, S000, [c, s]\n" // S002 = cos(angle), S003 = sin(angle)
+ "vdiv.s S100, S002, S003\n" // S100 = m->x.x = cotangent = cos(angle)/sin(angle)
+ "mtv %3, S001\n" // S001 = near
+ "mtv %4, S002\n" // S002 = far
+ "vsub.s S003, S001, S002\n" // S003 = deltaz = near-far
+ "vrcp.s S003, S003\n" // S003 = 1/deltaz
+ "mtv %2, S000\n" // S000 = aspect
+ "vmov.s S111, S100\n" // S111 = m->y.y = cotangent
+ "vdiv.s S100, S100, S000\n" // S100 = m->x.x = cotangent / aspect
+ "vadd.s S122, S001, S002\n" // S122 = m->z.z = far + near
+ "vmul.s S122, S122, S003\n" // S122 = m->z.z = (far+near)/deltaz
+ "vmul.s S132, S001, S002\n" // S132 = m->w.z = far * near
+ "vmul.s S132, S132, S132[2]\n" // S132 = m->w.z = 2 * (far*near)
+ "vmul.s S132, S132, S003\n" // S132 = m->w.z = 2 * (far*near) / deltaz
+ "vsub.s S123, S123, S123[1]\n" // S123 = m->z.w = -1.0
+ "sv.q C100, 0 + %0\n"
+ "sv.q C110, 16 + %0\n"
+ "sv.q C120, 32 + %0\n"
+ "sv.q C130, 48 + %0\n"
+ :"=m"(*m): "r"(fovy),"r"(aspect),"r"(near),"r"(far));
+}
diff --git a/source/libpspmath/vfpu_powf.c b/source/libpspmath/vfpu_powf.c
new file mode 100644
index 0000000..a9e35d3
--- /dev/null
+++ b/source/libpspmath/vfpu_powf.c
@@ -0,0 +1,15 @@
+#include "pspmath.h"
+
+float vfpu_powf(float x, float y) {
+ float result;
+ // result = exp2f(y * log2f(x));
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "mtv %2, S001\n"
+ "vlog2.s S001, S001\n"
+ "vmul.s S000, S000, S001\n"
+ "vexp2.s S000, S000\n"
+ "mfv %0, S000\n"
+ : "=r"(result) : "r"(x), "r"(y));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_quaternion_copy.c b/source/libpspmath/vfpu_quaternion_copy.c
new file mode 100644
index 0000000..1ec6e14
--- /dev/null
+++ b/source/libpspmath/vfpu_quaternion_copy.c
@@ -0,0 +1,8 @@
+#include "pspmath.h"
+
+void vfpu_quaternion_copy(ScePspQuatMatrix *dst, ScePspQuatMatrix *src) {
+ __asm__ volatile (
+ "lv.q C000, %1\n"
+ "sv.q C000, %0\n"
+ :"+m"(*dst) : "m"(*src));
+}
diff --git a/source/libpspmath/vfpu_quaternion_exp.c b/source/libpspmath/vfpu_quaternion_exp.c
new file mode 100644
index 0000000..c92d45d
--- /dev/null
+++ b/source/libpspmath/vfpu_quaternion_exp.c
@@ -0,0 +1,24 @@
+#include "pspmath.h"
+
+void vfpu_quaternion_exp(ScePspQuatMatrix *qout, ScePspQuatMatrix *qin) {
+ //float r = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);
+ //float et = exp(a[3]);
+ //float s = r>=0.00001f? et*sin(r)/r: 0.f;
+ //return quat(s*a[0],s*a[1],s*a[2],et*cos(r));
+ __asm__ volatile (
+ "lv.q C000, 0 + %1\n" // C000 = [x, y, z, w]
+ "vdot.t S010, C000, C000\n" // S010 = x^2 + y^2 + z^2
+ "vsqrt.s S010, S010\n" // S010 = r = sqrt(x^2 + y^2 + z^2)
+ "vcst.s S011, VFPU_LN2\n" // S011 = ln(2)
+ "vrcp.s S011, S011\n" // S011 = 1/ln(2)
+ "vmul.s S011, S011, S003\n" // S011 = w*(1/ln(2))
+ "vexp2.s S011, S011\n" // S011 = et = exp(w)
+ "vcst.s S012, VFPU_2_PI\n" // S012 = 2/PI
+ "vmul.s S012, S012, S010\n" // S012 = r * 2/PI
+ "vrot.p R003, S012, [c,s]\n" // S003 = cos(r), S013 = sin(r)
+ "vdiv.s S013, S013, S010\n" // S013 = sin(r)/r
+ "vscl.p R003, R003, S011\n" // S003 = et * cos(r), S013 = et * sin(r)/r
+ "vscl.t C000, C000, S013\n" // C000 = [s*x, s*y, s*z, et*cos(r)]
+ "sv.q C000, 0 + %0\n"
+ : "=m"(*qout) : "m"(*qin));
+}
diff --git a/source/libpspmath/vfpu_quaternion_from_euler.c b/source/libpspmath/vfpu_quaternion_from_euler.c
new file mode 100644
index 0000000..82373ae
--- /dev/null
+++ b/source/libpspmath/vfpu_quaternion_from_euler.c
@@ -0,0 +1,34 @@
+#include "pspmath.h"
+
+#define PI 3.14159265358979323846f
+const float piover180 = PI/180.0f;
+
+void vfpu_quaternion_from_euler(ScePspQuatMatrix *res, float x, float y, float z) {
+ __asm__ volatile (
+ "mtv %1, S000\n\t" // S000 = x
+ "mtv %2, S001\n\t" // S001 = y
+ "mtv %3, S002\n\t" // S002 = z
+ "lv.s S010, 0(%4)\n"
+ "vscl.t C000, C000, S010\n\t" // x *= pi/180, y *= pi/180, z *= pi/180
+
+ "vfim.s S010, 0.5\n\t" // S010 = 0.5
+ "vscl.t C000, C000, S010\n\t" // x *= 0.5, y *= 0.5, z *= 0.5
+ "vcst.s S010, VFPU_2_PI\n\t" // load 2/PI into S010, S011 and S012
+ "vscl.t C000, C000, S010\n\t" // x *= 2/PI, y *= 2/pi, z *= pi/2
+
+ "vrot.p C010, S000, [s, c]\n\t" // S010 = sr, S011 = cr
+ "vrot.p C020, S001, [s, c]\n\t" // S020 = sp, S021 = cp
+ "vrot.p C030, S002, [s, c]\n\t" // S030 = sy, S031 = cy
+
+ // fear the madness of prefixes
+ "vmul.q R100, C010[x,y,y,x], C020[y,x,x,y]\n"
+ "vmul.q R100, R100, C030[y,x,y,x]\n"
+ "vmul.q R101, C010[y,x,y,x], C020[y,x,y,x]\n"
+ "vmul.q R101, R101, C030[x,y,y,x]\n"
+ "vadd.q C000, R100[x,z,0,0], R100[-y,w,0,0]\n"
+ "vadd.q C000, C000, R101[0,0,x,z]\n"
+ "vadd.q C000, C000, R101[0,0,-y,w]\n"
+ "usv.q C000, %0\n\t"
+ :"=m"(*res) : "r"(x), "r"(y), "r"(z), "r"(&piover180));
+ vfpu_quaternion_normalize(res);
+}
diff --git a/source/libpspmath/vfpu_quaternion_hermite_tangent.c b/source/libpspmath/vfpu_quaternion_hermite_tangent.c
new file mode 100644
index 0000000..38df7b9
--- /dev/null
+++ b/source/libpspmath/vfpu_quaternion_hermite_tangent.c
@@ -0,0 +1,21 @@
+#include "pspmath.h"
+
+void vfpu_quaternion_hermite_tangent(ScePspQuatMatrix *qout, ScePspQuatMatrix *p1, ScePspQuatMatrix *p2, float bias) {
+ __asm__ volatile (
+ // load p1 and p2
+ "lv.q C000, 0(%1)\n"
+ "lv.q C010, 0(%2)\n"
+
+ // load bias
+ "mtv %3, S100\n"
+
+ // C020 = C010 - C000
+ "vsub.q C020, C010, C000\n"
+
+ // scale C020 by bias
+ "vscl.q C020, C020, S100\n"
+
+ // store result
+ "sv.q C020, 0(%0)\n"
+ :"+r"(qout): "r"(p1), "r"(p2), "r"(bias));
+}
diff --git a/source/libpspmath/vfpu_quaternion_identity.c b/source/libpspmath/vfpu_quaternion_identity.c
new file mode 100644
index 0000000..689f24f
--- /dev/null
+++ b/source/libpspmath/vfpu_quaternion_identity.c
@@ -0,0 +1,8 @@
+#include "pspmath.h"
+
+void vfpu_quaternion_identity(ScePspQuatMatrix *q) {
+ __asm__ volatile (
+ "vidt.q C030\n" // column is important here, we need the w component set to 1
+ "sv.q C030, %0\n"
+ : "+m"(*q));
+}
diff --git a/source/libpspmath/vfpu_quaternion_ln.c b/source/libpspmath/vfpu_quaternion_ln.c
new file mode 100644
index 0000000..77f2370
--- /dev/null
+++ b/source/libpspmath/vfpu_quaternion_ln.c
@@ -0,0 +1,26 @@
+#include "pspmath.h"
+
+void vfpu_quaternion_ln(ScePspQuatMatrix *qout, ScePspQuatMatrix *qin) {
+ //float r = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);
+ //float t = r>0.00001f? atan2(r,a[3])/r: 0.f;
+ //return quat(t*a[0],t*a[1],t*a[2],0.5*log(norm(a)));
+ float r;
+ __asm__ volatile (
+ "lv.q C010, 0 + %1\n"
+ "vdot.t S020, C010, C010\n" // r = x^2 + y^2 + z^2
+ "vsqrt.s S020, S020\n" // r = sqrt(x^2 + y^2 + z^2)
+ "mfv %0, S020\n"
+ :"=r"(r): "m"(*qin));
+ r = vfpu_atan2f(r, qin->w)/r;
+ __asm__ volatile (
+ "mtv %1, S021\n" // t = atan2(r, w) / r
+ "vdot.q S022, C010, C010\n" // norm = x^2 + y^2 + z^2 + w^2
+ "vcst.s S023, VFPU_LOG2E\n"
+ "vrcp.s S023, S023\n"
+ "vlog2.s S013, S022\n"
+ "vmul.s S013, S013, S023\n"
+ "vmul.s S013, S013, S013[1/2]\n"
+ "vscl.t C010, C010, S021\n"
+ "sv.q C010, 0 + %0\n"
+ : "=m"(*qout) : "r"(r));
+}
diff --git a/source/libpspmath/vfpu_quaternion_multiply.c b/source/libpspmath/vfpu_quaternion_multiply.c
new file mode 100644
index 0000000..f86e97b
--- /dev/null
+++ b/source/libpspmath/vfpu_quaternion_multiply.c
@@ -0,0 +1,10 @@
+#include "pspmath.h"
+
+void vfpu_quaternion_multiply(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b) {
+ __asm__ volatile (
+ "lv.q C000, %1\n" // load quaternion a
+ "lv.q C010, %2\n" // load quaternion b
+ "vqmul.q C020, C000, C010\n" // C000 = quat a * quat b (quaternion multiply)
+ "sv.q C020, %0\n" // store result
+ : "+m"(*qout) : "m"(*a), "m"(*b));
+}
diff --git a/source/libpspmath/vfpu_quaternion_normalize.c b/source/libpspmath/vfpu_quaternion_normalize.c
new file mode 100644
index 0000000..37f0673
--- /dev/null
+++ b/source/libpspmath/vfpu_quaternion_normalize.c
@@ -0,0 +1,11 @@
+#include "pspmath.h"
+
+void vfpu_quaternion_normalize(ScePspQuatMatrix *res) {
+ __asm__ volatile (
+ "lv.q C000, %0\n" // load quaternion into C000
+ "vdot.q S010, C000, C000\n" // S010 = x^2 + y^2 + z^2 + w^2
+ "vrsq.s S010, S010\n" // S020 = 1.0 / sqrt(S100)
+ "vscl.q C000, C000, S010\n" // C000 = C000 * S010 (normalized quaternion)
+ "sv.q C000, %0\n" // store into quaternion result
+ : "+m"(*res));
+}
diff --git a/source/libpspmath/vfpu_quaternion_sample_hermite.c b/source/libpspmath/vfpu_quaternion_sample_hermite.c
new file mode 100644
index 0000000..e4cb9ab
--- /dev/null
+++ b/source/libpspmath/vfpu_quaternion_sample_hermite.c
@@ -0,0 +1,67 @@
+#include "pspmath.h"
+
+// constants for the hermite curve functions
+ScePspFMatrix4 hermite = {
+ { 2, -3, 0, 1 },
+ { -2, 3, 0, 0 },
+ { 1, -2, 1, 0 },
+ { 1, -1, 0, 0 }
+};
+
+void vfpu_quaternion_sample_hermite(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b, ScePspQuatMatrix *at, ScePspQuatMatrix *bt, float t) {
+ __asm__ volatile (
+ // load hermite transform matrix
+ "lv.q C000, 0(%6)\n"
+ "lv.q C010, 16(%6)\n"
+ "lv.q C020, 32(%6)\n"
+ "lv.q C030, 48(%6)\n"
+
+ // load a, b, at, bt
+ "lv.q C100, 0(%1)\n"
+ "lv.q C110, 0(%2)\n"
+ "lv.q C120, 0(%3)\n"
+ "lv.q C130, 0(%4)\n"
+
+ // C200 = [ t^3, t^2, t, 1]
+ "mtv %5, S202\n"
+ "vmul.s S201, S202, S202\n"
+ "vmul.s S200, S202, S201\n"
+ "vone.s S203\n"
+
+ // multiply M000 by C200
+ // C000 = [ 2*t^3, -3*t^2, 0, 1]
+ // C010 = [ -2*t^3, 3*t^2, 0, 0]
+ // C020 = [ t^3, -2*t^2, t, 0]
+ // C030 = [ t^3, -t^2, 0, 0]
+
+ "vmul.q C000, C000, C200\n"
+ "vmul.q C010, C010, C200\n"
+ "vmul.q C020, C020, C200\n"
+ "vmul.q C030, C030, C200\n"
+
+ // sum the terms
+ // S210 = 2*t^3 - 3*t^2 + 1
+ // S211 = -2*t^3 + 3*t^2
+ // S212 = t^3 - 2*t^2 + t
+ // S213 = t^3 - t^2
+
+ "vfad.q S210, C000\n"
+ "vfad.q S211, C010\n"
+ "vfad.q S212, C020\n"
+ "vfad.q S213, C030\n"
+
+ // scale the qaternions with terms
+ "vscl.q C100, C100, S210\n"
+ "vscl.q C110, C110, S211\n"
+ "vscl.q C120, C120, S212\n"
+ "vscl.q C130, C130, S213\n"
+
+ // sum the results
+ "vadd.q C100, C100, C110\n"
+ "vadd.q C100, C100, C120\n"
+ "vadd.q C100, C100, C130\n"
+
+ // and return results
+ "sv.q C100, 0(%0)\n"
+ :"+r"(qout): "r"(a), "r"(b), "r"(at), "r"(bt), "r"(t), "r"(&hermite));
+}
diff --git a/source/libpspmath/vfpu_quaternion_sample_linear.c b/source/libpspmath/vfpu_quaternion_sample_linear.c
new file mode 100644
index 0000000..efe33ba
--- /dev/null
+++ b/source/libpspmath/vfpu_quaternion_sample_linear.c
@@ -0,0 +1,14 @@
+#include "pspmath.h"
+
+void vfpu_quaternion_sample_linear(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b, float t) {
+ __asm__ volatile (
+ "lv.q C000, 0 + %1\n"
+ "lv.q C010, 0 + %2\n"
+ "mtv %3, S020\n"
+ "vocp.s S021, S020\n"
+ "vscl.q C000, C000, S021\n"
+ "vscl.q C010, C010, S020\n"
+ "vadd.q C000, C000, C010\n"
+ "sv.q C000, 0 + %0\n"
+ : "=m"(*qout) : "m"(*a), "m"(*b), "r"(t));
+}
diff --git a/source/libpspmath/vfpu_quaternion_to_matrix.c b/source/libpspmath/vfpu_quaternion_to_matrix.c
new file mode 100644
index 0000000..a236cb5
--- /dev/null
+++ b/source/libpspmath/vfpu_quaternion_to_matrix.c
@@ -0,0 +1,34 @@
+#include "pspmath.h"
+
+void vfpu_quaternion_to_matrix(ScePspQuatMatrix *q, ScePspFMatrix4 *m) {
+ __asm__ volatile (
+ "lv.q C000, %1\n" // C000 = [x, y, z, w ]
+ "vmul.q C010, C000, C000\n" // C010 = [x2, y2, z2, w2]
+ "vcrs.t C020, C000, C000\n" // C020 = [yz, xz, xy ]
+ "vmul.q C030, C000[x,y,z,1], C000[w,w,w,2]\n" // C030 = [wx, wy, wz ]
+
+ "vadd.q C100, C020[0,z,y,0], C030[0,z,-y,0]\n" // C100 = [0, xy+wz, xz-wy]
+ "vadd.s S100, S011, S012\n" // C100 = [y2+z2, xy+wz, xz-wy]
+
+ "vadd.q C110, C020[z,0,x,0], C030[-z,0,x,0]\n" // C110 = [xy-wz, 0, yz+wx]
+ "vadd.s S111, S010, S012\n" // C110 = [xy-wz, x2+z2, yz+wx]
+
+ "vadd.q C120, C020[y,x,0,0], C030[y,-x,0,0]\n" // C120 = [xz+wy, yz-wx, 0 ]
+ "vadd.s S122, S010, S011\n" // C120 = [xz+wy, yz-wx, x2+y2]
+
+ "vmscl.t M100, M100, S033\n" // C100 = [2*(y2+z2), 2*(xy+wz), 2*(xz-wy)]
+ // C110 = [2*(xy-wz), 2*(x2+z2), 2*(yz+wx)]
+ // C120 = [2*(xz+wy), 2*(yz-wx), 2*(x2+y2)]
+
+ "vocp.s S100, S100\n" // C100 = [1-2*(y2+z2), 2*(xy+wz), 2*(xz-wy) ]
+ "vocp.s S111, S111\n" // C110 = [2*(xy-wz), 1-2*(x2+z2), 2*(yz+wx) ]
+ "vocp.s S122, S122\n" // C120 = [2*(xz+wy), 2*(yz-wx), 1-2*(x2+y2)]
+
+ "vidt.q C130\n" // C130 = [0, 0, 0, 1]
+
+ "sv.q R100, 0 + %0\n"
+ "sv.q R101, 16 + %0\n"
+ "sv.q R102, 32 + %0\n"
+ "sv.q R103, 48 + %0\n"
+ : "=m"(*m) : "m"(*q));
+}
diff --git a/source/libpspmath/vfpu_rand_8888.c b/source/libpspmath/vfpu_rand_8888.c
new file mode 100644
index 0000000..a68e8ab
--- /dev/null
+++ b/source/libpspmath/vfpu_rand_8888.c
@@ -0,0 +1,24 @@
+#include "pspmath.h"
+
+unsigned int vfpu_rand_8888(int min, int max) {
+ unsigned int result;
+ __asm__ volatile (
+ "mtv %1, S020\n"
+ "mtv %2, S021\n"
+ "vmov.t C000, C020[x, x, x]\n"
+ "vmov.t C010, C020[y, y, y]\n"
+ "vi2f.t C000, C000, 0\n"
+ "vi2f.t C010, C010, 0\n"
+ "vsub.t C010, C010, C000\n"
+ "vrndf1.t C020\n"
+ "vsub.t C020, C020, C020[1, 1, 1]\n"
+ "vmul.t C020, C020, C010\n"
+ "vadd.t C020, C020, C000\n"
+ "vf2iz.t C020, C020, 23\n"
+ "viim.s S023, 255\n"
+ "vf2iz.s S023, S023, 23\n"
+ "vi2uc.q S000, C020\n"
+ "mfv %0, S000\n"
+ :"=r"(result): "r"(min), "r"(max));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_randf.c b/source/libpspmath/vfpu_randf.c
new file mode 100644
index 0000000..d951ecb
--- /dev/null
+++ b/source/libpspmath/vfpu_randf.c
@@ -0,0 +1,17 @@
+#include "pspmath.h"
+
+float vfpu_randf(float min, float max) {
+ float result;
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "mtv %2, S001\n"
+ "vsub.s S001, S001, S000\n"
+ "vrndf1.s S002\n"
+ "vone.s S003\n"
+ "vsub.s S002, S002, S003\n"
+ "vmul.s S001, S002, S001\n"
+ "vadd.s S000, S000, S001\n"
+ "mfv %0, S000\n"
+ : "=r"(result) : "r"(min), "r"(max));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_scale_vector.c b/source/libpspmath/vfpu_scale_vector.c
new file mode 100644
index 0000000..2e57ca9
--- /dev/null
+++ b/source/libpspmath/vfpu_scale_vector.c
@@ -0,0 +1,10 @@
+#include "pspmath.h"
+
+void vfpu_scale_vector(ScePspFVector4 *vout, ScePspFVector4 *vin, float scale) {
+ __asm__ volatile (
+ "lv.q C000, %1\n"
+ "mtv %2, S010\n"
+ "vscl.t C000, C000, S010\n"
+ "sv.q C000, %0\n"
+ : "=m"(*vout) : "m"(*vin), "r"(scale));
+}
diff --git a/source/libpspmath/vfpu_sincos.c b/source/libpspmath/vfpu_sincos.c
new file mode 100644
index 0000000..9ca1279
--- /dev/null
+++ b/source/libpspmath/vfpu_sincos.c
@@ -0,0 +1,12 @@
+#include "pspmath.h"
+
+void vfpu_sincos(float r, float *s, float *c) {
+ __asm__ volatile (
+ "mtv %2, S002\n"
+ "vcst.s S003, VFPU_2_PI\n"
+ "vmul.s S002, S002, S003\n"
+ "vrot.p C000, S002, [s, c]\n"
+ "mfv %0, S000\n"
+ "mfv %1, S001\n"
+ : "=r"(*s), "=r"(*c): "r"(r));
+}
diff --git a/source/libpspmath/vfpu_sinf.c b/source/libpspmath/vfpu_sinf.c
new file mode 100644
index 0000000..1bbd517
--- /dev/null
+++ b/source/libpspmath/vfpu_sinf.c
@@ -0,0 +1,13 @@
+#include "pspmath.h"
+
+float vfpu_sinf(float rad) {
+ float result;
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "vcst.s S001, VFPU_2_PI\n"
+ "vmul.s S000, S000, S001\n"
+ "vsin.s S000, S000\n"
+ "mfv %0, S000\n"
+ : "=r"(result) : "r"(rad));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_sinhf.c b/source/libpspmath/vfpu_sinhf.c
new file mode 100644
index 0000000..5cd8e8a
--- /dev/null
+++ b/source/libpspmath/vfpu_sinhf.c
@@ -0,0 +1,20 @@
+#include "pspmath.h"
+
+float vfpu_sinhf(float x) {
+ float result;
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "vcst.s S001, VFPU_LN2\n"
+ "vrcp.s S001, S001\n"
+ "vmov.s S002, S000[|x|]\n"
+ "vcmp.s NE, S000, S002\n"
+ "vmul.s S002, S001, S002\n"
+ "vexp2.s S002, S002\n"
+ "vrcp.s S003, S002\n"
+ "vsub.s S002, S002, S003\n"
+ "vmul.s S002, S002, S002[1/2]\n"
+ "vcmov.s S002, S002[-x], 0\n"
+ "mfv %0, S002\n"
+ : "=r"(result) : "r"(x));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_sphere_to_cartesian.c b/source/libpspmath/vfpu_sphere_to_cartesian.c
new file mode 100644
index 0000000..93c634e
--- /dev/null
+++ b/source/libpspmath/vfpu_sphere_to_cartesian.c
@@ -0,0 +1,20 @@
+#include "pspmath.h"
+
+void vfpu_sphere_to_cartesian(float az, float ze, float rad, float *x, float *y, float *z) {
+ __asm__ volatile (
+ "mtv %3, S000\n"
+ "mtv %4, S001\n"
+ "mtv %5, S002\n"
+ "vcst.s S003, VFPU_2_PI\n" // C000 = [az, ze, rad, 2/pi]
+ "vscl.p C000, C000, S003\n" // C000 = [az*2/pi, ze*2/pi, rad, 2/pi]
+ "vrot.p C010, S000, [s, c]\n" // C010 = [sin(az), cos(az), ?, ?]
+ "vrot.p C012, S001, [s, c]\n" // C010 = [sin(az), cos(az), sin(ze), cos(ze)]
+ "vmul.q C020, C010[y, 1, x, 0], C010[z, w, z, 0]\n" // C020 = [0, cos(az)*sin(ez), cos(ze), sin(az)*sin(ze)]
+ "vscl.t C020, C020, S002\n" // C020 = [0, r*cos(az)*sin(ez), r*cos(ze), r*sin(az)*sin(ze)]
+ //"sv.q C020, 0 + %0\n"
+ "sv.s S020, %0\n"
+ "sv.s S021, %1\n"
+ "sv.s S022, %2\n"
+ :"+m"(*x), "+m"(*y), "+m"(*z)
+ :"r"(az), "r"(ze), "r"(rad));
+}
diff --git a/source/libpspmath/vfpu_sqrtf.c b/source/libpspmath/vfpu_sqrtf.c
new file mode 100644
index 0000000..38bfba0
--- /dev/null
+++ b/source/libpspmath/vfpu_sqrtf.c
@@ -0,0 +1,11 @@
+#include "pspmath.h"
+
+float vfpu_sqrtf(float x) {
+ float result;
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "vsqrt.s S000, S000\n"
+ "mfv %0, S000\n"
+ : "=r"(result) : "r"(x));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_srand.c b/source/libpspmath/vfpu_srand.c
new file mode 100644
index 0000000..7fbfc70
--- /dev/null
+++ b/source/libpspmath/vfpu_srand.c
@@ -0,0 +1,5 @@
+#include "pspmath.h"
+
+void vfpu_srand(unsigned int x) {
+ __asm__ volatile ( "mtv %0, S000\n vrnds.s S000" : "=r"(x));
+}
diff --git a/source/libpspmath/vfpu_tanf.c b/source/libpspmath/vfpu_tanf.c
new file mode 100644
index 0000000..492ed65
--- /dev/null
+++ b/source/libpspmath/vfpu_tanf.c
@@ -0,0 +1,15 @@
+#include "pspmath.h"
+
+float vfpu_tanf(float x) {
+ float result;
+ // result = sin(x)/cos(x);
+ __asm__ volatile (
+ "mtv %1, S000\n"
+ "vcst.s S001, VFPU_2_PI\n"
+ "vmul.s S000, S000, S001\n"
+ "vrot.p C002, S000, [s, c]\n"
+ "vdiv.s S000, S002, S003\n"
+ "mfv %0, S000\n"
+ : "=r"(result) : "r"(x));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_tanhf.c b/source/libpspmath/vfpu_tanhf.c
new file mode 100644
index 0000000..ac41642
--- /dev/null
+++ b/source/libpspmath/vfpu_tanhf.c
@@ -0,0 +1,20 @@
+#include "pspmath.h"
+
+float vfpu_tanhf(float x) {
+ float result;
+ //y = exp(x+x);
+ //return (y-1)/(y+1);
+ __asm__ volatile (
+ "mtv %0, S000\n"
+ "vadd.s S000, S000, S000\n"
+ "vcst.s S001, VFPU_LN2\n"
+ "vrcp.s S001, S001\n"
+ "vmul.s S000, S000, S001\n"
+ "vexp2.s S000, S000\n"
+ "vone.s S001\n"
+ "vbfy1.p C002, C000\n"
+ "vdiv.s S000, S003, S002\n"
+ "mfv %0, S000\n"
+ : "=r"(result): "r"(x));
+ return result;
+}
diff --git a/source/libpspmath/vfpu_translate_matrix.c b/source/libpspmath/vfpu_translate_matrix.c
new file mode 100644
index 0000000..ec613b7
--- /dev/null
+++ b/source/libpspmath/vfpu_translate_matrix.c
@@ -0,0 +1,19 @@
+#include "pspmath.h"
+
+void vfpu_translate_matrix(ScePspFMatrix4 *m, float x, float y, float z)
+{
+ __asm__ volatile (
+ "vmidt.q M000\n"
+ "mtv %1, S030\n"
+ "mtv %2, S031\n"
+ "mtv %3, S032\n"
+ //"vmidt.q M100\n"
+ //"lv.q C200, %1\n"
+ //"vmov.t C130, C200\n"
+ //"vmmul.q M200, M100, M000\n"
+ "sv.q C000, 0 + %0\n"
+ "sv.q C010, 16 + %0\n"
+ "sv.q C020, 32 + %0\n"
+ "sv.q C030, 48 + %0\n"
+ : "=m"(*m) : "r"(x), "r"(y), "r"(z));
+}
diff --git a/source/libpspmath/vfpu_zero_vector.c b/source/libpspmath/vfpu_zero_vector.c
new file mode 100644
index 0000000..afbc88d
--- /dev/null
+++ b/source/libpspmath/vfpu_zero_vector.c
@@ -0,0 +1,8 @@
+#include "pspmath.h"
+
+void vfpu_zero_vector(ScePspFVector4 *v) {
+ __asm__ volatile (
+ "vzero.t C000\n"
+ "sv.q C000, %0\n"
+ : "+m"(*v));
+}
diff --git a/source/mathlib.c b/source/mathlib.c
new file mode 100644
index 0000000..d864e14
--- /dev/null
+++ b/source/mathlib.c
@@ -0,0 +1,953 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// mathlib.c -- math primitives
+
+#include
+#include "quakedef.h"
+
+#ifdef PSP_VFPU
+#include
+#endif
+
+void Sys_Error (char *error, ...);
+
+vec3_t vec3_origin = {0,0,0};
+int nanmask = 255<<23;
+
+int _mathlib_temp_int1, _mathlib_temp_int2, _mathlib_temp_int3;
+float _mathlib_temp_float1, _mathlib_temp_float2, _mathlib_temp_float3;
+vec3_t _mathlib_temp_vec1, _mathlib_temp_vec2, _mathlib_temp_vec3;
+
+
+/*-----------------------------------------------------------------*/
+
+float rsqrt( float number )
+{
+#ifdef PSP_VFPU
+ float d;
+ __asm__ ( //from official pspsdk by sony
+ ".set push\n" // save assember option
+ ".set noreorder\n" // suppress reordering
+ "lv.s s000, %1\n" // s000 = s
+ "vrsq.s s000, s000\n" // s000 = 1 / sqrt(s000)
+ "sv.s s000, %0\n" // d = s000
+ ".set pop\n" // restore assember option
+ : "=m"(d)
+ : "m"(number)
+ );
+ return d;
+#else
+ int i;
+ float x, y;
+
+ if( number == 0.0f )
+ return 0.0f;
+
+ x = number * 0.5f;
+ i = *(int *)&number; // evil floating point bit level hacking
+ i = 0x5f3759df - (i >> 1); // what the fuck?
+ y = *(float *)&i;
+ y = y * (1.5f - (x * y * y)); // first iteration
+
+ return y;
+#endif
+}
+
+/*
+=================
+SinCos
+=================
+*/
+void SinCos( float radians, float *sine, float *cosine )
+{
+ #ifdef PSP_VFPU
+ vfpu_sincos(radians,sine,cosine);
+ #else
+ __asm__ volatile (
+ "mtv %2, S002\n"
+ "vcst.s S003, VFPU_2_PI\n"
+ "vmul.s S002, S002, S003\n"
+ "vrot.p C000, S002, [s, c]\n"
+ "mfv %0, S000\n"
+ "mfv %1, S001\n"
+ : "=r"(*sine), "=r"(*cosine): "r"(radians));
+ #endif
+}
+
+void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal )
+{
+ float d;
+ vec3_t n;
+ float inv_denom;
+
+ inv_denom = 1.0F / DotProduct( normal, normal );
+
+ d = DotProduct( normal, p ) * inv_denom;
+
+ n[0] = normal[0] * inv_denom;
+ n[1] = normal[1] * inv_denom;
+ n[2] = normal[2] * inv_denom;
+
+ dst[0] = p[0] - d * n[0];
+ dst[1] = p[1] - d * n[1];
+ dst[2] = p[2] - d * n[2];
+}
+
+/*
+** assumes "src" is normalized
+*/
+void PerpendicularVector( vec3_t dst, const vec3_t src )
+{
+ int pos;
+ int i;
+ float minelem = 1.0F;
+ vec3_t tempvec;
+
+ /*
+ ** find the smallest magnitude axially aligned vector
+ */
+ for ( pos = 0, i = 0; i < 3; i++ )
+ {
+ #ifdef PSP_VFPU
+ if ( vfpu_fabsf( src[i] ) < minelem )
+ #else
+ if ( fabsf( src[i] ) < minelem )
+ #endif
+ {
+ pos = i;
+ #ifdef PSP_VFPU
+ minelem = vfpu_fabsf( src[i] );
+ #else
+ minelem = fabsf( src[i] );
+ #endif
+ }
+ }
+ tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
+ tempvec[pos] = 1.0F;
+
+ /*
+ ** project the point onto the plane defined by src
+ */
+ ProjectPointOnPlane( dst, tempvec, src );
+
+ /*
+ ** normalize the result
+ */
+ VectorNormalize( dst );
+}
+
+
+void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees )
+{
+ float m[3][3];
+ float im[3][3];
+ float zrot[3][3];
+ float tmpmat[3][3];
+ float rot[3][3];
+ int i;
+ vec3_t vr, vup, vf;
+
+ vf[0] = dir[0];
+ vf[1] = dir[1];
+ vf[2] = dir[2];
+
+ PerpendicularVector( vr, dir );
+ CrossProduct( vr, vf, vup );
+
+ m[0][0] = vr[0];
+ m[1][0] = vr[1];
+ m[2][0] = vr[2];
+
+ m[0][1] = vup[0];
+ m[1][1] = vup[1];
+ m[2][1] = vup[2];
+
+ m[0][2] = vf[0];
+ m[1][2] = vf[1];
+ m[2][2] = vf[2];
+
+ memcpy( im, m, sizeof( im ) );
+
+ im[0][1] = m[1][0];
+ im[0][2] = m[2][0];
+ im[1][0] = m[0][1];
+ im[1][2] = m[2][1];
+ im[2][0] = m[0][2];
+ im[2][1] = m[1][2];
+
+ memset( zrot, 0, sizeof( zrot ) );
+ zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;
+
+ #ifdef PSP_VFPU
+ zrot[0][0] = vfpu_cosf( DEG2RAD( degrees ) );
+ zrot[0][1] = vfpu_sinf( DEG2RAD( degrees ) );
+ zrot[1][0] = -vfpu_sinf( DEG2RAD( degrees ) );
+ zrot[1][1] = vfpu_cosf( DEG2RAD( degrees ) );
+ #else
+ zrot[0][0] = cosf( DEG2RAD( degrees ) );
+ zrot[0][1] = sinf( DEG2RAD( degrees ) );
+ zrot[1][0] = -sinf( DEG2RAD( degrees ) );
+ zrot[1][1] = cosf( DEG2RAD( degrees ) );
+ #endif
+
+ R_ConcatRotations( m, zrot, tmpmat );
+ R_ConcatRotations( tmpmat, im, rot );
+
+ for ( i = 0; i < 3; i++ )
+ {
+ dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2];
+ }
+}
+
+/*-----------------------------------------------------------------*/
+
+
+float anglemod(float a)
+{
+#if 0
+ if (a >= 0)
+ a -= 360*(int)(a/360);
+ else
+ a += 360*( 1 + (int)(-a/360) );
+#endif
+ a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535);
+ return a;
+}
+
+/*
+==================
+BOPS_Error
+
+Split out like this for ASM to call.
+==================
+*/
+void BOPS_Error (void)
+{
+ Sys_Error ("BoxOnPlaneSide: Bad signbits");
+}
+
+/*
+==================
+BoxOnPlaneSide
+
+Returns 1, 2, or 1 + 2
+==================
+*/
+// crow_bar's enhanced boxonplaneside
+int BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, mplane_t *p)
+{
+#ifdef PSP_VFPU
+ int sides;
+ __asm__ (
+ ".set push\n" // save assembler option
+ ".set noreorder\n" // suppress reordering
+ "lv.s S000, 0 + %[normal]\n" // S000 = p->normal[0]
+ "lv.s S001, 4 + %[normal]\n" // S001 = p->normal[1]
+ "lv.s S002, 8 + %[normal]\n" // S002 = p->normal[2]
+ "vzero.p C030\n" // C030 = [0.0f, 0.0f]
+ "lv.s S032, %[dist]\n" // S032 = p->dist
+ "move $8, $0\n" // $8 = 0
+ "beq %[signbits], $8, 0f\n" // jump to 0
+ "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot )
+ "beq %[signbits], $8, 1f\n" // jump to 1
+ "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot )
+ "beq %[signbits], $8, 2f\n" // jump to 2
+ "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot )
+ "beq %[signbits], $8, 3f\n" // jump to 3
+ "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot )
+ "beq %[signbits], $8, 4f\n" // jump to 4
+ "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot )
+ "beq %[signbits], $8, 5f\n" // jump to 5
+ "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot )
+ "beq %[signbits], $8, 6f\n" // jump to 6
+ "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot )
+ "beq %[signbits], $8, 7f\n" // jump to 7
+ "nop\n" // ( delay slot )
+ "j 8f\n" // jump to SetSides
+ "nop\n" // ( delay slot )
+ "0:\n"
+/*
+ dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+ dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+*/
+ "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0]
+ "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1]
+ "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2]
+ "lv.s S020, 0 + %[emins]\n" // S020 = emins[0]
+ "lv.s S021, 4 + %[emins]\n" // S021 = emins[1]
+ "lv.s S022, 8 + %[emins]\n" // S022 = emins[2]
+ "vdot.t S030, C000, C010\n" // S030 = C000 * C010
+ "vdot.t S031, C000, C020\n" // S030 = C000 * C020
+ "j 8f\n" // jump to SetSides
+ "nop\n" // ( delay slot )
+ "1:\n"
+/*
+ dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+ dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+*/
+ "lv.s S010, 0 + %[emins]\n" // S010 = emins[0]
+ "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1]
+ "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2]
+ "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0]
+ "lv.s S021, 4 + %[emins]\n" // S021 = emins[1]
+ "lv.s S022, 8 + %[emins]\n" // S022 = emins[2]
+ "vdot.t S030, C000, C010\n" // S030 = C000 * C010
+ "vdot.t S031, C000, C020\n" // S030 = C000 * C020
+ "j 8f\n" // jump to SetSides
+ "nop\n" // ( delay slot )
+ "2:\n"
+/*
+ dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+ dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+*/
+ "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0]
+ "lv.s S011, 4 + %[emins]\n" // S011 = emins[1]
+ "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2]
+ "lv.s S020, 0 + %[emins]\n" // S020 = emins[0]
+ "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1]
+ "lv.s S022, 8 + %[emins]\n" // S022 = emins[2]
+ "vdot.t S030, C000, C010\n" // S030 = C000 * C010
+ "vdot.t S031, C000, C020\n" // S030 = C000 * C020
+ "j 8f\n" // jump to SetSides
+ "nop\n" // ( delay slot )
+ "3:\n"
+/*
+ dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+ dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+*/
+ "lv.s S010, 0 + %[emins]\n" // S010 = emins[0]
+ "lv.s S011, 4 + %[emins]\n" // S011 = emins[1]
+ "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2]
+ "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0]
+ "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1]
+ "lv.s S022, 8 + %[emins]\n" // S022 = emins[2]
+ "vdot.t S030, C000, C010\n" // S030 = C000 * C010
+ "vdot.t S031, C000, C020\n" // S030 = C000 * C020
+ "j 8f\n" // jump to SetSides
+ "nop\n" // ( delay slot )
+ "4:\n"
+/*
+ dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+ dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+*/
+ "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0]
+ "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1]
+ "lv.s S012, 8 + %[emins]\n" // S012 = emins[2]
+ "lv.s S020, 0 + %[emins]\n" // S020 = emins[0]
+ "lv.s S021, 4 + %[emins]\n" // S021 = emins[1]
+ "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2]
+ "vdot.t S030, C000, C010\n" // S030 = C000 * C010
+ "vdot.t S031, C000, C020\n" // S030 = C000 * C020
+ "j 8f\n" // jump to SetSides
+ "nop\n" // ( delay slot )
+ "5:\n"
+/*
+ dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+ dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+*/
+ "lv.s S010, 0 + %[emins]\n" // S010 = emins[0]
+ "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1]
+ "lv.s S012, 8 + %[emins]\n" // S012 = emins[2]
+ "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0]
+ "lv.s S021, 4 + %[emins]\n" // S021 = emins[1]
+ "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2]
+ "vdot.t S030, C000, C010\n" // S030 = C000 * C010
+ "vdot.t S031, C000, C020\n" // S030 = C000 * C020
+ "j 8f\n" // jump to SetSides
+ "nop\n" // ( delay slot )
+ "6:\n"
+/*
+ dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+ dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+*/
+ "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0]
+ "lv.s S011, 4 + %[emins]\n" // S011 = emins[1]
+ "lv.s S012, 8 + %[emins]\n" // S012 = emins[2]
+ "lv.s S020, 0 + %[emins]\n" // S020 = emins[0]
+ "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1]
+ "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2]
+ "vdot.t S030, C000, C010\n" // S030 = C000 * C010
+ "vdot.t S031, C000, C020\n" // S030 = C000 * C020
+ "j 8f\n" // jump to SetSides
+ "nop\n" // ( delay slot )
+ "7:\n"
+/*
+ dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+ dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+*/
+ "lv.s S010, 0 + %[emins]\n" // S010 = emins[0]
+ "lv.s S011, 4 + %[emins]\n" // S011 = emins[1]
+ "lv.s S012, 8 + %[emins]\n" // S012 = emins[2]
+ "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0]
+ "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1]
+ "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2]
+ "vdot.t S030, C000, C010\n" // S030 = C000 * C010
+ "vdot.t S031, C000, C020\n" // S030 = C000 * C020
+ "8:\n" // SetSides
+/*
+ if( dist1 >= p->dist )
+ sides = 1;
+ if( dist2 < p->dist )
+ sides |= 2;
+*/
+ "addiu %[sides], $0, 0\n" // sides = 0
+ "vcmp.s LT, S030, S032\n" // S030 < S032
+ "bvt 0, 9f\n" // if ( CC[0] == 1 ) jump to 9
+ "nop\n" // ( delay slot )
+ "addiu %[sides], %[sides], 1\n"// sides = 1
+ "9:\n"
+ "vcmp.s GE, S031, S032\n" // S031 >= S032
+ "bvt 0, 10f\n" // if ( CC[0] == 1 ) jump to 10
+ "nop\n" // ( delay slot )
+ "addiu %[sides], %[sides], 2\n"// sides = sides + 2
+ "10:\n"
+ ".set pop\n" // restore assembler option
+ : [sides] "=r" ( sides )
+ : [normal] "m" (*(p->normal)),
+ [emaxs] "m" ( *emaxs ),
+ [emins] "m" ( *emins ),
+ [signbits] "r" ( p->signbits ),
+ [dist] "m" ( p->dist )
+ : "$8"
+ );
+ return sides;
+#else
+ int sides = 0;
+ float dist1, dist2;
+
+ // general case
+ switch( p->signbits )
+ {
+ case 0:
+ dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+ dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+ break;
+ case 1:
+ dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+ dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+ break;
+ case 2:
+ dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+ dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+ break;
+ case 3:
+ dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+ dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+ break;
+ case 4:
+ dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+ dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+ break;
+ case 5:
+ dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+ dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+ break;
+ case 6:
+ dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+ dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+ break;
+ case 7:
+ dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+ dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+ break;
+ default:
+ // shut up compiler
+ dist1 = dist2 = 0;
+ break;
+ }
+
+ if( dist1 >= p->dist )
+ sides = 1;
+ if( dist2 < p->dist )
+ sides |= 2;
+
+ return sides;
+#endif
+}
+
+
+void vectoangles (vec3_t vec, vec3_t ang)
+{
+ float forward, yaw, pitch;
+
+ if (!vec[1] && !vec[0])
+ {
+ yaw = 0;
+ pitch = (vec[2] > 0) ? 90 : 270;
+ }
+ else
+ {
+ #ifdef PSP_VFPU
+ yaw = vec[0] ? (vfpu_atan2f(vec[1], vec[0]) * 180 / M_PI) : (vec[1] > 0) ? 90 : 270;
+ #else
+ yaw = vec[0] ? (atan2(vec[1], vec[0]) * 180 / M_PI) : (vec[1] > 0) ? 90 : 270;
+ #endif
+ if (yaw < 0)
+ yaw += 360;
+
+ #ifdef PSP_VFPU
+ forward = vfpu_sqrtf (vec[0] * vec[0] + vec[1] * vec[1]);
+ pitch = vfpu_atan2f (vec[2], forward) * 180 / M_PI;
+ #else
+ forward = sqrt (vec[0] * vec[0] + vec[1] * vec[1]);
+ pitch = atan2 (vec[2], forward) * 180 / M_PI;
+ #endif
+ if (pitch < 0)
+ pitch += 360;
+ }
+
+ ang[0] = pitch;
+ ang[1] = yaw;
+ ang[2] = 0;
+}
+
+void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
+{
+ float angle;
+ float sr, sp, sy, cr, cp, cy;
+
+ angle = angles[YAW] * (M_PI*2 / 360);
+ #ifdef PSP_VFPU
+ sy = vfpu_sinf(angle);
+ cy = vfpu_cosf(angle);
+ #else
+ sy = sinf(angle);
+ cy = cosf(angle);
+ #endif
+ angle = angles[PITCH] * (M_PI*2 / 360);
+ #ifdef PSP_VFPU
+ sp = vfpu_sinf(angle);
+ cp = vfpu_cosf(angle);
+ #else
+ sp = sinf(angle);
+ cp = cosf(angle);
+ #endif
+ angle = angles[ROLL] * (M_PI*2 / 360);
+ #ifdef PSP_VFPU
+ sr = vfpu_sinf(angle);
+ cr = vfpu_cosf(angle);
+ #else
+ sr = sinf(angle);
+ cr = cosf(angle);
+ #endif
+
+ forward[0] = cp*cy;
+ forward[1] = cp*sy;
+ forward[2] = -sp;
+ right[0] = (-1*sr*sp*cy+-1*cr*-sy);
+ right[1] = (-1*sr*sp*sy+-1*cr*cy);
+ right[2] = -1*sr*cp;
+ up[0] = (cr*sp*cy+-sr*-sy);
+ up[1] = (cr*sp*sy+-sr*cy);
+ up[2] = cr*cp;
+}
+
+float VectorLength (vec3_t v)
+{
+ #ifdef PSP_VFPU
+ return vfpu_sqrtf(DotProduct(v, v));
+ #else
+ return sqrtf(DotProduct(v, v));
+ #endif
+}
+
+int VectorCompare (vec3_t v1, vec3_t v2)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ if (v1[i] != v2[i])
+ return 0;
+
+ return 1;
+}
+
+void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc)
+{
+ vecc[0] = veca[0] + scale*vecb[0];
+ vecc[1] = veca[1] + scale*vecb[1];
+ vecc[2] = veca[2] + scale*vecb[2];
+}
+
+
+vec_t _DotProduct (vec3_t v1, vec3_t v2)
+{
+ return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out)
+{
+ out[0] = veca[0]-vecb[0];
+ out[1] = veca[1]-vecb[1];
+ out[2] = veca[2]-vecb[2];
+}
+
+void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out)
+{
+ out[0] = veca[0]+vecb[0];
+ out[1] = veca[1]+vecb[1];
+ out[2] = veca[2]+vecb[2];
+}
+
+void _VectorCopy (vec3_t in, vec3_t out)
+{
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
+{
+ cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+vec_t Length(vec3_t v)
+{
+ #ifdef PSP_VFPU
+ return vfpu_sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ #else
+ return sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ #endif
+}
+
+float VecLength2(vec3_t v1, vec3_t v2)
+{
+ vec3_t k;
+ VectorSubtract(v1, v2, k);
+ return sqrt(k[0]*k[0] + k[1]*k[1] + k[2]*k[2]);
+}
+
+float VectorNormalize (vec3_t v)
+{
+ #ifdef PSP_VFPU
+ float length = vfpu_sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ #else
+ float length = sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ #endif
+
+ if (length)
+ {
+ const float ilength = 1.0f / length;
+ v[0] *= ilength;
+ v[1] *= ilength;
+ v[2] *= ilength;
+ }
+
+ return length;
+
+}
+
+void VectorInverse (vec3_t v)
+{
+ v[0] = -v[0];
+ v[1] = -v[1];
+ v[2] = -v[2];
+}
+
+void VectorScale (vec3_t in, vec_t scale, vec3_t out)
+{
+ out[0] = in[0]*scale;
+ out[1] = in[1]*scale;
+ out[2] = in[2]*scale;
+}
+
+
+int Q_log2(int val)
+{
+ int answer=0;
+ while (val>>=1)
+ answer++;
+ return answer;
+}
+
+
+/*
+================
+R_ConcatRotations
+================
+*/
+void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3])
+{
+ out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
+ in1[0][2] * in2[2][0];
+ out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
+ in1[0][2] * in2[2][1];
+ out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
+ in1[0][2] * in2[2][2];
+ out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
+ in1[1][2] * in2[2][0];
+ out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
+ in1[1][2] * in2[2][1];
+ out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
+ in1[1][2] * in2[2][2];
+ out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
+ in1[2][2] * in2[2][0];
+ out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
+ in1[2][2] * in2[2][1];
+ out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
+ in1[2][2] * in2[2][2];
+}
+
+
+/*
+================
+R_ConcatTransforms
+================
+*/
+void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4])
+{
+ out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
+ in1[0][2] * in2[2][0];
+ out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
+ in1[0][2] * in2[2][1];
+ out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
+ in1[0][2] * in2[2][2];
+ out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +
+ in1[0][2] * in2[2][3] + in1[0][3];
+ out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
+ in1[1][2] * in2[2][0];
+ out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
+ in1[1][2] * in2[2][1];
+ out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
+ in1[1][2] * in2[2][2];
+ out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +
+ in1[1][2] * in2[2][3] + in1[1][3];
+ out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
+ in1[2][2] * in2[2][0];
+ out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
+ in1[2][2] * in2[2][1];
+ out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
+ in1[2][2] * in2[2][2];
+ out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +
+ in1[2][2] * in2[2][3] + in1[2][3];
+}
+
+
+/*
+===================
+FloorDivMod
+
+Returns mathematically correct (floor-based) quotient and remainder for
+numer and denom, both of which should contain no fractional part. The
+quotient must fit in 32 bits.
+====================
+*/
+
+void FloorDivMod (float numer, float denom, int *quotient,
+ int *rem)
+{
+ int q, r;
+ float x;
+
+ if (denom <= 0.0)
+ Sys_Error ("FloorDivMod: bad denominator %d\n", denom);
+
+ if (numer >= 0.0)
+ {
+
+ x = floorf(numer / denom);
+ q = (int)x;
+ r = (int)floorf(numer - (x * denom));
+ }
+ else
+ {
+ //
+ // perform operations with positive values, and fix mod to make floor-based
+ //
+ x = floorf(-numer / denom);
+ q = -(int)x;
+ r = (int)floorf(-numer - (x * denom));
+ if (r != 0)
+ {
+ q--;
+ r = (int)denom - r;
+ }
+ }
+
+ *quotient = q;
+ *rem = r;
+}
+
+
+/*
+===================
+GreatestCommonDivisor
+====================
+*/
+int GreatestCommonDivisor (int i1, int i2)
+{
+ if (i1 > i2)
+ {
+ if (i2 == 0)
+ return (i1);
+ return GreatestCommonDivisor (i2, i1 % i2);
+ }
+ else
+ {
+ if (i1 == 0)
+ return (i2);
+ return GreatestCommonDivisor (i1, i2 % i1);
+ }
+}
+
+
+#if !id386
+
+// TODO: move to nonintel.c
+
+/*
+===================
+Invert24To16
+
+Inverts an 8.24 value to a 16.16 value
+====================
+*/
+
+fixed16_t Invert24To16(fixed16_t val)
+{
+ if (val < 256)
+ return (0xFFFFFFFF);
+
+ return (fixed16_t)
+ (((float)0x10000 * (float)0x1000000 / (float)val) + 0.5);
+}
+
+#endif
+
+void VectorTransform (const vec3_t in1, matrix3x4 in2, vec3_t out)
+{
+ out[0] = DotProduct(in1, in2[0]) + in2[0][3];
+ out[1] = DotProduct(in1, in2[1]) + in2[1][3];
+ out[2] = DotProduct(in1, in2[2]) + in2[2][3];
+}
+
+void AngleQuaternion( const vec3_t angles, vec4_t quaternion )
+{
+ float angle;
+ float sr, sp, sy, cr, cp, cy;
+
+ // FIXME: rescale the inputs to 1/2 angle
+ angle = angles[2] * 0.5;
+ #ifdef PSP_VFPU
+ sy = vfpu_sinf(angle);
+ cy = vfpu_cosf(angle);
+ #else
+ sy = sin(angle);
+ cy = cos(angle);
+ #endif
+ angle = angles[1] * 0.5;
+ #ifdef PSP_VFPU
+ sp = vfpu_sinf(angle);
+ cp = vfpu_cosf(angle);
+ #else
+ sp = sin(angle);
+ cp = cos(angle);
+ #endif
+ angle = angles[0] * 0.5;
+ #ifdef PSP_VFPU
+ sr = vfpu_sinf(angle);
+ cr = vfpu_cosf(angle);
+ #else
+ sr = sin(angle);
+ cr = cos(angle);
+ #endif
+
+ quaternion[0] = sr*cp*cy-cr*sp*sy; // X
+ quaternion[1] = cr*sp*cy+sr*cp*sy; // Y
+ quaternion[2] = cr*cp*sy-sr*sp*cy; // Z
+ quaternion[3] = cr*cp*cy+sr*sp*sy; // W
+}
+
+void QuaternionMatrix( const vec4_t quaternion, float (*matrix)[4] )
+{
+
+ matrix[0][0] = 1.0 - 2.0 * quaternion[1] * quaternion[1] - 2.0 * quaternion[2] * quaternion[2];
+ matrix[1][0] = 2.0 * quaternion[0] * quaternion[1] + 2.0 * quaternion[3] * quaternion[2];
+ matrix[2][0] = 2.0 * quaternion[0] * quaternion[2] - 2.0 * quaternion[3] * quaternion[1];
+
+ matrix[0][1] = 2.0 * quaternion[0] * quaternion[1] - 2.0 * quaternion[3] * quaternion[2];
+ matrix[1][1] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[2] * quaternion[2];
+ matrix[2][1] = 2.0 * quaternion[1] * quaternion[2] + 2.0 * quaternion[3] * quaternion[0];
+
+ matrix[0][2] = 2.0 * quaternion[0] * quaternion[2] + 2.0 * quaternion[3] * quaternion[1];
+ matrix[1][2] = 2.0 * quaternion[1] * quaternion[2] - 2.0 * quaternion[3] * quaternion[0];
+ matrix[2][2] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[1] * quaternion[1];
+}
+
+void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt )
+{
+ int i;
+ float omega, cosom, sinom, sclp, sclq;
+
+ // decide if one of the quaternions is backwards
+ float a = 0;
+ float b = 0;
+ for (i = 0; i < 4; i++) {
+ a += (p[i]-q[i])*(p[i]-q[i]);
+ b += (p[i]+q[i])*(p[i]+q[i]);
+ }
+ if (a > b) {
+ for (i = 0; i < 4; i++) {
+ q[i] = -q[i];
+ }
+ }
+
+ cosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3];
+
+ if ((1.0 + cosom) > 0.00000001) {
+ if ((1.0 - cosom) > 0.00000001) {
+ omega = acos( cosom );
+ #ifdef PSP_VFPU
+ sinom = vfpu_sinf( omega );
+ sclp = vfpu_sinf( (1.0 - t)*omega) / sinom;
+ sclq = vfpu_sinf( t*omega ) / sinom;
+ #else
+ sinom = sin( omega );
+ sclp = sin( (1.0 - t)*omega) / sinom;
+ sclq = sin( t*omega ) / sinom;
+ #endif
+ }
+ else {
+ sclp = 1.0 - t;
+ sclq = t;
+ }
+ for (i = 0; i < 4; i++) {
+ qt[i] = sclp * p[i] + sclq * q[i];
+ }
+ }
+ else {
+ qt[0] = -p[1];
+ qt[1] = p[0];
+ qt[2] = -p[3];
+ qt[3] = p[2];
+ #ifdef PSP_VFPU
+ sclp = vfpu_sinf( (1.0 - t) * 0.5 * M_PI);
+ sclq = vfpu_sinf( t * 0.5 * M_PI);
+ #else
+ sclp = sin( (1.0 - t) * 0.5 * M_PI);
+ sclq = sin( t * 0.5 * M_PI);
+ #endif
+ for (i = 0; i < 3; i++) {
+ qt[i] = sclp * p[i] + sclq * qt[i];
+ }
+ }
+}
diff --git a/source/mathlib.h b/source/mathlib.h
new file mode 100644
index 0000000..b5247d8
--- /dev/null
+++ b/source/mathlib.h
@@ -0,0 +1,201 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// mathlib.h
+#ifdef PSP_VFPU
+#include
+#endif
+
+typedef float vec_t;
+typedef vec_t vec2_t[2];
+typedef vec_t vec3_t[3];
+typedef vec_t vec4_t[4];
+typedef vec_t vec5_t[5];
+
+typedef byte byte_vec4_t[4];
+
+typedef int fixed4_t;
+typedef int fixed8_t;
+typedef int fixed16_t;
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h
+#endif
+
+struct mplane_s;
+
+extern vec3_t vec3_origin;
+extern int nanmask;
+
+/*
+#ifndef M_PI
+#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h
+#endif
+
+#define M_PI_DIV_180 (M_PI / 180.0) //johnfitz
+//#define DEG2RAD( a ) ( a * M_PI ) / 180.0F
+#define DEG2RAD( a ) ( (a) * M_PI_DIV_180 ) //johnfitz
+
+//#else
+*/
+#ifndef M_PI
+#define M_PI = GU_PI // matches value in gcc v2 math.h
+#endif
+
+#define M_PI_DIV_180 (M_PI / 180.0) //johnfitz
+//#define DEG2RAD( a ) ( a * M_PI ) / 180.0F
+#define DEG2RAD( a ) ( (a) * M_PI_DIV_180 ) //johnfitz
+//#endif
+
+#define CLAMP(min, x, max) ((x) < (min) ? (min) : (x) > (max) ? (max) : (x)) //johnfitz
+
+#define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
+
+#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)
+
+#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
+#define VectorSubtract(a,b,c) {(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];(c)[2]=(a)[2]-(b)[2];}
+#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
+#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];}
+#define VectorClear(a) ((a)[0] = (a)[1] = (a)[2] = 0)
+#define VectorNegate(a, b) ((b)[0] = -(a)[0], (b)[1] = -(a)[1], (b)[2] = -(a)[2])
+#define VectorSet(v, x, y, z) ((v)[0] = (x), (v)[1] = (y), (v)[2] = (z))
+#define VectorRandom(v) {do{(v)[0] = lhrandom(-1, 1);(v)[1] = lhrandom(-1, 1);(v)[2] = lhrandom(-1, 1);}while(DotProduct(v, v) > 1);}
+
+#define VSM(a,b,c) {c[0]=a[0]*b;c[1]=a[1]*b;c[2]=a[2]*b;}
+
+// MDave -- courtesy of johnfitz, lordhavoc
+#define VectorNormalizeFast(_v)\
+{\
+ float _y, _number;\
+ _number = DotProduct(_v, _v);\
+ if (_number != 0.0)\
+ {\
+ *((long *)&_y) = 0x5f3759df - ((* (long *) &_number) >> 1);\
+ _y = _y * (1.5f - (_number * 0.5f * _y * _y));\
+ VectorScale(_v, _y, _v);\
+ }\
+}
+
+typedef float matrix3x4[3][4];
+typedef float matrix3x3[3][3];
+
+void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc);
+
+vec_t _DotProduct (vec3_t v1, vec3_t v2);
+void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out);
+void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out);
+void _VectorCopy (vec3_t in, vec3_t out);
+
+void vectoangles (vec3_t vec, vec3_t ang);
+
+int VectorCompare (vec3_t v1, vec3_t v2);
+vec_t Length (vec3_t v);
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
+float VectorLength (vec3_t v);
+float VecLength2(vec3_t v1, vec3_t v2);
+float VectorNormalize (vec3_t v); // returns vector length
+void VectorInverse (vec3_t v);
+void VectorScale (vec3_t in, vec_t scale, vec3_t out);
+int Q_log2(int val);
+
+void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]);
+void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]);
+
+void FloorDivMod (float numer, float denom, int *quotient,
+ int *rem);
+fixed16_t Invert24To16(fixed16_t val);
+int GreatestCommonDivisor (int i1, int i2);
+
+void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
+int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct mplane_s *plane);
+float anglemod(float a);
+
+#define VectorL2Compare(v, w, m) \
+ (_mathlib_temp_float1 = (m) * (m), \
+ _mathlib_temp_vec1[0] = (v)[0] - (w)[0], _mathlib_temp_vec1[1] = (v)[1] - (w)[1], _mathlib_temp_vec1[2] = (v)[2] - (w)[2],\
+ _mathlib_temp_vec1[0] * _mathlib_temp_vec1[0] + \
+ _mathlib_temp_vec1[1] * _mathlib_temp_vec1[1] + \
+ _mathlib_temp_vec1[2] * _mathlib_temp_vec1[2] < _mathlib_temp_float1)
+
+
+#define VectorSupCompare(v, w, m) \
+ (_mathlib_temp_float1 = m, \
+ (v)[0] - (w)[0] > -_mathlib_temp_float1 && (v)[0] - (w)[0] < _mathlib_temp_float1 && \
+ (v)[1] - (w)[1] > -_mathlib_temp_float1 && (v)[1] - (w)[1] < _mathlib_temp_float1 && \
+ (v)[2] - (w)[2] > -_mathlib_temp_float1 && (v)[2] - (w)[2] < _mathlib_temp_float1)
+
+/*
+#define VectorNormalizeFast(_v) \
+do { \
+ _mathlib_temp_float1 = DotProduct((_v), (_v)); \
+ if (_mathlib_temp_float1) { \
+ _mathlib_temp_float2 = 0.5f * _mathlib_temp_float1; \
+ _mathlib_temp_int1 = *((int *) &_mathlib_temp_float1); \
+ _mathlib_temp_int1 = 0x5f375a86 - (_mathlib_temp_int1 >> 1); \
+ _mathlib_temp_float1 = *((float *) &_mathlib_temp_int1); \
+ _mathlib_temp_float1 = _mathlib_temp_float1 * (1.5f - _mathlib_temp_float2 * _mathlib_temp_float1 * _mathlib_temp_float1); \
+ VectorScale((_v), _mathlib_temp_float1, (_v)) \
+ } \
+} while (0);
+*/
+
+#define BOX_ON_PLANE_SIDE(emins, emaxs, p) \
+ (((p)->type < 3)? \
+ ( \
+ ((p)->dist <= (emins)[(p)->type])? \
+ 1 \
+ : \
+ ( \
+ ((p)->dist >= (emaxs)[(p)->type])?\
+ 2 \
+ : \
+ 3 \
+ ) \
+ ) \
+ : \
+ BoxOnPlaneSide( (emins), (emaxs), (p)))
+
+#define VectorInterpolate(v1, _frac, v2, v) \
+do { \
+ _mathlib_temp_float1 = _frac; \
+ \
+ (v)[0] = (v1)[0] + _mathlib_temp_float1 * ((v2)[0] - (v1)[0]);\
+ (v)[1] = (v1)[1] + _mathlib_temp_float1 * ((v2)[1] - (v1)[1]);\
+ (v)[2] = (v1)[2] + _mathlib_temp_float1 * ((v2)[2] - (v1)[2]);\
+} while(0)
+
+#define FloatInterpolate(f1, _frac, f2) \
+ (_mathlib_temp_float1 = _frac, \
+ (f1) + _mathlib_temp_float1 * ((f2) - (f1)))
+
+#define PlaneDist(point, plane) ( \
+ (plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal) \
+)
+
+#define PlaneDiff(point, plane) ( \
+ (((plane)->type < 3) ? (point)[(plane)->type] - (plane)->dist : DotProduct((point), (plane)->normal) - (plane)->dist) \
+)
+
+// Prototypes added by PM.
+void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees );
+void VectorTransform (const vec3_t in1, matrix3x4 in2, vec3_t out);
+extern int _mathlib_temp_int1, _mathlib_temp_int2, _mathlib_temp_int3;
+extern float _mathlib_temp_float1, _mathlib_temp_float2, _mathlib_temp_float3;
+extern vec3_t _mathlib_temp_vec1, _mathlib_temp_vec2, _mathlib_temp_vec3;
\ No newline at end of file
diff --git a/source/matrixlib.c b/source/matrixlib.c
new file mode 100644
index 0000000..df32c1d
--- /dev/null
+++ b/source/matrixlib.c
@@ -0,0 +1,532 @@
+/*
+matrixlib.c - internal matrixlib
+Copyright (C) 2010 Uncle Mike
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+*/
+
+#include "quakedef.h"
+
+
+const matrix3x4 matrix3x4_identity =
+{
+{ 1, 0, 0, 0 }, // PITCH [forward], org[0]
+{ 0, 1, 0, 0 }, // YAW [right] , org[1]
+{ 0, 0, 1, 0 }, // ROLL [up] , org[2]
+};
+
+/*
+========================================================================
+
+ Matrix3x4 operations
+
+========================================================================
+*/
+void Matrix3x4_VectorTransform( const matrix3x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + in[0][3];
+ out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + in[1][3];
+ out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] + in[2][3];
+}
+
+void Matrix3x4_VectorITransform( const matrix3x4 in, const float v[3], float out[3] )
+{
+ vec3_t dir;
+
+ dir[0] = v[0] - in[0][3];
+ dir[1] = v[1] - in[1][3];
+ dir[2] = v[2] - in[2][3];
+
+ out[0] = dir[0] * in[0][0] + dir[1] * in[1][0] + dir[2] * in[2][0];
+ out[1] = dir[0] * in[0][1] + dir[1] * in[1][1] + dir[2] * in[2][1];
+ out[2] = dir[0] * in[0][2] + dir[1] * in[1][2] + dir[2] * in[2][2];
+}
+
+void Matrix3x4_VectorRotate( const matrix3x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2];
+ out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2];
+ out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2];
+}
+
+void Matrix3x4_VectorIRotate( const matrix3x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[1][0] + v[2] * in[2][0];
+ out[1] = v[0] * in[0][1] + v[1] * in[1][1] + v[2] * in[2][1];
+ out[2] = v[0] * in[0][2] + v[1] * in[1][2] + v[2] * in[2][2];
+}
+
+void Matrix3x4_ConcatTransforms( matrix3x4 out, const matrix3x4 in1, const matrix3x4 in2 )
+{
+ out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
+ out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
+ out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2];
+ out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3];
+ out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0];
+ out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1];
+ out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2];
+ out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3];
+ out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0];
+ out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1];
+ out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2];
+ out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3];
+}
+
+void Matrix3x4_SetOrigin( matrix3x4 out, float x, float y, float z )
+{
+ out[0][3] = x;
+ out[1][3] = y;
+ out[2][3] = z;
+}
+
+void Matrix3x4_OriginFromMatrix( const matrix3x4 in, float *out )
+{
+ out[0] = in[0][3];
+ out[1] = in[1][3];
+ out[2] = in[2][3];
+}
+
+void Matrix3x4_FromOriginQuat( matrix3x4 out, const vec4_t quaternion, const vec3_t origin )
+{
+ out[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2];
+ out[1][0] = 2.0f * quaternion[0] * quaternion[1] + 2.0f * quaternion[3] * quaternion[2];
+ out[2][0] = 2.0f * quaternion[0] * quaternion[2] - 2.0f * quaternion[3] * quaternion[1];
+
+ out[0][1] = 2.0f * quaternion[0] * quaternion[1] - 2.0f * quaternion[3] * quaternion[2];
+ out[1][1] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[2] * quaternion[2];
+ out[2][1] = 2.0f * quaternion[1] * quaternion[2] + 2.0f * quaternion[3] * quaternion[0];
+
+ out[0][2] = 2.0f * quaternion[0] * quaternion[2] + 2.0f * quaternion[3] * quaternion[1];
+ out[1][2] = 2.0f * quaternion[1] * quaternion[2] - 2.0f * quaternion[3] * quaternion[0];
+ out[2][2] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[1] * quaternion[1];
+
+ out[0][3] = origin[0];
+ out[1][3] = origin[1];
+ out[2][3] = origin[2];
+}
+
+void Matrix3x4_CreateFromEntity( matrix3x4 out, const vec3_t angles, const vec3_t origin, float scale )
+{
+ float angle, sr, sp, sy, cr, cp, cy;
+
+ if( angles[ROLL] )
+ {
+ angle = angles[YAW] * (M_PI*2 / 360);
+ SinCos( angle, &sy, &cy );
+ angle = angles[PITCH] * (M_PI*2 / 360);
+ SinCos( angle, &sp, &cp );
+ angle = angles[ROLL] * (M_PI*2 / 360);
+ SinCos( angle, &sr, &cr );
+
+ out[0][0] = (cp*cy) * scale;
+ out[0][1] = (sr*sp*cy+cr*-sy) * scale;
+ out[0][2] = (cr*sp*cy+-sr*-sy) * scale;
+ out[0][3] = origin[0];
+ out[1][0] = (cp*sy) * scale;
+ out[1][1] = (sr*sp*sy+cr*cy) * scale;
+ out[1][2] = (cr*sp*sy+-sr*cy) * scale;
+ out[1][3] = origin[1];
+ out[2][0] = (-sp) * scale;
+ out[2][1] = (sr*cp) * scale;
+ out[2][2] = (cr*cp) * scale;
+ out[2][3] = origin[2];
+ }
+ else if( angles[PITCH] )
+ {
+ angle = angles[YAW] * (M_PI*2 / 360);
+ SinCos( angle, &sy, &cy );
+ angle = angles[PITCH] * (M_PI*2 / 360);
+ SinCos( angle, &sp, &cp );
+
+ out[0][0] = (cp*cy) * scale;
+ out[0][1] = (-sy) * scale;
+ out[0][2] = (sp*cy) * scale;
+ out[0][3] = origin[0];
+ out[1][0] = (cp*sy) * scale;
+ out[1][1] = (cy) * scale;
+ out[1][2] = (sp*sy) * scale;
+ out[1][3] = origin[1];
+ out[2][0] = (-sp) * scale;
+ out[2][1] = 0;
+ out[2][2] = (cp) * scale;
+ out[2][3] = origin[2];
+ }
+ else if( angles[YAW] )
+ {
+ angle = angles[YAW] * (M_PI*2 / 360);
+ SinCos( angle, &sy, &cy );
+
+ out[0][0] = (cy) * scale;
+ out[0][1] = (-sy) * scale;
+ out[0][2] = 0;
+ out[0][3] = origin[0];
+ out[1][0] = (sy) * scale;
+ out[1][1] = (cy) * scale;
+ out[1][2] = 0;
+ out[1][3] = origin[1];
+ out[2][0] = 0;
+ out[2][1] = 0;
+ out[2][2] = scale;
+ out[2][3] = origin[2];
+ }
+ else
+ {
+ out[0][0] = scale;
+ out[0][1] = 0;
+ out[0][2] = 0;
+ out[0][3] = origin[0];
+ out[1][0] = 0;
+ out[1][1] = scale;
+ out[1][2] = 0;
+ out[1][3] = origin[1];
+ out[2][0] = 0;
+ out[2][1] = 0;
+ out[2][2] = scale;
+ out[2][3] = origin[2];
+ }
+}
+
+void Matrix3x4_TransformPositivePlane( const matrix3x4 in, const vec3_t normal, float d, vec3_t out, float *dist )
+{
+ #ifdef PSP_VFPU
+ float scale = vfpu_sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] );
+ #else
+ float scale = sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] );
+ #endif
+ float iscale = 1.0f / scale;
+
+ out[0] = (normal[0] * in[0][0] + normal[1] * in[0][1] + normal[2] * in[0][2]) * iscale;
+ out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale;
+ out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale;
+ *dist = d * scale + ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] );
+}
+
+void Matrix3x4_Invert_Simple( matrix3x4 out, const matrix3x4 in1 )
+{
+ // we only support uniform scaling, so assume the first row is enough
+ // (note the lack of sqrt here, because we're trying to undo the scaling,
+ // this means multiplying by the inverse scale twice - squaring it, which
+ // makes the sqrt a waste of time)
+ float scale = 1.0 / (in1[0][0] * in1[0][0] + in1[0][1] * in1[0][1] + in1[0][2] * in1[0][2]);
+
+ // invert the rotation by transposing and multiplying by the squared
+ // recipricol of the input matrix scale as described above
+ out[0][0] = in1[0][0] * scale;
+ out[0][1] = in1[1][0] * scale;
+ out[0][2] = in1[2][0] * scale;
+ out[1][0] = in1[0][1] * scale;
+ out[1][1] = in1[1][1] * scale;
+ out[1][2] = in1[2][1] * scale;
+ out[2][0] = in1[0][2] * scale;
+ out[2][1] = in1[1][2] * scale;
+ out[2][2] = in1[2][2] * scale;
+
+ // invert the translate
+ out[0][3] = -(in1[0][3] * out[0][0] + in1[1][3] * out[0][1] + in1[2][3] * out[0][2]);
+ out[1][3] = -(in1[0][3] * out[1][0] + in1[1][3] * out[1][1] + in1[2][3] * out[1][2]);
+ out[2][3] = -(in1[0][3] * out[2][0] + in1[1][3] * out[2][1] + in1[2][3] * out[2][2]);
+}
+
+const matrix4x4 matrix4x4_identity =
+{
+{ 1, 0, 0, 0 }, // PITCH
+{ 0, 1, 0, 0 }, // YAW
+{ 0, 0, 1, 0 }, // ROLL
+{ 0, 0, 0, 1 }, // ORIGIN
+};
+
+/*
+========================================================================
+
+ Matrix4x4 operations
+
+========================================================================
+*/
+void Matrix4x4_VectorTransform( const matrix4x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + in[0][3];
+ out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + in[1][3];
+ out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] + in[2][3];
+}
+
+void Matrix4x4_VectorITransform( const matrix4x4 in, const float v[3], float out[3] )
+{
+ vec3_t dir;
+
+ dir[0] = v[0] - in[0][3];
+ dir[1] = v[1] - in[1][3];
+ dir[2] = v[2] - in[2][3];
+
+ out[0] = dir[0] * in[0][0] + dir[1] * in[1][0] + dir[2] * in[2][0];
+ out[1] = dir[0] * in[0][1] + dir[1] * in[1][1] + dir[2] * in[2][1];
+ out[2] = dir[0] * in[0][2] + dir[1] * in[1][2] + dir[2] * in[2][2];
+}
+
+void Matrix4x4_VectorRotate( const matrix4x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2];
+ out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2];
+ out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2];
+}
+
+void Matrix4x4_VectorIRotate( const matrix4x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[1][0] + v[2] * in[2][0];
+ out[1] = v[0] * in[0][1] + v[1] * in[1][1] + v[2] * in[2][1];
+ out[2] = v[0] * in[0][2] + v[1] * in[1][2] + v[2] * in[2][2];
+}
+
+void Matrix4x4_ConcatTransforms( matrix4x4 out, const matrix4x4 in1, const matrix4x4 in2 )
+{
+ out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
+ out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
+ out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2];
+ out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3];
+ out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0];
+ out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1];
+ out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2];
+ out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3];
+ out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0];
+ out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1];
+ out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2];
+ out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3];
+}
+
+void Matrix4x4_SetOrigin( matrix4x4 out, float x, float y, float z )
+{
+ out[0][3] = x;
+ out[1][3] = y;
+ out[2][3] = z;
+}
+
+void Matrix4x4_OriginFromMatrix( const matrix4x4 in, float *out )
+{
+ out[0] = in[0][3];
+ out[1] = in[1][3];
+ out[2] = in[2][3];
+}
+
+void Matrix4x4_FromOriginQuat( matrix4x4 out, const vec4_t quaternion, const vec3_t origin )
+{
+ out[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2];
+ out[1][0] = 2.0f * quaternion[0] * quaternion[1] + 2.0f * quaternion[3] * quaternion[2];
+ out[2][0] = 2.0f * quaternion[0] * quaternion[2] - 2.0f * quaternion[3] * quaternion[1];
+ out[0][3] = origin[0];
+ out[0][1] = 2.0f * quaternion[0] * quaternion[1] - 2.0f * quaternion[3] * quaternion[2];
+ out[1][1] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[2] * quaternion[2];
+ out[2][1] = 2.0f * quaternion[1] * quaternion[2] + 2.0f * quaternion[3] * quaternion[0];
+ out[1][3] = origin[1];
+ out[0][2] = 2.0f * quaternion[0] * quaternion[2] + 2.0f * quaternion[3] * quaternion[1];
+ out[1][2] = 2.0f * quaternion[1] * quaternion[2] - 2.0f * quaternion[3] * quaternion[0];
+ out[2][2] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[1] * quaternion[1];
+ out[2][3] = origin[2];
+ out[3][0] = 0;
+ out[3][1] = 0;
+ out[3][2] = 0;
+ out[3][3] = 1;
+}
+
+void Matrix4x4_CreateFromEntity( matrix4x4 out, const vec3_t angles, const vec3_t origin, float scale )
+{
+ float angle, sr, sp, sy, cr, cp, cy;
+
+ if( angles[ROLL] )
+ {
+ angle = angles[YAW] * (M_PI*2 / 360);
+ SinCos( angle, &sy, &cy );
+ angle = angles[PITCH] * (M_PI*2 / 360);
+ SinCos( angle, &sp, &cp );
+ angle = angles[ROLL] * (M_PI*2 / 360);
+ SinCos( angle, &sr, &cr );
+
+ out[0][0] = (cp*cy) * scale;
+ out[0][1] = (sr*sp*cy+cr*-sy) * scale;
+ out[0][2] = (cr*sp*cy+-sr*-sy) * scale;
+ out[0][3] = origin[0];
+ out[1][0] = (cp*sy) * scale;
+ out[1][1] = (sr*sp*sy+cr*cy) * scale;
+ out[1][2] = (cr*sp*sy+-sr*cy) * scale;
+ out[1][3] = origin[1];
+ out[2][0] = (-sp) * scale;
+ out[2][1] = (sr*cp) * scale;
+ out[2][2] = (cr*cp) * scale;
+ out[2][3] = origin[2];
+ out[3][0] = 0;
+ out[3][1] = 0;
+ out[3][2] = 0;
+ out[3][3] = 1;
+ }
+ else if( angles[PITCH] )
+ {
+ angle = angles[YAW] * (M_PI*2 / 360);
+ SinCos( angle, &sy, &cy );
+ angle = angles[PITCH] * (M_PI*2 / 360);
+ SinCos( angle, &sp, &cp );
+
+ out[0][0] = (cp*cy) * scale;
+ out[0][1] = (-sy) * scale;
+ out[0][2] = (sp*cy) * scale;
+ out[0][3] = origin[0];
+ out[1][0] = (cp*sy) * scale;
+ out[1][1] = (cy) * scale;
+ out[1][2] = (sp*sy) * scale;
+ out[1][3] = origin[1];
+ out[2][0] = (-sp) * scale;
+ out[2][1] = 0;
+ out[2][2] = (cp) * scale;
+ out[2][3] = origin[2];
+ out[3][0] = 0;
+ out[3][1] = 0;
+ out[3][2] = 0;
+ out[3][3] = 1;
+ }
+ else if( angles[YAW] )
+ {
+ angle = angles[YAW] * (M_PI*2 / 360);
+ SinCos( angle, &sy, &cy );
+
+ out[0][0] = (cy) * scale;
+ out[0][1] = (-sy) * scale;
+ out[0][2] = 0;
+ out[0][3] = origin[0];
+ out[1][0] = (sy) * scale;
+ out[1][1] = (cy) * scale;
+ out[1][2] = 0;
+ out[1][3] = origin[1];
+ out[2][0] = 0;
+ out[2][1] = 0;
+ out[2][2] = scale;
+ out[2][3] = origin[2];
+ out[3][0] = 0;
+ out[3][1] = 0;
+ out[3][2] = 0;
+ out[3][3] = 1;
+ }
+ else
+ {
+ out[0][0] = scale;
+ out[0][1] = 0;
+ out[0][2] = 0;
+ out[0][3] = origin[0];
+ out[1][0] = 0;
+ out[1][1] = scale;
+ out[1][2] = 0;
+ out[1][3] = origin[1];
+ out[2][0] = 0;
+ out[2][1] = 0;
+ out[2][2] = scale;
+ out[2][3] = origin[2];
+ out[3][0] = 0;
+ out[3][1] = 0;
+ out[3][2] = 0;
+ out[3][3] = 1;
+ }
+}
+
+void Matrix4x4_ConvertToEntity( const matrix4x4 in, vec3_t angles, vec3_t origin )
+{
+ #ifdef PSP_VFPU
+ float xyDist = vfpu_sqrtf( in[0][0] * in[0][0] + in[1][0] * in[1][0] );
+ #else
+ float xyDist = sqrtf( in[0][0] * in[0][0] + in[1][0] * in[1][0] );
+ #endif
+
+ // enough here to get angles?
+ if( xyDist > 0.001f )
+ {
+ #ifdef PSP_VFPU
+ angles[0] = RAD2DEG( vfpu_atan2f( -in[2][0], xyDist ) );
+ angles[1] = RAD2DEG( vfpu_atan2f( in[1][0], in[0][0] ) );
+ angles[2] = RAD2DEG( vfpu_atan2f( in[2][1], in[2][2] ) );
+ #else
+ angles[0] = RAD2DEG( atan2( -in[2][0], xyDist ) );
+ angles[1] = RAD2DEG( atan2( in[1][0], in[0][0] ) );
+ angles[2] = RAD2DEG( atan2( in[2][1], in[2][2] ) );
+ #endif
+ }
+ else // forward is mostly Z, gimbal lock
+ {
+ #ifdef PSP_VFPU
+ angles[0] = RAD2DEG( vfpu_atan2f( -in[2][0], xyDist ) );
+ angles[1] = RAD2DEG( vfpu_atan2f( -in[0][1], in[1][1] ) );
+ #else
+ angles[0] = RAD2DEG( atan2( -in[2][0], xyDist ) );
+ angles[1] = RAD2DEG( atan2( -in[0][1], in[1][1] ) );
+ #endif
+ angles[2] = 0;
+ }
+
+ origin[0] = in[0][3];
+ origin[1] = in[1][3];
+ origin[2] = in[2][3];
+}
+
+void Matrix4x4_TransformPositivePlane( const matrix4x4 in, const vec3_t normal, float d, vec3_t out, float *dist )
+{
+ #ifdef PSP_VFPU
+ float scale = vfpu_sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] );
+ #else
+ float scale = sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] );
+ #endif
+ float iscale = 1.0f / scale;
+
+ out[0] = (normal[0] * in[0][0] + normal[1] * in[0][1] + normal[2] * in[0][2]) * iscale;
+ out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale;
+ out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale;
+ *dist = d * scale + ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] );
+}
+
+void Matrix4x4_TransformStandardPlane( const matrix4x4 in, const vec3_t normal, float d, vec3_t out, float *dist )
+{
+ #ifdef PSP_VFPU
+ float scale = vfpu_sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] );
+ #else
+ float scale = sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] );
+ #endif
+ float iscale = 1.0f / scale;
+
+ out[0] = (normal[0] * in[0][0] + normal[1] * in[0][1] + normal[2] * in[0][2]) * iscale;
+ out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale;
+ out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale;
+ *dist = d * scale - ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] );
+}
+
+void Matrix4x4_Invert_Simple( matrix4x4 out, const matrix4x4 in1 )
+{
+ // we only support uniform scaling, so assume the first row is enough
+ // (note the lack of sqrt here, because we're trying to undo the scaling,
+ // this means multiplying by the inverse scale twice - squaring it, which
+ // makes the sqrt a waste of time)
+ float scale = 1.0f / (in1[0][0] * in1[0][0] + in1[0][1] * in1[0][1] + in1[0][2] * in1[0][2]);
+
+ // invert the rotation by transposing and multiplying by the squared
+ // recipricol of the input matrix scale as described above
+ out[0][0] = in1[0][0] * scale;
+ out[0][1] = in1[1][0] * scale;
+ out[0][2] = in1[2][0] * scale;
+ out[1][0] = in1[0][1] * scale;
+ out[1][1] = in1[1][1] * scale;
+ out[1][2] = in1[2][1] * scale;
+ out[2][0] = in1[0][2] * scale;
+ out[2][1] = in1[1][2] * scale;
+ out[2][2] = in1[2][2] * scale;
+
+ // invert the translate
+ out[0][3] = -(in1[0][3] * out[0][0] + in1[1][3] * out[0][1] + in1[2][3] * out[0][2]);
+ out[1][3] = -(in1[0][3] * out[1][0] + in1[1][3] * out[1][1] + in1[2][3] * out[1][2]);
+ out[2][3] = -(in1[0][3] * out[2][0] + in1[1][3] * out[2][1] + in1[2][3] * out[2][2]);
+
+ // don't know if there's anything worth doing here
+ out[3][0] = 0;
+ out[3][1] = 0;
+ out[3][2] = 0;
+ out[3][3] = 1;
+}
diff --git a/source/menu.c b/source/menu.c
new file mode 100644
index 0000000..c19fb38
--- /dev/null
+++ b/source/menu.c
@@ -0,0 +1,5697 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#include "quakedef.h"
+
+#include
+#include
+#include
+//#include
+#include
+#include
+#include "net_dgrm.h"
+#include "cl_slist.h"
+#include "crypter.h"
+
+
+extern cvar_t accesspoint;
+extern cvar_t r_wateralpha;
+extern cvar_t r_vsync;
+extern cvar_t in_disable_analog;
+extern cvar_t in_analog_strafe;
+extern cvar_t in_x_axis_adjust;
+extern cvar_t in_y_axis_adjust;
+extern cvar_t crosshair;
+extern cvar_t r_dithering;
+extern cvar_t r_retro;
+extern cvar_t waypoint_mode;
+
+extern qpic_t *b_square;
+extern qpic_t *b_triangle;
+extern qpic_t *b_cross;
+extern qpic_t *b_circle;
+extern qpic_t *b_lt;
+extern qpic_t *b_rt;
+extern qpic_t *b_start;
+extern qpic_t *b_left;
+extern qpic_t *b_right;
+
+extern qboolean bmg_type_changed;
+
+extern int loadingScreen;
+extern int ShowBlslogo;
+
+// Backgrounds
+qpic_t *menu_bk;
+
+// Map screens
+qpic_t *menu_ndu;
+qpic_t *menu_wh;
+//qpic_t *menu_kn;
+qpic_t *menu_ch;
+//qpic_t *menu_wn;
+qpic_t *menu_custom;
+qpic_t *menu_cuthum;
+
+
+typedef struct
+{
+ int occupied;
+ int map_allow_game_settings;
+ int map_use_thumbnail;
+ char* map_name;
+ char* map_name_pretty;
+ char* map_desc_1;
+ char* map_desc_2;
+ char* map_desc_3;
+ char* map_desc_4;
+ char* map_desc_5;
+ char* map_desc_6;
+ char* map_desc_7;
+ char* map_desc_8;
+ char* map_author;
+ char* map_thumbnail_path;
+} usermap_t;
+
+SceIoStat custom_thumbnail_size;
+usermap_t custom_maps[50];
+
+enum
+{
+m_none,
+m_start,
+m_main,
+m_paused_menu,
+m_map,
+m_singleplayer,
+m_multiplayer,
+m_achievement,
+m_setup,
+m_net,
+m_options,
+m_audio,
+m_gameplay,
+m_video,
+m_keys,
+m_credits,
+m_quit,
+m_restart,
+m_exit,
+m_serialconfig,
+m_modemconfig,
+m_lanconfig,
+m_gameoptions,
+m_search,
+m_slist,
+m_sedit,
+m_osk
+} m_state;
+
+void M_Start_Menu_f (void);
+void M_Paused_Menu_f (void);
+ void M_Menu_Restart_f (void);
+ void M_Menu_Exit_f (void);
+void M_Menu_Main_f (void);
+ void M_Menu_SinglePlayer_f (void);
+ void M_Menu_Load_f (void);
+ void M_Menu_Save_f (void);
+ void M_Menu_MultiPlayer_f (void);
+ void M_Menu_Setup_f (void);
+ void M_Menu_Net_f (void);
+ void M_Menu_ServerList_f (void);
+ void M_Menu_Achievement_f (void);
+ void M_Menu_Options_f (void);
+ void M_Menu_Keys_f (void);
+ void M_Menu_Audio_f (void);
+ void M_Menu_Gameplay_f (void);
+ void M_Menu_Screen_f (void);
+ void M_Menu_Credits_f (void);
+ void M_Menu_Quit_f (void);
+void M_Menu_SerialConfig_f (void);
+ void M_Menu_ModemConfig_f (void);
+void M_Menu_LanConfig_f (void);
+void M_Menu_GameOptions_f (void);
+void M_Menu_Search_f (void);
+void M_Menu_ServerList_f (void);
+
+void M_Main_Draw (void);
+ void M_Setup_Draw (void);
+ void M_Net_Draw (void);
+ void M_Options_Draw (void);
+ void M_Keys_Draw (void);
+ void M_Credits_Draw (void);
+ void M_Quit_Draw (void);
+void M_SerialConfig_Draw (void);
+ void M_ModemConfig_Draw (void);
+void M_LanConfig_Draw (void);
+void M_GameOptions_Draw (void);
+void M_Search_Draw (void);
+void M_ServerList_Draw (void);
+
+void M_Main_Key (int key);
+void M_SinglePlayer_Key (int key);
+void M_MultiPlayer_Key (int key);
+ void M_Setup_Key (int key);
+ void M_Net_Key (int key);
+void M_Options_Key (int key);
+ void M_Keys_Key (int key);
+void M_Credits_Key (int key);
+void M_Quit_Key (int key);
+void M_SerialConfig_Key (int key);
+void M_ModemConfig_Key (int key);
+void M_LanConfig_Key (int key);
+void M_GameOptions_Key (int key);
+void M_Search_Key (int key);
+void M_ServerList_Key (int key);
+
+void Con_SetOSKActive(qboolean active);
+void M_Menu_OSK_f (char *input, char *output, int outlen);
+
+
+qboolean m_entersound; // play after drawing a frame, so caching
+ // won't disrupt the sound
+qboolean m_recursiveDraw;
+
+int m_return_state;
+qboolean m_return_onerror;
+char m_return_reason [32];
+
+#define StartingGame (m_multiplayer_cursor == 1)
+#define JoiningGame (m_multiplayer_cursor == 0)
+#define SerialConfig (m_net_cursor == 0)
+#define DirectConfig (m_net_cursor == 1)
+#define IPXConfig (m_net_cursor == 2)
+#define TCPIPConfig (m_net_cursor == 3)
+
+void M_ConfigureNetSubsystem(void);
+
+/*
+================
+M_DrawCharacter
+
+Draws one solid graphics character
+================
+*/
+void M_DrawCharacter (int cx, int line, int num)
+{
+ Draw_Character ( cx, line, num);
+}
+void M_DrawCharacter2 (int cx, int line, int num)
+{
+ Draw_Character ( cx + ((vid.width - 320)>>1), line, num);
+}
+
+void M_PrintOld (int cx, int cy, char *str)
+{
+ while (*str)
+ {
+ M_DrawCharacter2 (cx, cy, (*str)+128);
+ str++;
+ cx += 8;
+ }
+}
+
+void M_Print (int cx, int cy, char *str)
+{
+ char str2[80];
+
+ strcpy (str2,"&cD32");//831
+ strcat (str2, str);
+ Draw_ColoredString (cx, cy, str2, 255, 255, 255, 255, 1);
+}
+
+int M_ColorForMap (int m)
+{
+ return m < 128 ? m + 8 : m + 8;
+}
+
+void M_PrintWhiteOld (int cx, int cy, char *str)
+{
+ while (*str)
+ {
+ M_DrawCharacter2 (cx, cy, *str);
+ str++;
+ cx += 8;
+ }
+}
+void M_PrintWhite (int cx, int cy, char *str)
+{
+ while (*str)
+ {
+ M_DrawCharacter (cx, cy, *str);
+ str++;
+ cx += 8;
+ }
+}
+
+void M_DrawTransPic (int x, int y, qpic_t *pic)
+{
+ Draw_TransPic (x + ((vid.width - 320)>>1), y, pic);
+}
+
+void M_DrawPic (int x, int y, qpic_t *pic)
+{
+ Draw_Pic (x + ((vid.width - 320)>>1), y, pic);
+}
+
+byte identityTable[256];
+byte translationTable[256];
+
+void M_BuildTranslationTable(int top, int bottom)
+{
+ int j;
+ byte *dest, *source;
+
+ for (j = 0; j < 256; j++)
+ identityTable[j] = j;
+ dest = translationTable;
+ source = identityTable;
+ memcpy (dest, source, 256);
+
+ if (top < 128) // the artists made some backwards ranges. sigh.
+ memcpy (dest + TOP_RANGE, source + top, 16);
+ else
+ for (j=0 ; j<16 ; j++)
+ dest[TOP_RANGE+j] = source[top+15-j];
+
+ if (bottom < 128)
+ memcpy (dest + BOTTOM_RANGE, source + bottom, 16);
+ else
+ for (j=0 ; j<16 ; j++)
+ dest[BOTTOM_RANGE+j] = source[bottom+15-j];
+}
+
+
+void M_DrawTransPicTranslate (int x, int y, qpic_t *pic)
+{
+ Draw_TransPicTranslate (x + ((vid.width - 320)>>1), y, pic, translationTable);
+}
+
+
+void M_DrawTextBox (int x, int y, int width, int lines)
+{
+ qpic_t *p;
+ int cx, cy;
+ int n;
+
+ // draw left side
+ cx = x;
+ cy = y;
+ p = Draw_CachePic ("gfx/box_tl.lmp");
+ M_DrawTransPic (cx, cy, p);
+ p = Draw_CachePic ("gfx/box_ml.lmp");
+ for (n = 0; n < lines; n++)
+ {
+ cy += 8;
+ M_DrawTransPic (cx, cy, p);
+ }
+ p = Draw_CachePic ("gfx/box_bl.lmp");
+ M_DrawTransPic (cx, cy+8, p);
+
+ // draw middle
+ cx += 8;
+ while (width > 0)
+ {
+ cy = y;
+ p = Draw_CachePic ("gfx/box_tm.lmp");
+ M_DrawTransPic (cx, cy, p);
+ p = Draw_CachePic ("gfx/box_mm.lmp");
+ for (n = 0; n < lines; n++)
+ {
+ cy += 8;
+ if (n == 1)
+ p = Draw_CachePic ("gfx/box_mm2.lmp");
+ M_DrawTransPic (cx, cy, p);
+ }
+ p = Draw_CachePic ("gfx/box_bm.lmp");
+ M_DrawTransPic (cx, cy+8, p);
+ width -= 2;
+ cx += 16;
+ }
+
+ // draw right side
+ cy = y;
+ p = Draw_CachePic ("gfx/box_tr.lmp");
+ M_DrawTransPic (cx, cy, p);
+ p = Draw_CachePic ("gfx/box_mr.lmp");
+ for (n = 0; n < lines; n++)
+ {
+ cy += 8;
+ M_DrawTransPic (cx, cy, p);
+ }
+ p = Draw_CachePic ("gfx/box_br.lmp");
+ M_DrawTransPic (cx, cy+8, p);
+}
+
+void M_DrawCheckbox (int x, int y, int on)
+{
+ if (on)
+ Draw_String (x, y, "Enabled");
+ else
+ Draw_String (x, y, "Disabled");
+}
+
+//=============================================================================
+
+/*
+================
+M_ToggleMenu_f
+================
+*/
+void M_ToggleMenu_f (void)
+{
+ m_entersound = true;
+
+ if (key_dest == key_menu || key_dest == key_menu_pause)
+ {
+ if (m_state != m_main && m_state != m_paused_menu)
+ {
+ M_Menu_Main_f ();
+ return;
+ }
+ key_dest = key_game;
+ m_state = m_none;
+ return;
+ }
+ if (key_dest == key_console)
+ {
+ Con_ToggleConsole_f ();
+ }
+ else if (sv.active && (svs.maxclients > 1 || key_dest == key_game))
+ {
+ M_Paused_Menu_f();
+ }
+ else
+ {
+ M_Menu_Main_f ();
+ }
+}
+
+//=============================================================================
+
+int M_Start_Cusor;
+#define Max_Start_Iteams 5
+
+void M_Start_Menu_f ()
+{
+ key_dest = key_menu;
+ m_state = m_start;
+ m_entersound = true;
+ //loadingScreen = 0;
+ Load_Achivements();
+}
+
+static void M_Start_Menu_Draw ()
+{
+ // Background
+ menu_bk = Draw_CacheImg("gfx/menu/menu_background");
+ Draw_Pic (0, 0, menu_bk);
+
+ // Fill black to make everything easier to see
+ Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102));
+
+ Draw_ColoredString((vid.width)/2 - 44, (vid.height - 64), "Press Start", 255, 0, 0, 255, 1);
+}
+
+void M_Start_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ S_LocalSound ("sounds/menu/enter.wav");
+ Cbuf_AddText("togglemenu\n");
+ break;
+ }
+}
+//=============================================================================
+
+int M_Paused_Cusor;
+#define Max_Paused_Iteams 5
+
+void M_Paused_Menu_f ()
+{
+ key_dest = key_menu_pause;
+ m_state = m_paused_menu;
+ m_entersound = true;
+ loadingScreen = 0;
+ M_Paused_Cusor = 0;
+}
+
+static void M_Paused_Menu_Draw ()
+{
+ // Fill black to make everything easier to see
+ Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102));
+
+ // Header
+ Draw_ColoredString(10, 10, "PAUSED", 255, 255, 255, 255, 2);
+
+ if ((M_Paused_Cusor == 0))
+ Draw_ColoredString(10, 135, "Resume", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 135, "Resume", 255, 255, 255, 255, 1);
+
+ if ((M_Paused_Cusor == 1))
+ Draw_ColoredString(10, 145, "Restart", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 145, "Restart", 255, 255, 255, 255, 1);
+
+ if ((M_Paused_Cusor == 2))
+ Draw_ColoredString(10, 155, "Settings", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 155, "Settings", 255, 255, 255, 255, 1);
+
+ if (waypoint_mode.value) {
+ if ((M_Paused_Cusor == 3))
+ Draw_ColoredString(10, 165, "Save Waypoints", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 165, "Save Waypoints", 255, 255, 255, 255, 1);
+ } else {
+ if ((M_Paused_Cusor == 3))
+ Draw_ColoredString(10, 165, "Achievements", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 165, "Achievements", 255, 255, 255, 255, 1);
+ }
+
+ if ((M_Paused_Cusor == 4))
+ Draw_ColoredString(10, 175, "Main Menu", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 175, "Main Menu", 255, 255, 255, 255, 1);
+}
+
+static void M_Paused_Menu_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ S_LocalSound ("sounds/menu/enter.wav");
+ Cbuf_AddText("togglemenu\n");
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (++M_Paused_Cusor >= Max_Paused_Iteams)
+ M_Paused_Cusor = 0;
+ break;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (--M_Paused_Cusor < 0)
+ M_Paused_Cusor = Max_Paused_Iteams - 1;
+ break;
+
+ case K_ENTER:
+ m_entersound = true;
+
+ switch (M_Paused_Cusor)
+ {
+ case 0:
+ key_dest = key_game;
+ m_state = m_none;
+ break;
+ case 1:
+ M_Menu_Restart_f();
+ break;
+ case 2:
+ M_Menu_Options_f();
+ key_dest = key_menu_pause;
+ break;
+ case 3:
+ if (waypoint_mode.value)
+ Cbuf_AddText("impulse 101\n");
+ else
+ M_Menu_Achievement_f();
+ key_dest = key_menu_pause;
+ break;
+ case 4:
+ M_Menu_Exit_f();
+ break;
+ }
+ }
+}
+
+//=============================================================================
+/* MAIN MENU */
+
+int m_main_cursor;
+#define MAIN_ITEMS 5
+
+
+void M_Menu_Main_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_main;
+ m_entersound = true;
+}
+
+
+void M_Main_Draw (void)
+{
+ // Background
+ Draw_Pic (0, 0, menu_bk);
+
+ // Fill black to make everything easier to see
+ Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102));
+
+ // Version String
+ Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1);
+
+ // Header
+ Draw_ColoredString(10, 10, "MAIN MENU", 255, 255, 255, 255, 2);
+
+ // Solo
+ if (m_main_cursor == 0)
+ Draw_ColoredString(10, 45, "Solo", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 45, "Solo", 255, 255, 255, 255, 1);
+
+
+ // Co-Op (Unfinished, so non-selectable)
+ Draw_ColoredString(10, 55, "Co-Op (Coming Soon!)", 128, 128, 128, 255, 1);
+
+ // Divider
+ Draw_FillByColor(10, 68, 160, 2, GU_RGBA(130, 130, 130, 255));
+
+ if (m_main_cursor == 1)
+ Draw_ColoredString(10, 75, "Settings", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 75, "Settings", 255, 255, 255, 255, 1);
+
+ if (m_main_cursor == 2)
+ Draw_ColoredString(10, 85, "Achievements", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 85, "Achievements", 255, 255, 255, 255, 1);
+
+ // Divider
+ Draw_FillByColor(10, 98, 160, 2, GU_RGBA(130, 130, 130, 255));
+
+ if (m_main_cursor == 3)
+ Draw_ColoredString(10, 105, "Credits", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 105, "Credits", 255, 255, 255, 255, 1);
+
+ // Divider
+ Draw_FillByColor(10, 118, 160, 2, GU_RGBA(130, 130, 130, 255));
+
+ if (m_main_cursor == 4)
+ Draw_ColoredString(10, 125, "Exit", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 125, "Exit", 255, 255, 255, 255, 1);
+
+ // Descriptions
+ switch(m_main_cursor) {
+ case 0: // Solo
+ Draw_ColoredString(10, 230, "Take on the Hordes by yourself.", 255, 255, 255, 255, 1);
+ break;
+ case 1: // Settings
+ Draw_ColoredString(10, 230, "Adjust your Settings to Optimize your Experience.", 255, 255, 255, 255, 1);
+ break;
+ case 2: // Achievements
+ Draw_ColoredString(10, 230, "View locked or unlocked Achievements.", 255, 255, 255, 255, 1);
+ break;
+ case 3: // Credits
+ Draw_ColoredString(10, 230, "See who made NZ:P possible.", 255, 255, 255, 255, 1);
+ break;
+ case 4: // Exit
+ Draw_ColoredString(10, 230, "Return to XMB.", 255, 255, 255, 255, 1);
+ break;
+ }
+}
+
+
+void M_Main_Key (int key)
+{
+ switch (key)
+ {
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (++m_main_cursor >= MAIN_ITEMS)
+ m_main_cursor = 0;
+ break;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (--m_main_cursor < 0)
+ m_main_cursor = MAIN_ITEMS - 1;
+ break;
+
+ case K_ENTER:
+ m_entersound = true;
+
+ switch (m_main_cursor)
+ {
+ case 0:
+ M_Menu_SinglePlayer_f ();
+ break;
+
+ /*case 1:
+ M_Menu_MultiPlayer_f ();
+ break;*/
+
+ case 1:
+ M_Menu_Options_f ();
+ break;
+
+ case 2:
+ M_Menu_Achievement_f ();
+ break;
+
+ case 3:
+ M_Menu_Credits_f ();
+ break;
+
+ case 4:
+ M_Menu_Quit_f ();
+ break;
+ }
+ break;
+ }
+}
+
+
+
+
+//=============================================================================
+/* RESTART MENU */
+
+qboolean wasInMenus;
+
+
+char *restartMessage [] =
+{
+
+ " Are you sure you want",
+ " to restart this game? ", //msg:0
+ " ",
+ " X :Yes O : No "
+};
+
+
+void M_Menu_Restart_f (void)
+{
+ wasInMenus = (key_dest == key_menu_pause);
+ key_dest = key_menu_pause;
+ m_state = m_restart;
+ m_entersound = true;
+}
+
+
+void M_Restart_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ case 'n':
+ case 'N':
+ m_state = m_paused_menu;
+ m_entersound = true;
+ break;
+
+ case 'Y':
+ case 'y':
+ case K_ENTER:
+ key_dest = key_game;
+ m_state = m_none;
+ // Cbuf_AddText ("restart\n"); // nai -- old, now do soft reset
+ PR_ExecuteProgram (pr_global_struct->Soft_Restart);
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+
+void M_Restart_Draw (void)
+{
+ m_state = m_paused_menu;
+ m_recursiveDraw = true;
+ M_Draw ();
+ m_state = m_restart;
+
+ M_DrawTextBox (56, 76, 24, 4);
+ M_PrintOld (64, 84, restartMessage[0]);
+ M_PrintOld (64, 92, restartMessage[1]);
+ M_PrintOld (64, 100, restartMessage[2]);
+ M_PrintOld (64, 108, restartMessage[3]);
+}
+
+
+
+
+//=============================================================================
+/* EXIT MENU */
+
+
+char *exitMessage [] =
+{
+
+ " Are you sure you want ",
+ "to quit to the Main Menu?", //msg:0
+ " ",
+ " X :Yes O : No "
+};
+
+
+void M_Menu_Exit_f (void)
+{
+ wasInMenus = (key_dest == key_menu_pause);
+ key_dest = key_menu_pause;
+ m_state = m_exit;
+ m_entersound = true;
+}
+
+
+void M_Exit_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ case 'n':
+ case 'N':
+ m_state = m_paused_menu;
+ m_entersound = true;
+ break;
+
+ case 'Y':
+ case 'y':
+ case K_ENTER:
+ Cbuf_AddText("disconnect\n");
+ M_Menu_Main_f();
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+
+void M_Exit_Draw (void)
+{
+ m_state = m_paused_menu;
+ m_recursiveDraw = true;
+ M_Draw ();
+ m_state = m_exit;
+
+ M_DrawTextBox (56, 76, 24, 4);
+ M_PrintOld (64, 84, exitMessage[0]);
+ M_PrintOld (64, 92, exitMessage[1]);
+ M_PrintOld (64, 100, exitMessage[2]);
+ M_PrintOld (64, 108, exitMessage[3]);
+}
+
+//=============================================================================
+/* SINGLE PLAYER MENU */
+
+int m_map_cursor;
+int MAP_ITEMS;
+int user_maps_num = 0;
+int current_custom_map_page;
+int custom_map_pages;
+int multiplier;
+char user_levels[256][MAX_QPATH];
+
+void M_Menu_Map_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_map;
+ m_entersound = true;
+ MAP_ITEMS = 13;
+ current_custom_map_page = 1;
+}
+
+
+void M_Map_Draw (void)
+{
+ // Background
+ Draw_Pic(0, 0, menu_bk);
+
+ // Fill black to make everything easier to see
+ Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102));
+
+ // Version String
+ Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1);
+
+ // Header
+ Draw_ColoredString(10, 10, "CUSTOM MAPS", 255, 255, 255, 255, 2);
+
+ int line_increment;
+
+ line_increment = 0;
+
+ if (current_custom_map_page > 1)
+ multiplier = (current_custom_map_page - 1) * 15;
+ else
+ multiplier = 0;
+
+ for (int i = 0; i < 15; i++) {
+ if (custom_maps[i + multiplier].occupied == false)
+ continue;
+
+ if (m_map_cursor == i) {
+
+ if (custom_maps[i + multiplier].map_use_thumbnail == 1) {
+ menu_cuthum = Draw_CacheImg(custom_maps[i + multiplier].map_thumbnail_path);
+ Draw_Pic(256, 45, menu_cuthum);
+ }
+
+ if (custom_maps[i + multiplier].map_name_pretty != 0)
+ Draw_ColoredString(10, 45 + (10 * i), custom_maps[i + multiplier].map_name_pretty, 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 45 + (10 * i), custom_maps[i + multiplier].map_name, 255, 0, 0, 255, 1);
+
+ if (custom_maps[i + multiplier].map_desc_1 != 0) {
+ if (strcmp(custom_maps[i + multiplier].map_desc_1, " ") != 0) {
+ Draw_ColoredString(215, 155, custom_maps[i + multiplier].map_desc_1, 255, 255, 255, 255, 1);
+ }
+ }
+ if (custom_maps[i + multiplier].map_desc_2 != 0) {
+ if (strcmp(custom_maps[i + multiplier].map_desc_2, " ") != 0) {
+ line_increment++;
+ Draw_ColoredString(215, 165, custom_maps[i + multiplier].map_desc_2, 255, 255, 255, 255, 1);
+ }
+ }
+ if (custom_maps[i + multiplier].map_desc_3 != 0) {
+ if (strcmp(custom_maps[i + multiplier].map_desc_3, " ") != 0) {
+ line_increment++;
+ Draw_ColoredString(215, 175, custom_maps[i + multiplier].map_desc_3, 255, 255, 255, 255, 1);
+ }
+ }
+ if (custom_maps[i + multiplier].map_desc_4 != 0) {
+ if (strcmp(custom_maps[i + multiplier].map_desc_4, " ") != 0) {
+ line_increment++;
+ Draw_ColoredString(215, 185, custom_maps[i + multiplier].map_desc_4, 255, 255, 255, 255, 1);
+ }
+ }
+ if (custom_maps[i + multiplier].map_desc_5 != 0) {
+ if (strcmp(custom_maps[i + multiplier].map_desc_5, " ") != 0) {
+ line_increment++;
+ Draw_ColoredString(215, 195, custom_maps[i + multiplier].map_desc_5, 255, 255, 255, 255, 1);
+ }
+ }
+ if (custom_maps[i + multiplier].map_desc_6 != 0) {
+ if (strcmp(custom_maps[i + multiplier].map_desc_6, " ") != 0) {
+ line_increment++;
+ Draw_ColoredString(215, 205, custom_maps[i + multiplier].map_desc_6, 255, 255, 255, 255, 1);
+ }
+ }
+ if (custom_maps[i + multiplier].map_desc_7 != 0) {
+ if (strcmp(custom_maps[i + multiplier].map_desc_7, " ") != 0) {
+ line_increment++;
+ Draw_ColoredString(215, 215, custom_maps[i + multiplier].map_desc_7, 255, 255, 255, 255, 1);
+ }
+ }
+ if (custom_maps[i + multiplier].map_desc_8 != 0) {
+ if (strcmp(custom_maps[i + multiplier].map_desc_8, " ") != 0) {
+ line_increment++;
+ Draw_ColoredString(215, 225, custom_maps[i + multiplier].map_desc_8, 255, 255, 255, 255, 1);
+ }
+ }
+ if (custom_maps[i + multiplier].map_author != 0) {
+ if (strcmp(custom_maps[i + multiplier].map_author, " ") != 0) {
+ int y = 165 + (10 * line_increment);
+ Draw_ColoredString(215, y, custom_maps[i + multiplier].map_author, 255, 255, 0, 255, 1);
+ }
+ }
+ } else {
+ if (custom_maps[i + multiplier].map_name_pretty != 0)
+ Draw_ColoredString(10, 45 + (10 * i), custom_maps[i + multiplier].map_name_pretty, 255, 255, 255, 255, 1);
+ else
+ Draw_ColoredString(10, 45 + (10 * i), custom_maps[i + multiplier].map_name, 255, 255, 255, 255, 1);
+ }
+ }
+
+ if (current_custom_map_page != custom_map_pages) {
+ if (m_map_cursor == 15)
+ Draw_ColoredString(10, 230, "Next Page", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 230, "Next Page", 255, 255, 255, 255, 1);
+ } else {
+ Draw_ColoredString(10, 230, "Next Page", 128, 128, 128, 255, 1);
+ }
+
+ if (current_custom_map_page != 1) {
+ if (m_map_cursor == 16)
+ Draw_ColoredString(10, 240, "Previous Page", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 240, "Previous Page", 255, 255, 255, 255, 1);
+ } else {
+ Draw_ColoredString(10, 240, "Previous Page", 128, 128, 128, 255, 1);
+ }
+
+
+
+ if (m_map_cursor == 17)
+ Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1);
+}
+
+
+void M_Map_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ M_Menu_SinglePlayer_f ();
+ break;
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+
+ m_map_cursor++;
+
+ if (m_map_cursor < 14 && custom_maps[m_map_cursor + multiplier].occupied == false) {
+ m_map_cursor = 15;
+ }
+
+ if (m_map_cursor == 15 && current_custom_map_page == custom_map_pages)
+ m_map_cursor = 16;
+
+ if (m_map_cursor == 16 && current_custom_map_page == 1)
+ m_map_cursor = 17;
+
+ if (m_map_cursor >= 18)
+ m_map_cursor = 0;
+ break;
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+
+ m_map_cursor--;
+
+ if (m_map_cursor < 0)
+ m_map_cursor = 17;
+
+ if (m_map_cursor == 16 && current_custom_map_page == 1)
+ m_map_cursor = 15;
+
+ if (m_map_cursor == 15 && current_custom_map_page == custom_map_pages)
+ m_map_cursor = 14;
+
+ if (m_map_cursor <= 14 && custom_maps[m_map_cursor + multiplier].occupied == false) {
+ for (int i = 14; i > -1; i--) {
+ if (custom_maps[i + multiplier].occupied == true) {
+ m_map_cursor = i;
+ break;
+ }
+ }
+ }
+ break;
+ case K_ENTER:
+ m_entersound = true;
+ if (m_map_cursor == 17) {
+ M_Menu_SinglePlayer_f ();
+ } else if (m_map_cursor == 16) {
+ current_custom_map_page--;
+ } else if (m_map_cursor == 15) {
+ current_custom_map_page++;
+ } else
+ {
+ key_dest = key_game;
+ if (sv.active)
+ Cbuf_AddText ("disconnect\n");
+ Cbuf_AddText ("maxplayers 1\n");
+ Cbuf_AddText (va("map %s\n", custom_maps[m_map_cursor + multiplier].map_name));
+ //loadingScreen = 1;
+ }
+ break;
+ }
+}
+
+
+//=============================================================================
+/* SINGLE PLAYER MENU */
+
+int m_singleplayer_cursor;
+#define SINGLEPLAYER_ITEMS 5
+
+
+void M_Menu_SinglePlayer_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_singleplayer;
+ m_entersound = true;
+}
+
+
+void M_SinglePlayer_Draw (void)
+{
+ menu_ndu = Draw_CacheImg("gfx/menu/nacht_der_untoten");
+ //menu_kn = Draw_CacheImg("gfx/menu/kino_der_toten");
+ menu_wh = Draw_CacheImg("gfx/menu/warehouse");
+ //menu_wn = Draw_CacheImg("gfx/menu/wahnsinn");
+ menu_ch = Draw_CacheImg("gfx/menu/christmas_special");
+ menu_custom = Draw_CacheImg("gfx/menu/custom");
+
+ // Background
+ Draw_Pic(0, 0, menu_bk);
+
+ // Fill black to make everything easier to see
+ Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102));
+
+ // Version String
+ Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1);
+
+ // Header
+ Draw_ColoredString(10, 10, "SOLO", 255, 255, 255, 255, 2);
+
+ // Nacht der Untoten
+ if (m_singleplayer_cursor == 0)
+ Draw_ColoredString(10, 45, "Nacht der Untoten", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 45, "Nacht der Untoten", 255, 255, 255, 255, 1);
+
+ // Kino der Toten
+ Draw_ColoredString(10, 55, "Kino der Toten", 128, 128, 128, 255, 1);
+
+ // Warehouse
+ if (m_singleplayer_cursor == 1)
+ Draw_ColoredString(10, 65, "Warehouse", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 65, "Warehouse", 255, 255, 255, 255, 1);
+
+ // Wahnsinn
+ Draw_ColoredString(10, 75, "Wahnsinn", 128, 128, 128, 255, 1);
+
+ // Christmas Special
+ if (m_singleplayer_cursor == 2)
+ Draw_ColoredString(10, 85, "Christmas Special", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 85, "Christmas Special", 255, 255, 255, 255, 1);
+
+ // Divider
+ Draw_FillByColor(10, 98, 160, 2, GU_RGBA(130, 130, 130, 255));
+
+ // Custom Maps
+ if (m_singleplayer_cursor == 3)
+ Draw_ColoredString(10, 105, "Custom Maps", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 105, "Custom Maps", 255, 255, 255, 255, 1);
+
+ // Back
+ if (m_singleplayer_cursor == 4)
+ Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1);
+
+ // Map description & pic
+ switch(m_singleplayer_cursor) {
+ case 0:
+ Draw_Pic(256, 45, menu_ndu);
+ Draw_ColoredString(215, 155, "Lock and Load; Crashed Plane.", 255, 255, 255, 255, 1);
+ Draw_ColoredString(215, 165, "Divided. Thousands of Undead.", 255, 255, 255, 255, 1);
+ Draw_ColoredString(215, 175, "This is the Night of the Dead.", 255, 255, 255, 255, 1);
+ break;
+ case 1:
+ Draw_Pic(256, 45, menu_wh);
+ Draw_ColoredString(215, 155, "Old Warehouse full of Zombies!", 255, 255, 255, 255, 1);
+ Draw_ColoredString(215, 165, "Fight your way to the Power", 255, 255, 255, 255, 1);
+ Draw_ColoredString(215, 175, "Switch through the Hordes!", 255, 255, 255, 255, 1);
+ break;
+ case 2:
+ Draw_Pic(256, 45, menu_ch);
+ Draw_ColoredString(215, 155, "No Santa this year. Though we're", 255, 255, 255, 255, 1);
+ Draw_ColoredString(215, 165, "sure you will get presents from", 255, 255, 255, 255, 1);
+ Draw_ColoredString(215, 175, "the undead! Will you accept them?", 255, 255, 255, 255, 1);
+ break;
+ case 3:
+ Draw_Pic(256, 45, menu_custom);
+ Draw_ColoredString(215, 155, "Custom Maps made by Community", 255, 255, 255, 255, 1);
+ Draw_ColoredString(215, 165, "Members on the Fourm and on", 255, 255, 255, 255, 1);
+ Draw_ColoredString(215, 175, "Discord!", 255, 255, 255, 255, 1);
+ break;
+ }
+}
+
+
+void M_SinglePlayer_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ M_Menu_Main_f ();
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (++m_singleplayer_cursor >= SINGLEPLAYER_ITEMS)
+ m_singleplayer_cursor = 0;
+ break;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (--m_singleplayer_cursor < 0)
+ m_singleplayer_cursor = SINGLEPLAYER_ITEMS - 1;
+ break;
+
+ case K_ENTER:
+ m_entersound = true;
+
+ switch (m_singleplayer_cursor)
+ {
+ case 0:
+ key_dest = key_game;
+ if (sv.active)
+ Cbuf_AddText ("disconnect\n");
+ Cbuf_AddText ("maxplayers 1\n");
+ Cbuf_AddText ("map ndu\n");
+ loadingScreen = 1;
+ break;
+ case 1:
+ key_dest = key_game;
+ if (sv.active)
+ Cbuf_AddText ("disconnect\n");
+ Cbuf_AddText ("maxplayers 1\n");
+ Cbuf_AddText ("map warehouse\n");
+ loadingScreen = 2;
+ break;
+ case 2:
+ key_dest = key_game;
+ if (sv.active)
+ Cbuf_AddText ("disconnect\n");
+ Cbuf_AddText ("maxplayers 1\n");
+ Cbuf_AddText ("map christmas_special\n");
+ loadingScreen = 3;
+ break;
+ case 3:
+ M_Menu_Map_f ();
+ break;
+ case 4:
+ M_Menu_Main_f ();
+ break;
+ }
+ break;
+ }
+}
+
+//=============================================================================
+/* ACHIEVEMENT MENU */
+
+int m_achievement_cursor;
+int m_achievement_selected;
+int m_achievement_scroll[2];
+int total_unlocked_achievements;
+int total_locked_achievements;
+
+
+achievement_list_t achievement_list[MAX_ACHIEVEMENTS];
+qpic_t *achievement_locked;
+
+void Achievement_Init (void)
+{
+ achievement_list[0].img = Draw_CachePic("gfx/achievement/ready");
+ achievement_list[0].unlocked = 0;
+ achievement_list[0].progress = 0;
+ strcpy(achievement_list[0].name, "Ready..");
+ strcpy(achievement_list[0].description, "Reach round 5");
+
+ achievement_list[1].img = Draw_CachePic("gfx/achievement/steady");
+ achievement_list[1].unlocked = 0;
+ achievement_list[1].progress = 0;
+ strcpy(achievement_list[1].name, "Steady..");
+ strcpy(achievement_list[1].description, "Reach round 10");
+
+ achievement_list[2].img = Draw_CachePic("gfx/achievement/go_hell_no");
+ achievement_list[2].unlocked = 0;
+ achievement_list[2].progress = 0;
+ strcpy(achievement_list[2].name, "Go? Hell No...");
+ strcpy(achievement_list[2].description, "Reach round 15");
+
+ achievement_list[3].img = Draw_CachePic("gfx/achievement/where_legs_go");
+ achievement_list[3].unlocked = 0;
+ achievement_list[3].progress = 0;
+ strcpy(achievement_list[3].name, "Where Did Legs Go?");
+ strcpy(achievement_list[3].description, "Turn a zombie into a crawler");
+
+ achievement_list[4].img = Draw_CachePic("gfx/achievement/the_f_bomb");
+ achievement_list[4].unlocked = 0;
+ achievement_list[4].progress = 0;
+ strcpy(achievement_list[4].name, "The F Bomb");
+ strcpy(achievement_list[4].description, "Use the Nuke power-up to kill a single Zombie");
+
+ /*achievement_list[3].img = Draw_CachePic("gfx/achievement/beast");
+ achievement_list[3].unlocked = 0;
+ achievement_list[3].progress = 0;
+ strcpy(achievement_list[3].name, "Beast");
+ strcpy(achievement_list[3].description, "Survive round after round 5 with knife and grenades only");
+
+ achievement_list[4].img = Draw_CachePic("gfx/achievement/survival");
+ achievement_list[4].unlocked = 0;
+ achievement_list[4].progress = 0;
+ strcpy(achievement_list[4].name, "Survival Expert");
+ strcpy(achievement_list[4].description, "Use pistol and knife only to reach round 10");
+
+ achievement_list[5].img = Draw_CachePic("gfx/achievement/boomstick");
+ achievement_list[5].unlocked = 0;
+ achievement_list[5].progress = 0;
+ strcpy(achievement_list[5].name, "Boomstick");
+ strcpy(achievement_list[5].description, "3 for 1 with shotgun blast");
+
+ achievement_list[6].img = Draw_CachePic("gfx/achievement/boom_headshots");
+ achievement_list[6].unlocked = 0;
+ achievement_list[6].progress = 0;
+ strcpy(achievement_list[6].name, "Boom Headshots");
+ strcpy(achievement_list[6].description, "Get 10 headshots");
+
+ achievement_list[7].img = Draw_CachePic("gfx/achievement/where_did");
+ achievement_list[7].unlocked = 0;
+ achievement_list[7].progress = 0;
+ strcpy(achievement_list[7].name, "Where Did Legs Go?");
+ strcpy(achievement_list[7].description, "Make a crawler zombie");
+
+ achievement_list[8].img = Draw_CachePic("gfx/achievement/keep_the_change");
+ achievement_list[8].unlocked = 0;
+ achievement_list[8].progress = 0;
+ strcpy(achievement_list[8].name, "Keep The Change");
+ strcpy(achievement_list[8].description, "Purchase everything");
+
+ achievement_list[9].img = Draw_CachePic("gfx/achievement/big_thanks");
+ achievement_list[9].unlocked = 0;
+ achievement_list[9].progress = 0;
+ strcpy(achievement_list[9].name, "Big Thanks To Explosion");
+ strcpy(achievement_list[9].description, "Get 10 kills with one grenade");
+
+ achievement_list[10].img = Draw_CachePic("gfx/achievement/warmed-up");
+ achievement_list[10].unlocked = 0;
+ achievement_list[10].progress = 0;
+ strcpy(achievement_list[10].name, "Getting Warmed-Up");
+ strcpy(achievement_list[10].description, "Achieve 10 achievements");
+
+ achievement_list[11].img = Draw_CachePic("gfx/achievement/mysterybox_maniac");
+ achievement_list[11].unlocked = 0;
+ achievement_list[11].progress = 0;
+ strcpy(achievement_list[11].name, "Mysterybox Maniac");
+ strcpy(achievement_list[11].description, "use mysterybox 20 times");
+
+ achievement_list[12].img = Draw_CachePic("gfx/achievement/instant_help");
+ achievement_list[12].unlocked = 0;
+ achievement_list[12].progress = 0;
+ strcpy(achievement_list[12].name, "Instant Help");
+ strcpy(achievement_list[12].description, "Kill 100 zombies with insta-kill");
+
+ achievement_list[13].img = Draw_CachePic("gfx/achievement/blow_the_bank");
+ achievement_list[13].unlocked = 0;
+ achievement_list[13].progress = 0;
+ strcpy(achievement_list[13].name, "Blow The Bank");
+ strcpy(achievement_list[13].description, "earn 1,000,000");
+
+ achievement_list[14].img = Draw_CachePic("gfx/achievement/ammo_cost");
+ achievement_list[14].unlocked = 0;
+ achievement_list[14].progress = 0;
+ strcpy(achievement_list[14].name, "Ammo Cost Too Much");
+ strcpy(achievement_list[14].description, "After round 5, don't fire a bullet for whole round");
+
+ achievement_list[15].img = Draw_CachePic("gfx/achievement/the_f_bomb");
+ achievement_list[15].unlocked = 0;
+ achievement_list[15].progress = 0;
+ strcpy(achievement_list[15].name, "The F Bomb");
+ strcpy(achievement_list[15].description, "Only nuke one zombie");
+
+ achievement_list[16].img = Draw_CachePic("gfx/achievement/why_are");
+ achievement_list[16].unlocked = 0;
+ achievement_list[16].progress = 0;
+ strcpy(achievement_list[16].name, "Why Are We Waiting?");
+ strcpy(achievement_list[16].description, "Stand still for 2 minutes");
+
+ achievement_list[17].img = Draw_CachePic("gfx/achievement/never_missed");
+ achievement_list[17].unlocked = 0;
+ achievement_list[17].progress = 0;
+ strcpy(achievement_list[17].name, "Never Missed A Shot");
+ strcpy(achievement_list[17].description, "Make it to round 5 without missing a shot");
+
+ achievement_list[18].img = Draw_CachePic("gfx/achievement/300_bastards_less");
+ achievement_list[18].unlocked = 0;
+ achievement_list[18].progress = 0;
+ strcpy(achievement_list[18].name, "300 Bastards less");
+ strcpy(achievement_list[18].description, "Kill 300 zombies");
+
+ achievement_list[19].img = Draw_CachePic("gfx/achievement/music_fan");
+ achievement_list[19].unlocked = 0;
+ achievement_list[19].progress = 0;
+ strcpy(achievement_list[19].name, "Music Fan");
+ strcpy(achievement_list[19].description, "Turn on radio 20 times");
+
+ achievement_list[20].img = Draw_CachePic("gfx/achievement/one_clip");
+ achievement_list[20].unlocked = 0;
+ achievement_list[20].progress = 0;
+ strcpy(achievement_list[20].name, "One Clip");
+ strcpy(achievement_list[20].description, "Complete a round with mg42 without reloading");
+
+ achievement_list[21].img = Draw_CachePic("gfx/achievement/one_20_20");
+ achievement_list[21].unlocked = 0;
+ achievement_list[21].progress = 0;
+ strcpy(achievement_list[21].name, "One Clip, 20 Bullets, 20 Headshots");
+ strcpy(achievement_list[21].description, "Score 20 headshots, with 20 bullets, and don't reload");
+
+ achievement_list[22].img = Draw_CachePic("gfx/achievement/and_stay_out");
+ achievement_list[22].unlocked = 0;
+ achievement_list[22].progress = 0;
+ strcpy(achievement_list[22].name, "And Stay out");
+ strcpy(achievement_list[22].description, "Don't let zombies in for 2 rounds");*/
+
+ achievement_locked = Draw_CachePic("gfx/achievement/achievement_locked");
+
+ m_achievement_scroll[0] = 0;
+ m_achievement_scroll[1] = 0;
+}
+
+
+void Load_Achivements (void)
+{
+ int achievement_file;
+ achievement_file = sceIoOpen(va("%s/data/ach.dat",com_gamedir), PSP_O_RDONLY, 0);
+
+ if (achievement_file >= 0) {
+ char* buffer = (char*)calloc(2, sizeof(char));
+ for (int i = 0; i < MAX_ACHIEVEMENTS; i++) {
+ sceIoRead(achievement_file, buffer, sizeof(char)*2);
+ achievement_list[i].unlocked = atoi(buffer);
+ sceIoRead(achievement_file, buffer, sizeof(char)*2);
+ achievement_list[i].progress = atoi(buffer);
+ }
+ } else {
+ achievement_file = sceIoOpen(va("%s/data/ach.dat",com_gamedir), PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC, 0);
+
+ for (int i = 0; i < MAX_ACHIEVEMENTS; i++) {
+ sceIoWrite(achievement_file, "0\n", sizeof(char)*2);
+ sceIoWrite(achievement_file, "0\n", sizeof(char)*2);
+ }
+ }
+ sceIoClose(achievement_file);
+}
+void Save_Achivements (void)
+{
+ int achievement_file;
+ achievement_file = sceIoOpen(va("%s/data/ach.dat",com_gamedir), PSP_O_WRONLY, 0);
+
+ if (achievement_file >= 0) {
+ for (int i = 0; i < MAX_ACHIEVEMENTS; i++) {
+ char* buffer = va("%i\n", achievement_list[i].unlocked);
+ char* buffer2 = va("%i\n", achievement_list[i].progress);
+ sceIoWrite(achievement_file, va("%i\n", achievement_list[i].unlocked), strlen(buffer));
+ sceIoWrite(achievement_file, va("%i\n", achievement_list[i].progress), strlen(buffer2));
+ }
+ } else {
+ Load_Achivements();
+ }
+}
+
+
+void M_Menu_Achievement_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_achievement;
+ m_entersound = true;
+ //Load_Achivements();
+}
+
+void M_Achievement_Draw (void)
+{
+ int i, b, y;
+ int unlocked_achievement[MAX_ACHIEVEMENTS];
+ int locked_achievement[MAX_ACHIEVEMENTS];
+ int maxLenght = floor((vid.width - 155)/8);
+ int stringLenght;
+ char *description;
+ char *string_line = (char*) malloc(maxLenght);
+ int lines;
+
+ y = 0;
+ total_unlocked_achievements = 0;
+ total_locked_achievements = 0;
+
+ for (i = 0; i < MAX_ACHIEVEMENTS; i++)
+ {
+ unlocked_achievement[i] = -1;
+ locked_achievement[i] = -1;
+ if (achievement_list[i].unlocked)
+ {
+ unlocked_achievement[total_unlocked_achievements] = i;
+ total_unlocked_achievements += 1;
+ }
+ else
+ {
+ locked_achievement[total_locked_achievements] = i;
+ total_locked_achievements += 1;
+ }
+ }
+
+ // Background
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+
+ // Fill black to make everything easier to see
+ Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102));
+
+ // Version String
+ Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1);
+
+ if (!m_achievement_selected)
+ {
+ Draw_FillByColor(15, 8, 225, 12, GU_RGBA(204, 0, 0, 100));
+ Draw_FillByColor(240, 8, 225, 12, GU_RGBA(0, 0, 0, 100));
+
+ if (total_unlocked_achievements <= 0)
+ {
+ Draw_FillByColor(15, 25, vid.width - 30, 60, GU_RGBA (0, 0, 0, 100));
+ Draw_Pic (20, 30 + y, achievement_locked);
+ Draw_String (125, 30 + y, "No achievements unlocked :(");
+ }
+ else
+ {
+ for (i = 0; i < 3; i++)
+ {
+ if (unlocked_achievement[i + m_achievement_scroll[0]] >= 0)
+ {
+ Draw_FillByColor(15, 25 + y, vid.width - 30, 60, GU_RGBA (0, 0, 0, 100));
+
+ Draw_Pic (20, 30 + y, achievement_list[unlocked_achievement[i + m_achievement_scroll[0]]].img);
+ Draw_String (125, 30 + y, achievement_list[unlocked_achievement[i + m_achievement_scroll[0]]].name);
+ description = achievement_list[unlocked_achievement[i + m_achievement_scroll[0]]].description;
+ stringLenght = strlen(description);
+ lines = stringLenght/maxLenght;
+ if ((maxLenght % stringLenght) != 0)
+ lines++;
+
+ for (b = 0; b < lines; b++) {
+ strncpy(string_line, description+maxLenght*b, (maxLenght-1));
+ Draw_String (125, 40 + y + b*8, string_line);
+ }
+
+ //if (stringLenght <= maxLenght)
+ // Draw_String (125, 40 + y, description);
+
+ y += 75;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (total_locked_achievements <= 0)
+ {
+ Draw_FillByColor(15, 25, vid.width - 30, 60, GU_RGBA (0, 0, 0, 100));
+ Draw_Pic (20, 30 + y, achievement_locked);
+ Draw_String (125, 30 + y, "All achievements unlocked :)");
+ }
+
+ Draw_FillByColor(15, 8, 225, 12, GU_RGBA(0, 0, 0, 100));
+ Draw_FillByColor(240, 8, 225, 12, GU_RGBA(204, 0, 0, 100));
+
+ for (i = 0; i < 3; i++)
+ {
+ if (locked_achievement[i + m_achievement_scroll[1]] >= 0)
+ {
+ Draw_FillByColor(15, 25 + y, vid.width - 30, 60, GU_RGBA (0, 0, 0, 100));
+
+ Draw_Pic (20, 30 + y, achievement_locked);
+ Draw_String (125, 30 + y, achievement_list[locked_achievement[i + m_achievement_scroll[1]]].name);
+ description = achievement_list[locked_achievement[i + m_achievement_scroll[1]]].description;
+ stringLenght = strlen(description);
+ lines = stringLenght/maxLenght;
+ if ((maxLenght % stringLenght) != 0)
+ lines++;
+
+ for (b = 0; b < lines; b++) {
+ strncpy(string_line, description+maxLenght*b, (maxLenght-1));
+ Draw_String (125, 40 + y + b*8, string_line);
+ }
+
+
+ //if (stringLenght <= maxLenght)
+ // Draw_String (125, 40 + y, description);
+
+ y += 70;
+ }
+ }
+ }
+
+ free(string_line);
+ Draw_String (15, 10, "Unlocked Achievements");
+ Draw_String (vid.width - 167, 10, "Locked Achievements");
+}
+
+void M_Achievement_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ if (key_dest == key_menu_pause)
+ M_Paused_Menu_f();
+ else
+ M_Menu_Main_f ();
+ break;
+
+ case K_AUX1:
+ case K_LEFTARROW:
+ m_achievement_selected = 0;
+ break;
+
+ case K_AUX2:
+ case K_RIGHTARROW:
+ m_achievement_selected = 1;
+ break;
+
+ case K_UPARROW:
+ m_achievement_scroll[m_achievement_selected]--;
+ if (m_achievement_scroll[m_achievement_selected] < 0)
+ m_achievement_scroll[m_achievement_selected] = 0;
+ break;
+
+ case K_DOWNARROW:
+ m_achievement_scroll[m_achievement_selected]++;
+
+ if (m_achievement_selected)
+ {
+ if (m_achievement_scroll[1] > total_locked_achievements - 3)
+ m_achievement_scroll[1] = total_locked_achievements - 3;
+ }
+ else
+ {
+ if (m_achievement_scroll[0] > total_unlocked_achievements - 3)
+ m_achievement_scroll[0] = total_unlocked_achievements - 3;
+ }
+ if (m_achievement_scroll[m_achievement_selected] < 0)
+ m_achievement_scroll[m_achievement_selected] = 0;
+ break;
+
+ case K_ENTER:
+ m_entersound = true;
+ switch (m_achievement_cursor)
+ {
+ }
+ }
+}
+
+//=============================================================================
+/* MULTIPLAYER MENU */
+
+int m_multiplayer_cursor;
+
+#define MULTIPLAYER_ITEMS 6
+
+void M_Menu_MultiPlayer_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_multiplayer;
+ m_entersound = true;
+}
+
+#include
+void M_MultiPlayer_Draw (void)
+{
+ //int f;
+
+
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+ //else
+ //Draw_AlphaPic (0, 0, pause_bk, 0.4);
+ //f = (int)(host_time * 10)%6;
+
+ //M_DrawTransPic (54, 32 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
+
+
+ if (m_multiplayer_cursor == 0)
+ M_Print ((vid.width - ((9 * 8) + 16)), (vid.height - 64), "Join Game");
+ else
+ M_PrintWhite ((vid.width - ((9 * 8) + 16)), (vid.height - 64), "Join Game");
+
+ if (m_multiplayer_cursor == 1)
+ M_Print ((vid.width - ((9 * 8) + 16)), (vid.height - 56), "Host Game");
+ else
+ M_PrintWhite ((vid.width - ((9 * 8) + 16)), (vid.height - 56), "Host Game");
+
+ if (m_multiplayer_cursor == 2)
+ M_Print ((vid.width - ((12 * 8) + 16)), (vid.height - 48), "Player Setup");
+ else
+ M_PrintWhite ((vid.width - ((12 * 8) + 16)), (vid.height - 48), "Player Setup");
+
+
+ if (m_multiplayer_cursor == 3)
+ M_Print ((vid.width - ((11 * 8) + 16)), (vid.height - 40), "Server List");
+ else
+ M_PrintWhite ((vid.width - ((11 * 8) + 16)), (vid.height - 40), "Server List");
+
+
+ // M_PrintOld (72, 97, "Slist ");
+ if (m_multiplayer_cursor == 4)
+ M_Print ((vid.width - ((14 * 8) + 16)), (vid.height - 32), "Infrastructure");
+ else
+ M_PrintWhite ((vid.width - ((14 * 8) + 16)), (vid.height - 32), "Infrastructure");
+
+ if (m_multiplayer_cursor == 5)
+ M_Print ((vid.width - ((5 * 8) + 16)), (vid.height - 24), "Adhoc");
+ else
+ M_PrintWhite ((vid.width - ((5 * 8) + 16)), (vid.height - 24), "Adhoc");
+
+ // M_PrintOld (72, 117, "Infrastructure ");
+ //M_DrawCheckboxOld (220, 117, tcpipAvailable && !tcpipAdhoc);
+ M_DrawCheckbox (220, vid.height - 32, tcpipAvailable && !tcpipAdhoc);
+
+ //M_PrintOld (72, 137, "Adhoc ");
+ //M_DrawCheckboxOld (220, 137, tcpipAvailable && tcpipAdhoc);
+ M_DrawCheckbox (220, vid.height - 24, tcpipAvailable && tcpipAdhoc);
+
+ if (serialAvailable || ipxAvailable || tcpipAvailable)
+ return;
+
+ //M_PrintWhiteOld ((160) - ((35*8)/2), 180, "No Network Communications Available");
+ M_PrintWhiteOld ((160) - ((35*8)/2), 180, "PSP MULTIPLAYER IS NOT OFFICALY SUPPORTED");
+ M_PrintWhiteOld ((160) - ((35*8)/2), 188, "DO NOT ASK HOW TO PLAY ONLINE ON PSP");
+
+}
+
+
+void M_MultiPlayer_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ M_Menu_Main_f ();
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS)
+ m_multiplayer_cursor = 0;
+ break;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (--m_multiplayer_cursor < 0)
+ m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1;
+ break;
+
+ case K_ENTER:
+ m_entersound = true;
+ switch (m_multiplayer_cursor)
+ {
+ case 0:
+ if ((serialAvailable || ipxAvailable || tcpipAvailable) && sceWlanDevIsPowerOn())
+ {
+ //M_Menu_Net_f ();
+ M_Menu_LanConfig_f ();
+ }
+ break;
+
+ case 1:
+ if ((serialAvailable || ipxAvailable || tcpipAvailable) && sceWlanDevIsPowerOn())
+ {
+ //M_Menu_Net_f ();
+ M_Menu_LanConfig_f ();
+ }
+ break;
+
+ case 2:
+ M_Menu_Setup_f ();
+ break;
+
+ case 3:
+ M_Menu_ServerList_f();
+ break;
+
+ case 4:
+ Datagram_Shutdown();
+
+ tcpipAvailable = !tcpipAvailable;
+
+ if(tcpipAvailable && sceWlanDevIsPowerOn())
+ {
+ tcpipAdhoc = false;
+ net_driver_to_use = 0;
+ Datagram_Init();
+ }
+ break;
+ case 5:
+ Datagram_Shutdown();
+
+ tcpipAvailable = !tcpipAvailable;
+
+ if(tcpipAvailable && sceWlanDevIsPowerOn())
+ {
+ tcpipAdhoc = true;
+ net_driver_to_use = 1;
+ Datagram_Init();
+ }
+ break;
+ }
+ }
+}
+
+//=============================================================================
+/* SETUP MENU */
+
+int setup_cursor = 5;
+int setup_cursor_table[] = {40, 56, 72, 96, 120, 156};
+
+char setup_hostname[16];
+char setup_myname[16];
+
+// Define PSP specific variables
+#define NUM_SETUP_CMDS 4
+extern int totalAccessPoints;
+extern int accessPointNumber[100];
+char setup_accesspoint[64];
+
+void M_Menu_Setup_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_setup;
+ m_entersound = true;
+ Q_strcpy(setup_myname, cl_name.string);
+ Q_strcpy(setup_hostname, hostname.string);
+ //setup_accesspoint ;
+
+ if(totalAccessPoints)
+ {
+ sceUtilityGetNetParam(accessPointNumber[(int)accesspoint.value], 0, (netData*)setup_accesspoint);
+ }
+}
+
+void M_Setup_Draw (void)
+{
+
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+
+ if (setup_cursor == 0)
+ M_Print (64, 72, "Access Point");
+ else
+ M_PrintWhite (64,72, "Access Point");
+
+ //M_PrintOld (64, 40, "Access Point");
+ //M_DrawTextBox (160, 32, 16, 1);
+ M_PrintOld (168, 72, setup_accesspoint);
+
+ if (setup_cursor == 1)
+ M_Print (64, 92, "Hostname");
+ else
+ M_PrintWhite (64,92, "Hostname");
+
+ M_DrawTextBox (160, 84, 16, 1);
+ M_PrintOld (168, 92, setup_hostname);
+
+ if (setup_cursor == 2)
+ M_Print (64, 104, "Player Name");
+ else
+ M_PrintWhite (64,104, "Player Name");
+
+ M_DrawTextBox (160, 96, 16, 1);
+ M_PrintOld (168, 104, setup_myname);
+
+ if (setup_cursor == 3)
+ M_Print (186, 120, "Accept Changes");
+ else
+ M_PrintWhite (186,120, "Accept Changes");
+}
+
+
+void M_Setup_Key (int k)
+{
+ int l;
+ int offset = 0;
+
+ switch (k)
+ {
+ case K_ESCAPE:
+ M_Menu_MultiPlayer_f ();
+ break;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ setup_cursor--;
+ if (setup_cursor < 0)
+ setup_cursor = NUM_SETUP_CMDS-1;
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ setup_cursor++;
+ if (setup_cursor >= NUM_SETUP_CMDS)
+ setup_cursor = 0;
+ break;
+
+ case K_LEFTARROW:
+ if (setup_cursor == 0)
+ {
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if(accesspoint.value > 1)
+ {
+ Cvar_SetValue("accesspoint", accesspoint.value-1);
+ //blubso
+ sceUtilityGetNetParam(accessPointNumber[(int)accesspoint.value], 0, (netData*)setup_accesspoint);
+ }
+ }
+ offset = 1;
+ if (setup_cursor < 2+offset)
+ return;
+ S_LocalSound ("sounds/menu/navigate.wav");
+ break;
+ case K_RIGHTARROW:
+ if (setup_cursor == 0)
+ {
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if(accesspoint.value < totalAccessPoints)
+ {
+ Cvar_SetValue("accesspoint", accesspoint.value+1);
+ //blubso
+ sceUtilityGetNetParam(accessPointNumber[(int)accesspoint.value], 0, (netData*)setup_accesspoint);
+ }
+ }
+
+ offset = 1;
+
+ if (setup_cursor < 2+offset)
+ return;
+
+ S_LocalSound ("sounds/menu/navigate.wav");
+ break;
+
+ case K_INS:
+ offset = 1;
+ if (setup_cursor == 0+offset)
+ {
+ M_Menu_OSK_f(setup_hostname, setup_hostname, 16);
+ break;
+ }
+
+ if (setup_cursor == 1+offset)
+ {
+ M_Menu_OSK_f(setup_myname, setup_myname,16);
+ break;
+ }
+ break;
+
+ case K_ENTER:
+ offset = 1;
+ if (setup_cursor == 0+offset || setup_cursor == 1+offset || setup_cursor== 0)
+ return;
+
+ //if (setup_cursor == 2+offset || setup_cursor == 3+offset)
+ // goto forward;
+
+ // if (setup_cursor < 4+offset)
+ // break;
+
+ // setup_cursor == 4 (OK)
+ if (Q_strcmp(cl_name.string, setup_myname) != 0)
+ Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) );
+ if (Q_strcmp(hostname.string, setup_hostname) != 0)
+ Cvar_Set("hostname", setup_hostname);
+ m_entersound = true;
+ M_Menu_MultiPlayer_f ();
+ break;
+
+ case K_BACKSPACE:
+ offset = 1;
+ if (setup_cursor == 0+offset)
+ {
+ if (strlen(setup_hostname))
+ setup_hostname[strlen(setup_hostname)-1] = 0;
+ }
+
+ if (setup_cursor == 1+offset)
+ {
+ if (strlen(setup_myname))
+ setup_myname[strlen(setup_myname)-1] = 0;
+ }
+ break;
+
+ default:
+ if (k < 32 || k > 127)
+ break;
+
+ offset = 1;
+
+ if (setup_cursor == 0+offset)
+ {
+ l = strlen(setup_hostname);
+ if (l < 15)
+ {
+ setup_hostname[l+1] = 0;
+ setup_hostname[l] = k;
+ }
+ }
+ if (setup_cursor == 1+offset)
+ {
+ l = strlen(setup_myname);
+ if (l < 15)
+ {
+ setup_myname[l+1] = 0;
+ setup_myname[l] = k;
+ }
+ }
+ }
+}
+
+//=============================================================================
+/* SERVER LIST MENU */
+void M_Menu_SEdit_f (void);
+
+#define MENU_X 50
+#define MENU_Y 21
+#define TITLE_Y 4
+#define STAT_Y 166
+
+int slist_cursor = 0, slist_mins = 0, slist_maxs = 15, slist_state;
+
+void M_Menu_ServerList_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_slist;
+ m_entersound = true;
+
+ slist_state = 0;
+}
+
+void M_ServerList_Draw (void)
+{
+ int serv, line;
+
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+ //else
+ //Draw_AlphaPic (0, 0, pause_bk, 0.4);
+
+ M_DrawTextBox (MENU_X, TITLE_Y, 23, 1);
+ M_PrintWhiteOld (MENU_X + 60, TITLE_Y + 8, "Server List");
+
+ if (!slist[0].server)
+ {
+ M_PrintWhiteOld (84, MENU_Y + 16 + 16, "Empty server list");
+ M_PrintOld (60, MENU_Y + 16 + 32, "Press TRIANGLE to add a server");
+ M_PrintOld (60, MENU_Y + 16 + 40, "Press SQUARE to edit a server");
+ M_PrintOld (60, MENU_Y + 16 + 48, "Press CIRCLE to exit");
+ return;
+ }
+
+ M_DrawTextBox (MENU_X, STAT_Y, 23, 1);
+ M_DrawTextBox (MENU_X, MENU_Y, 23, slist_maxs - slist_mins + 1);
+ for (serv = slist_mins, line = 1 ; serv <= slist_maxs && serv < MAX_SERVER_LIST && slist[serv].server ; serv++, line++)
+ M_PrintOld (MENU_X + 18, line * 8 + MENU_Y, va("%1.21s", slist[serv].description));
+ M_PrintWhiteOld (MENU_X, STAT_Y - 4, "TRIANGLE = add server, SQUARE = edit");
+ M_PrintWhiteOld (MENU_X + 18, STAT_Y + 8, va("%1.22s", slist[slist_cursor].server));
+ M_DrawCharacter2 (MENU_X + 8, (slist_cursor - slist_mins + 1) * 8 + MENU_Y, 12+((int)(realtime*4)&1));
+}
+
+void M_ServerList_Key (int key)
+{
+ int slist_length;
+
+ if (!slist[0].server && key != K_ESCAPE && key != K_DEL)
+ return;
+
+ switch (key)
+ {
+ case K_ESCAPE:
+ M_Menu_MultiPlayer_f ();
+ break;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (slist_cursor > 0)
+ {
+ if (keydown[K_CTRL])
+ SList_Switch (slist_cursor, slist_cursor - 1);
+ slist_cursor--;
+ }
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (keydown[K_CTRL])
+ {
+ if (slist_cursor != SList_Length() - 1)
+ {
+ SList_Switch (slist_cursor, slist_cursor + 1);
+ slist_cursor++;
+ }
+ }
+ else if (slist_cursor < MAX_SERVER_LIST - 1 && slist[slist_cursor+1].server)
+ slist_cursor++;
+ break;
+
+ case K_HOME:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ slist_cursor = 0;
+ break;
+
+ case K_END:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ slist_cursor = SList_Length() - 1;
+ break;
+
+ case K_PGUP:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ slist_cursor -= (slist_maxs - slist_mins);
+ if (slist_cursor < 0)
+ slist_cursor = 0;
+ break;
+
+ case K_PGDN:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ slist_cursor += (slist_maxs - slist_mins);
+ if (slist_cursor >= MAX_SERVER_LIST)
+ slist_cursor = MAX_SERVER_LIST - 1;
+ while (!slist[slist_cursor].server)
+ slist_cursor--;
+ break;
+
+ case K_ENTER:
+ if (keydown[K_CTRL])
+ {
+ M_Menu_SEdit_f ();
+ break;
+ }
+ m_state = m_main;
+ M_ToggleMenu_f ();
+ Cbuf_AddText (va("connect \"%s\"\n", slist[slist_cursor].server));
+ break;
+
+ //case 'e':
+ //case 'E':
+ case K_INS:
+ M_Menu_SEdit_f ();
+ break;
+
+ case K_DEL:
+ S_LocalSound ("sounds/menu/enter.wav");
+ if ((slist_length = SList_Length()) < MAX_SERVER_LIST)
+ {
+ if (keydown[K_CTRL] && slist_length > 0)
+ {
+ if (slist_cursor < slist_length - 1)
+ memmove (&slist[slist_cursor+2], &slist[slist_cursor+1], (slist_length - slist_cursor - 1) * sizeof(slist[0]));
+ SList_Reset_NoFree (slist_cursor + 1);
+ SList_Set (slist_cursor + 1, "127.0.0.1", "");
+ if (slist_length)
+ slist_cursor++;
+ }
+ else
+ {
+ memmove (&slist[slist_cursor+1], &slist[slist_cursor], (slist_length - slist_cursor) * sizeof(slist[0]));
+ SList_Reset_NoFree (slist_cursor);
+ SList_Set (slist_cursor, "127.0.0.1", "");
+ }
+ }
+ break;
+
+ case K_LEFTARROW:
+ S_LocalSound("sounds/menu/enter.wav");
+ if ((slist_length = SList_Length()) > 0)
+ {
+ SList_Reset (slist_cursor);
+ if (slist_cursor > 0 && slist_length - 1 == slist_cursor)
+ {
+ slist_cursor--;
+ }
+ else
+ {
+ memmove (&slist[slist_cursor], &slist[slist_cursor+1], (slist_length - slist_cursor - 1) * sizeof(slist[0]));
+ SList_Reset_NoFree (slist_length - 1);
+ }
+ }
+ break;
+ }
+
+ if (slist_cursor < slist_mins)
+ {
+ slist_maxs -= (slist_mins - slist_cursor);
+ slist_mins = slist_cursor;
+ }
+ if (slist_cursor > slist_maxs)
+ {
+ slist_mins += (slist_cursor - slist_maxs);
+ slist_maxs = slist_cursor;
+ }
+}
+
+#define SERV_X 60
+#define SERV_Y 64
+#define DESC_X 60
+#define DESC_Y 40
+#define SERV_L 23
+#define DESC_L 23
+
+#define SLIST_BUFSIZE 128
+
+static char slist_serv[SLIST_BUFSIZE], slist_desc[SLIST_BUFSIZE];
+static int slist_serv_max, slist_serv_min, slist_desc_max, slist_desc_min, sedit_state;
+
+void M_Menu_SEdit_f (void)
+{
+ int size;
+
+ key_dest = key_menu;
+ m_state = m_sedit;
+ m_entersound = true;
+
+ sedit_state = 0;
+ Q_strncpyz (slist_serv, slist[slist_cursor].server, sizeof(slist_serv));
+ Q_strncpyz (slist_desc, slist[slist_cursor].description, sizeof(slist_desc));
+ slist_serv_max = (size = strlen(slist_serv)) > SERV_L ? size : SERV_L;
+ slist_serv_min = slist_serv_max - SERV_L;
+ slist_desc_max = (size = strlen(slist_desc)) > DESC_L ? size : DESC_L;
+ slist_desc_min = slist_desc_max - DESC_L;
+}
+
+void M_SEdit_Draw (void)
+{
+
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+ //else
+ //Draw_AlphaPic (0, 0, pause_bk, 0.4);
+
+ M_DrawTextBox (SERV_X, SERV_Y, 23, 1);
+ M_DrawTextBox (DESC_X, DESC_Y, 23, 1);
+ M_PrintWhiteOld (SERV_X, SERV_Y - 4, "Hostname/IP:");
+ M_PrintWhiteOld (DESC_X, DESC_Y - 4, "Description:");
+ M_PrintOld (SERV_X + 9, SERV_Y + 8, va("%1.23s", slist_serv + slist_serv_min));
+ M_PrintOld (DESC_X + 9, DESC_Y + 8, va("%1.23s", slist_desc + slist_desc_min));
+ if (sedit_state == 0)
+ M_DrawCharacter2 (SERV_X + 9 + 8*(strlen(slist_serv) - slist_serv_min), SERV_Y + 8, 10+((int)(realtime*4)&1));
+ else
+ M_DrawCharacter2 (DESC_X + 9 + 8*(strlen(slist_desc) - slist_desc_min), DESC_Y + 8, 10+((int)(realtime*4)&1));
+}
+
+void M_SEdit_Key (int key)
+{
+ int l;
+
+ switch (key)
+ {
+ case K_ESCAPE:
+ M_Menu_ServerList_f ();
+ break;
+
+ case K_ENTER:
+ SList_Set (slist_cursor, slist_serv, slist_desc);
+ M_Menu_ServerList_f ();
+ break;
+
+ case K_UPARROW:
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ sedit_state = !sedit_state;
+ break;
+
+ case K_INS:
+ if (sedit_state == 0)
+ M_Menu_OSK_f(slist_serv, slist_serv, 16);
+ else
+ M_Menu_OSK_f(slist_desc, slist_desc, 16);
+ break;
+
+ case K_BACKSPACE:
+ if (sedit_state == 0)
+ {
+ if ((l = strlen(slist_serv)))
+ slist_serv[--l] = 0;
+ if (strlen(slist_serv) - 6 < slist_serv_min && slist_serv_min)
+ {
+ slist_serv_min--;
+ slist_serv_max--;
+ }
+ }
+ else
+ {
+ if ((l = strlen(slist_desc)))
+ slist_desc[--l] = 0;
+ if (strlen(slist_desc) - 6 < slist_desc_min && slist_desc_min)
+ {
+ slist_desc_min--;
+ slist_desc_max--;
+ }
+ }
+ break;
+
+ default:
+ if (key < 32 || key > 127)
+ break;
+
+ if (sedit_state == 0)
+ {
+ l = strlen (slist_serv);
+ if (l < SLIST_BUFSIZE - 1)
+ {
+ slist_serv[l+1] = 0;
+ slist_serv[l] = key;
+ l++;
+ }
+ if (l > slist_serv_max)
+ {
+ slist_serv_min++;
+ slist_serv_max++;
+ }
+ }
+ else
+ {
+ l = strlen (slist_desc);
+ if (l < SLIST_BUFSIZE - 1)
+ {
+ slist_desc[l+1] = 0;
+ slist_desc[l] = key;
+ l++;
+ }
+ if (l > slist_desc_max)
+ {
+ slist_desc_min++;
+ slist_desc_max++;
+ }
+ }
+ break;
+ }
+}
+
+//=============================================================================
+/* NET MENU */
+
+int m_net_cursor;
+int m_net_items;
+int m_net_saveHeight;
+
+char *net_helpMessage [] =
+{
+/* .........1.........2.... */
+ " ",
+ " Two computers connected",
+ " through two modems. ",
+ " ",
+
+ " ",
+ " Two computers connected",
+ " by a null-modem cable. ",
+ " ",
+
+ " Novell network LANs ",
+ " or Windows 95 DOS-box. ",
+ " ",
+ "(LAN=Local Area Network)",
+
+ " Commonly used to play ",
+ " over the Internet, but ",
+ " also used on a Local ",
+ " Area Network. "
+};
+
+void M_Menu_Net_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_net;
+ m_entersound = true;
+ m_net_items = 4;
+
+ if (m_net_cursor >= m_net_items)
+ m_net_cursor = 0;
+ m_net_cursor--;
+ M_Net_Key (K_DOWNARROW);
+}
+
+
+void M_Net_Draw (void)
+{
+ int f;
+
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+ //else
+ //Draw_AlphaPic (0, 0, pause_bk, 0.4);
+
+ f = 32;
+ qpic_t *p;
+
+ if (serialAvailable)
+ {
+ p = Draw_CachePic ("gfx/netmen1.lmp");
+ }
+ else
+ {
+ p = Draw_CachePic ("gfx/dim_modm.lmp");
+ }
+
+ if (p)
+ M_DrawTransPic (72, f, p);
+
+ f += 19;
+
+ if (serialAvailable)
+ {
+ p = Draw_CachePic ("gfx/netmen2.lmp");
+ }
+ else
+ {
+ p = Draw_CachePic ("gfx/dim_drct.lmp");
+ }
+
+ if (p)
+ M_DrawTransPic (72, f, p);
+
+ f += 19;
+ if (ipxAvailable)
+ p = Draw_CachePic ("gfx/netmen3.lmp");
+ else
+ p = Draw_CachePic ("gfx/dim_ipx.lmp");
+ M_DrawTransPic (72, f, p);
+
+ f += 19;
+ if (tcpipAvailable)
+ p = Draw_CachePic ("gfx/netmen4.lmp");
+ else
+ p = Draw_CachePic ("gfx/dim_tcp.lmp");
+ M_DrawTransPic (72, f, p);
+
+ if (m_net_items == 5) // JDC, could just be removed
+ {
+ f += 19;
+ p = Draw_CachePic ("gfx/netmen5.lmp");
+ M_DrawTransPic (72, f, p);
+ }
+
+ f = (320-26*8)/2;
+ M_DrawTextBox (f, 134, 24, 4);
+ f += 8;
+ M_PrintOld (f, 142, net_helpMessage[m_net_cursor*4+0]);
+ M_PrintOld (f, 150, net_helpMessage[m_net_cursor*4+1]);
+ M_PrintOld (f, 158, net_helpMessage[m_net_cursor*4+2]);
+ M_PrintOld (f, 166, net_helpMessage[m_net_cursor*4+3]);
+
+ f = (int)(host_time * 10)%6;
+ M_DrawTransPic (54, 32 + m_net_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
+}
+
+
+void M_Net_Key (int k)
+{
+again:
+ switch (k)
+ {
+ case K_ESCAPE:
+ M_Menu_MultiPlayer_f ();
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (++m_net_cursor >= m_net_items)
+ m_net_cursor = 0;
+ break;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (--m_net_cursor < 0)
+ m_net_cursor = m_net_items - 1;
+ break;
+
+ case K_ENTER:
+ m_entersound = true;
+
+ switch (m_net_cursor)
+ {
+ case 0:
+ M_Menu_SerialConfig_f ();
+ break;
+
+ case 1:
+ M_Menu_SerialConfig_f ();
+ break;
+
+ case 2:
+ M_Menu_LanConfig_f ();
+ break;
+
+ case 3:
+ M_Menu_LanConfig_f ();
+ break;
+
+ case 4:
+// multiprotocol
+ break;
+ }
+ }
+
+ if (m_net_cursor == 0 && !serialAvailable)
+ goto again;
+ if (m_net_cursor == 1 && !serialAvailable)
+ goto again;
+ if (m_net_cursor == 2 && !ipxAvailable)
+ goto again;
+ if (m_net_cursor == 3 && !tcpipAvailable)
+ goto again;
+}
+
+//=============================================================================
+/* OPTIONS MENU */
+#define SLIDER_RANGE 10
+
+
+enum //video menu
+{
+ OPT_SHOWFPS,
+ OPT_MAXFPS,
+ OPT_FOV,
+ OPT_GAMMA,
+ OPT_DECALS,
+ OPT_PARTICLES,
+ OPT_FULLBRIGHT,
+ OPT_DITHERING,
+ OPT_RETRO,
+ VID_ITEMS
+};
+
+enum //audio menu
+{
+ OPT_MUSICVOL,
+ OPT_SNDVOL,
+ OPT_MUSICTYPE,
+ AUDIO_ITEMS
+};
+
+enum //gameplay menu
+{
+ OPT_CROSSHAIR,
+ OPT_AIMASSIST,
+ OPT_IN_SPEED,
+ OPT_IN_ACCELERATION,
+ OPT_INVMOUSE,
+ OPT_IN_TOLERANCE,
+ OPT_MOUSELOOK,
+ GAMEPLAY_ITEMS
+};
+
+
+
+extern cvar_t show_fps;
+extern cvar_t show_bat;
+
+void M_AdjustSliders (int dir, int m_submenu, int options_cursor)
+{
+ S_LocalSound ("sounds/menu/navigate.wav");
+
+
+ if (m_submenu == 0)
+ {
+ switch (options_cursor)
+ {
+ case OPT_GAMMA: // gamma
+ v_gamma.value -= dir * 0.05;
+ if (v_gamma.value < 0.5)
+ v_gamma.value = 0.5;
+ if (v_gamma.value > 1)
+ v_gamma.value = 1;
+ Cvar_SetValue ("gamma", v_gamma.value);
+ break;
+ case OPT_MAXFPS:
+ cl_maxfps.value = cl_maxfps.value + dir * 5;
+ if (cl_maxfps.value < 30)
+ cl_maxfps.value = 30;
+ if (cl_maxfps.value > 65)
+ cl_maxfps.value = 65;
+ Cvar_SetValue ("cl_maxfps", cl_maxfps.value);
+ break;
+ case OPT_FOV:
+ scr_fov.value += dir * 5;
+ if (scr_fov.value < 50)
+ scr_fov.value = 50;
+ if (scr_fov.value > 120 && dir > 0)
+ scr_fov.value = 120;
+ Cvar_SetValue ("scr_fov", scr_fov.value);
+ break;
+ case OPT_DECALS:
+ Cvar_SetValue ("nzp_decals", !nzp_decals.value);
+ break;
+ case OPT_PARTICLES:
+ Cvar_SetValue ("nzp_particles", !nzp_decals.value);
+ break;
+ case OPT_FULLBRIGHT:
+ Cvar_SetValue ("r_fullbright", !nzp_decals.value);
+ break;
+ case OPT_DITHERING:
+ Cvar_SetValue ("r_dithering", !r_dithering.value);
+ break;
+ case OPT_RETRO:
+ Cvar_SetValue ("r_retro", !r_retro.value);
+ break;
+ case OPT_SHOWFPS:
+ Cvar_SetValue ("show_fps", !show_fps.value);
+ break;
+ }
+ }
+ else if (m_submenu == 1)
+ {
+ switch (options_cursor)
+ {
+ case OPT_MUSICVOL: // music volume
+ bgmvolume.value += dir * 0.1;
+ if (bgmvolume.value < 0)
+ bgmvolume.value = 0;
+ if (bgmvolume.value > 1)
+ bgmvolume.value = 1;
+ Cvar_SetValue ("bgmvolume", bgmvolume.value);
+ break;
+ case OPT_SNDVOL: // sfx volume
+ volume.value += dir * 0.1;
+ if (volume.value < 0)
+ volume.value = 0;
+ if (volume.value > 1)
+ volume.value = 1;
+ Cvar_SetValue ("volume", volume.value);
+ break;
+ case OPT_MUSICTYPE: // bgm type
+ if (strcmp(bgmtype.string,"cd") == 0)
+ {
+ Cvar_Set("bgmtype","none");
+ bmg_type_changed = true;
+ }
+ else
+ {
+ Cvar_Set("bgmtype","cd");
+ bmg_type_changed = true;
+ }
+ break;
+ }
+ }
+ else if (m_submenu == 2)
+ {
+ switch (options_cursor)
+ {
+ case OPT_CROSSHAIR:
+ Cvar_SetValue ("crosshair", !crosshair.value);
+ break;
+ case OPT_AIMASSIST:
+ Cvar_SetValue ("in_aimassist", !in_aimassist.value);
+ break;
+
+ case OPT_IN_SPEED: // mouse speed
+ in_sensitivity.value += dir * 0.5;
+ if (in_sensitivity.value < 1)
+ in_sensitivity.value = 1;
+ if (in_sensitivity.value > 11)
+ in_sensitivity.value = 11;
+ Cvar_SetValue ("sensitivity", in_sensitivity.value);
+ break;
+
+ case OPT_IN_ACCELERATION: // mouse tolerance
+ in_acceleration.value -= dir * 0.25;
+ if (in_acceleration.value < 0.5)
+ in_acceleration.value = 0.5;
+ if (in_acceleration.value > 2)
+ in_acceleration.value = 2;
+ Cvar_SetValue ("acceleration", in_acceleration.value);
+ break;
+
+ case OPT_IN_TOLERANCE: // mouse tolerance
+ in_tolerance.value += dir * 0.05;
+ if (in_tolerance.value < 0)
+ in_tolerance.value = 0;
+ if (in_tolerance.value > 1)
+ in_tolerance.value = 1;
+ Cvar_SetValue ("tolerance", in_tolerance.value);
+ break;
+ case OPT_INVMOUSE: // invert mouse
+ Cvar_SetValue ("m_pitch", -m_pitch.value);
+ break;
+ case OPT_MOUSELOOK:
+ Cvar_SetValue ("in_mlook", !in_mlook.value);
+ Cvar_SetValue ("in_analog_strafe", !in_analog_strafe.value);
+ break;
+ }
+ }
+}
+
+void M_DrawSlider (int x, int y, float range)
+{
+ int i;
+
+ if (range < 0)
+ range = 0;
+ if (range > 1)
+ range = 1;
+ Draw_Character (x-8, y, 128);
+ for (i=0 ; i= VID_ITEMS+1)
+ video_cursor = 0;
+ break;
+
+ case K_LEFTARROW:
+ M_AdjustSliders (-1, 0, video_cursor);
+ break;
+
+ case K_RIGHTARROW:
+ M_AdjustSliders (1, 0, video_cursor);
+ break;
+ }
+}
+
+
+
+//=============================================================================
+/* Audio Setting menu */
+
+int audio_cursor;
+void M_Menu_Audio_f (void)
+{
+ m_state = m_audio;
+ m_entersound = true;
+ audio_cursor = 0;
+}
+
+
+void M_Audio_Draw (void)
+{
+ float r;
+
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+ //else
+ //Draw_AlphaPic (0, 0, pause_bk, 0.4);
+
+
+ Draw_String (352, 30, "Sound Settings");
+
+ if (audio_cursor == OPT_MUSICVOL)
+ M_Print (172, 50+(OPT_MUSICVOL*8), " CD Music Volume");
+ else
+ Draw_String (172, 50+(OPT_MUSICVOL*8), " CD Music Volume");
+ r = bgmvolume.value;
+ M_DrawSlider (376, 50+(OPT_MUSICVOL*8), r);
+
+ if (audio_cursor == OPT_SNDVOL)
+ M_Print (172, 50+(OPT_SNDVOL*8), " Sound Volume");
+ else
+ Draw_String (172, 50+(OPT_SNDVOL*8), " Sound Volume");
+ r = volume.value;
+ M_DrawSlider (376, 50+(OPT_SNDVOL*8), r);
+
+ if (audio_cursor == OPT_MUSICTYPE)
+ M_Print (172, 50+(OPT_MUSICTYPE*8)," Music Type");
+ else
+ Draw_String (172, 50+(OPT_MUSICTYPE*8)," Music Type");
+ if (strcmp(bgmtype.string,"cd") == 0)
+ Draw_String (424, 50+(OPT_MUSICTYPE*8), "CD/MP3");
+ else
+ Draw_String (432, 50+(OPT_MUSICTYPE*8), "None");
+
+ if (audio_cursor == AUDIO_ITEMS)
+ M_Print (432, 58+(AUDIO_ITEMS*8), "Back");
+ else
+ Draw_String (432, 58+(AUDIO_ITEMS*8), "Back");
+}
+
+
+void M_Audio_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ if (key_dest == key_menu_pause)
+ {
+ M_Menu_Options_f ();
+ key_dest = key_menu_pause;
+ }
+ else
+ M_Menu_Options_f ();
+ break;
+
+ case K_ENTER:
+ m_entersound = true;
+ switch (audio_cursor)
+ {
+ case AUDIO_ITEMS:
+ if (key_dest == key_menu_pause)
+ {
+ M_Menu_Options_f ();
+ key_dest = key_menu_pause;
+ }
+ else
+ M_Menu_Options_f ();
+ break;
+
+ default:
+ M_AdjustSliders (1, 1, audio_cursor);
+ break;
+ }
+ return;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ audio_cursor--;
+ if (audio_cursor < 0)
+ audio_cursor = AUDIO_ITEMS;
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ audio_cursor++;
+ if (audio_cursor >= AUDIO_ITEMS+1)
+ audio_cursor = 0;
+ break;
+
+ case K_LEFTARROW:
+ M_AdjustSliders (-1, 1, audio_cursor);
+ break;
+
+ case K_RIGHTARROW:
+ M_AdjustSliders (1, 1, audio_cursor);
+ break;
+ }
+}
+
+//=============================================================================
+/* Gameplay Setting menu */
+
+int gameplay_cursor;
+void M_Menu_Gameplay_f (void)
+{
+ m_state = m_gameplay;
+ m_entersound = true;
+ gameplay_cursor = 0;
+}
+
+/*
+ float r;
+
+ // Background
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+
+ // Fill black to make everything easier to see
+ Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102));
+
+ // Version String
+ Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1);
+
+ // Header
+ Draw_ColoredString(10, 10, "GRAPHICS SETTINGS", 255, 255, 255, 255, 2);
+
+ // Show FPS
+ if (video_cursor == 0)
+ Draw_ColoredString(10, 45, "Show FPS", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 45, "Show FPS", 255, 255, 255, 255, 1);
+ M_DrawCheckbox(225, 45, show_fps.value);
+
+ // Max FPS
+ if (video_cursor == 1)
+ Draw_ColoredString(10, 55, "Max FPS", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 55, "Max FPS", 255, 255, 255, 255, 1);
+ r = (cl_maxfps.value - 30.0)*(1.0/35.0);
+ M_DrawSlider (225, 55, r);
+
+ // FOV
+ if (video_cursor == 2)
+ Draw_ColoredString(10, 65, "Field of View", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 65, "Field of View", 255, 255, 255, 255, 1);
+ r = (scr_fov.value - 50.0)*(1.0/70.0);
+ M_DrawSlider (225, 65, r);
+
+ // Brightness
+ if (video_cursor == 3)
+ Draw_ColoredString(10, 75, "Brightness", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 75, "Brightness", 255, 255, 255, 255, 1);
+ r = (1.0 - v_gamma.value) / 0.5;
+ M_DrawSlider (225, 75, r);
+
+ // Decals
+ if (video_cursor == 4)
+ Draw_ColoredString(10, 85, "Decals", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 85, "Decals", 255, 255, 255, 255, 1);
+ M_DrawCheckbox(225, 85, nzp_decals.value);
+
+ // Particles
+ if (video_cursor == 5)
+ Draw_ColoredString(10, 95, "Particles", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 95, "Particles", 255, 255, 255, 255, 1);
+ M_DrawCheckbox(225, 95, nzp_particles.value);
+
+ // Fullbright
+ if (video_cursor == 6)
+ Draw_ColoredString(10, 105, "Fullbright", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 105, "Fullbright", 255, 255, 255, 255, 1);
+ M_DrawCheckbox(225, 105, r_fullbright.value);
+
+ // Dithering
+ if (video_cursor == 7)
+ Draw_ColoredString(10, 115, "Dithering", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 115, "Dithering", 255, 255, 255, 255, 1);
+ M_DrawCheckbox(225, 115, r_dithering.value);
+
+ // Retro
+ if (video_cursor == 8)
+ Draw_ColoredString(10, 125, "Retro", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 125, "Retro", 255, 255, 255, 255, 1);
+ M_DrawCheckbox(225, 125, r_retro.value);
+
+ // Back
+ if (video_cursor == 9)
+ Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1);
+
+ // Descriptions
+ switch(video_cursor) {
+ case 0: // Show FPS
+ Draw_ColoredString(10, 230, "Toggle Framerate Overlay.", 255, 255, 255, 255, 1);
+ break;
+ case 1: // Max FPS
+ Draw_ColoredString(10, 230, "Increase of Decrease Max Frames per Second.", 255, 255, 255, 255, 1);
+ break;
+ case 2: // FOV
+ Draw_ColoredString(10, 230, "Adjust Game Field of View.", 255, 255, 255, 255, 1);
+ break;
+ case 3: // Brightness
+ Draw_ColoredString(10, 230, "Increase or Decrease Game Brightness.", 255, 255, 255, 255, 1);
+ break;
+ case 4: // Decals
+ Draw_ColoredString(10, 230, "Toggle Bullet and Explosive Decals.", 255, 255, 255, 255, 1);
+ break;
+ case 5: // Particles
+ Draw_ColoredString(10, 230, "Toggle Appearence of (most) Particles.", 255, 255, 255, 255, 1);
+ break;
+ case 6: // Fullbright
+ Draw_ColoredString(10, 230, "Toggle all non-realtime lights (Requires Map Restart).", 255, 255, 255, 255, 1);
+ break;
+ case 7: // Dithering
+ Draw_ColoredString(10, 230, "Toggle decrease in Color Banding.", 255, 255, 255, 255, 1);
+ break;
+ case 8: // Retro
+ Draw_ColoredString(10, 230, "Toggle texture filtering.", 255, 255, 255, 255, 1);
+ break;
+ }
+ */
+
+
+void M_Gameplay_Draw (void)
+{
+ float r;
+
+ // Background
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+
+ // Fill black to make everything easier to see
+ Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102));
+
+ // Version String
+ Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1);
+
+ // Header
+ Draw_ColoredString(10, 10, "CONTROL SETTINGS", 255, 255, 255, 255, 2);
+
+ // Draw Crosshair
+ if (gameplay_cursor == OPT_CROSSHAIR) {
+ Draw_ColoredString(10, 45, "Draw Crosshair", 255, 0, 0, 255, 1);
+ } else {
+ Draw_ColoredString(10, 45, "Draw Crosshair", 255, 255, 255, 255, 1);
+ }
+ M_DrawCheckbox(225, 45, crosshair.value);
+
+ // Aim Assist
+ if (gameplay_cursor == OPT_AIMASSIST) {
+ Draw_ColoredString(10, 55, "Aim Assist", 255, 0, 0, 255, 1);
+ } else {
+ Draw_ColoredString(10, 55, "Aim Assist", 255, 255, 255, 255, 1);
+ }
+ M_DrawCheckbox(225, 55, in_aimassist.value);
+
+ // Look Sensitivity
+ if (gameplay_cursor == OPT_IN_SPEED) {
+ Draw_ColoredString(10, 65, "Look Sensitivity", 255, 0, 0, 255, 1);
+ } else {
+ Draw_ColoredString(10, 65, "Look Sensitivity", 255, 255, 255, 255, 1);
+ }
+ r = (in_sensitivity.value - 1)/10;
+ M_DrawSlider (225, 65, r);
+
+ // Look Acceleration
+ if (gameplay_cursor == OPT_IN_ACCELERATION) {
+ Draw_ColoredString(10, 75, "Look Acceleration", 255, 0, 0, 255, 1);
+ } else {
+ Draw_ColoredString(10, 75, "Look Acceleration", 255, 255, 255, 255, 1);
+ }
+ r = 1.0f -((in_acceleration.value - 0.5f)/1.5f);
+ M_DrawSlider (225, 75, r);
+
+ // Look Inversion
+ if (gameplay_cursor == OPT_INVMOUSE) {
+ Draw_ColoredString(10, 85, "Look Inversion", 255, 0, 0, 255, 1);
+ } else {
+ Draw_ColoredString(10, 85, "Look Inversion", 255, 255, 255, 255, 1);
+ }
+ M_DrawCheckbox(225, 85, m_pitch.value < 0);
+
+ // A-Nub Deadzone
+ if (gameplay_cursor == OPT_IN_TOLERANCE) {
+ Draw_ColoredString(10, 95, "A-Nub Tolerance", 255, 0, 0, 255, 1);
+ } else {
+ Draw_ColoredString(10, 95, "A-Nub Tolerance", 255, 255, 255, 255, 1);
+ }
+ r = (in_tolerance.value )/1.0f;
+ M_DrawSlider (225, 95, r);
+
+ // A-Nub Mode
+ if (gameplay_cursor == OPT_MOUSELOOK) {
+ Draw_ColoredString(10, 105, "A-Nub Mode", 255, 0, 0, 255, 1);
+ } else {
+ Draw_ColoredString(10, 105, "A-Nub Mode", 255, 255, 255, 255, 1);
+ }
+ if (in_mlook.value) {
+ Draw_ColoredString(225, 105, "Look", 255, 255, 255, 255, 1);
+ } else {
+ Draw_ColoredString(225, 105, "Move (Buggy!)", 255, 255, 255, 255, 1);
+ }
+
+ // Back
+ if (gameplay_cursor == GAMEPLAY_ITEMS) {
+ Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1);
+ } else {
+ Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1);
+ }
+
+ // Descriptions
+ switch(gameplay_cursor) {
+ case OPT_CROSSHAIR:
+ Draw_ColoredString(10, 230, "Toggle Crosshair in-game.", 255, 255, 255, 255, 1);
+ break;
+ case OPT_AIMASSIST:
+ Draw_ColoredString(10, 230, "Toggle Assisted Aim to improve Targeting.", 255, 255, 255, 255, 1);
+ break;
+ case OPT_IN_SPEED:
+ Draw_ColoredString(10, 230, "Adjust Look Sensitivity.", 255, 255, 255, 255, 1);
+ break;
+ case OPT_IN_ACCELERATION:
+ Draw_ColoredString(10, 230, "Adjust Acceleration when A-Nub Mode is set to 'Look'.", 255, 255, 255, 255, 1);
+ break;
+ case OPT_INVMOUSE:
+ Draw_ColoredString(10, 230, "Invert A-Nub Look.", 255, 255, 255, 255, 1);
+ break;
+ case OPT_IN_TOLERANCE:
+ Draw_ColoredString(10, 230, "Adjust how Tolerant the A-Nub is to change.", 255, 255, 255, 255, 1);
+ break;
+ case OPT_MOUSELOOK:
+ Draw_ColoredString(10, 230, "Toggle whether to use the A-Nub for Look or Movement.", 255, 255, 255, 255, 1);
+ break;
+ }
+
+}
+
+
+void M_Gameplay_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ if (key_dest == key_menu_pause)
+ {
+ M_Menu_Options_f ();
+ key_dest = key_menu_pause;
+ }
+ else
+ M_Menu_Options_f ();
+ break;
+
+ case K_ENTER:
+ m_entersound = true;
+ switch (gameplay_cursor)
+ {
+ case GAMEPLAY_ITEMS:
+ if (key_dest == key_menu_pause)
+ {
+ M_Menu_Options_f ();
+ key_dest = key_menu_pause;
+ }
+ else
+ M_Menu_Options_f ();
+ break;
+ default:
+ M_AdjustSliders (1, 2, gameplay_cursor);
+ break;
+ }
+ return;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ gameplay_cursor--;
+ if (gameplay_cursor < 0)
+ gameplay_cursor = GAMEPLAY_ITEMS;
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ gameplay_cursor++;
+ if (gameplay_cursor >= GAMEPLAY_ITEMS+1)
+ gameplay_cursor = 0;
+ break;
+
+ case K_LEFTARROW:
+ M_AdjustSliders (-1, 2, gameplay_cursor);
+ break;
+
+ case K_RIGHTARROW:
+ M_AdjustSliders (1, 2, gameplay_cursor);
+ break;
+ }
+}
+
+int options_cursor;
+#define OPTIONS_ITEMS 5
+
+void M_Menu_Options_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_options;
+ m_entersound = true;
+}
+
+void M_Options_Draw (void)
+{
+ // Background
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+
+ // Fill black to make everything easier to see
+ Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102));
+
+ // Version String
+ Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1);
+
+ // Header
+ Draw_ColoredString(10, 10, "SETTINGS", 255, 255, 255, 255, 2);
+
+ // Graphics Settings
+ if (options_cursor == 0)
+ Draw_ColoredString(10, 45, "Graphics Settings", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 45, "Graphics Settings", 255, 255, 255, 255, 1);
+
+ // Controls
+ if (options_cursor == 1)
+ Draw_ColoredString(10, 55, "Controls", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 55, "Controls", 255, 255, 255, 255, 1);
+
+ // Control Settings
+ if (options_cursor == 2)
+ Draw_ColoredString(10, 65, "Control Settings", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 65, "Control Settings", 255, 255, 255, 255, 1);
+
+ // Divider
+ Draw_FillByColor(10, 78, 160, 2, GU_RGBA(130, 130, 130, 255));
+
+ // Console
+ if (options_cursor == 3)
+ Draw_ColoredString(10, 85, "Console", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 85, "Console", 255, 255, 255, 255, 1);
+
+ // Back
+ if (options_cursor == 4)
+ Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1);
+
+ // Descriptions
+ switch(options_cursor) {
+ case 0: // Graphics Settings
+ Draw_ColoredString(10, 230, "Adjust settings relating to Graphical Fidelity.", 255, 255, 255, 255, 1);
+ break;
+ case 1: // Controls
+ Draw_ColoredString(10, 230, "Customize your Control Scheme.", 255, 255, 255, 255, 1);
+ break;
+ case 2: // Control Settings
+ Draw_ColoredString(10, 230, "Adjust settings in relation to how NZ:P Controls.", 255, 255, 255, 255, 1);
+ break;
+ case 3: // Console
+ Draw_ColoredString(10, 230, "Open the Console to input Commands.", 255, 255, 255, 255, 1);
+ break;
+ }
+}
+
+
+extern qboolean console_enabled;
+void M_Options_Key (int k)
+{
+ switch (k)
+ {
+ case K_ESCAPE:
+ if (key_dest == key_menu_pause)
+ M_Paused_Menu_f();
+ else
+ M_Menu_Main_f ();
+ break;
+
+ case K_ENTER:
+ m_entersound = true;
+ switch (options_cursor)
+ {
+ case 0:
+ M_Menu_Screen_f();
+ break;
+ case 1:
+ M_Menu_Keys_f();
+ break;
+ case 2:
+ M_Menu_Gameplay_f ();
+ break;
+ case 3:
+ m_state = m_none;
+ console_enabled = true;
+ Con_ToggleConsole_f ();
+ break;
+ case 4:
+ if (key_dest == key_menu_pause)
+ M_Paused_Menu_f();
+ else
+ M_Menu_Main_f ();
+ break;
+ /*case 0:
+ M_Menu_Keys_f();
+ break;
+
+ case 1:
+ M_Menu_Screen_f ();
+ break;
+
+ case 2:
+ M_Menu_Audio_f ();
+ break;
+
+ case 3:
+ M_Menu_Gameplay_f ();
+ break;
+
+ case 4:
+ m_state = m_none;
+ console_enabled = true;
+ Con_ToggleConsole_f ();
+ break;
+
+ case 5:
+ if (key_dest == key_menu_pause)
+ M_Paused_Menu_f();
+ else
+ M_Menu_Main_f ();
+ break;*/
+ }
+ return;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ options_cursor--;
+ if (options_cursor < 0) {
+ options_cursor = OPTIONS_ITEMS-1;
+ }
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ options_cursor++;
+ if (options_cursor >= OPTIONS_ITEMS)
+ options_cursor = 0;
+ break;
+ }
+}
+
+//=============================================================================
+/* KEYS MENU */
+
+char *bindnames[][2] =
+{
+ {"+forward", "Walk Forward"},
+ {"+back", "Walk Backward"},
+ {"+moveleft", "Move Left"},
+ {"+moveright", "Move Right"},
+ {"+lookup", "Look Up"},
+ {"+lookdown", "Look Down"},
+ {"+left", "Look Left"},
+ {"+right", "Look Right"},
+ {"+jump", "Jump"},
+ {"+attack", "Fire"},
+ {"+aim", "Aim Down Sight"},
+ {"+switch", "Switch Weapon"},
+ {"+use", "Interact"},
+ {"+reload", "Reload"},
+ {"+knife", "Melee"},
+ {"+grenade", "Grenade"}
+};
+
+#define NUMCOMMANDS (sizeof(bindnames)/sizeof(bindnames[0]))
+
+int keys_cursor;
+int bind_grab;
+
+void M_Menu_Keys_f (void)
+{
+ m_state = m_keys;
+ m_entersound = true;
+}
+
+
+void M_FindKeysForCommand (char *command, int *twokeys)
+{
+ int count;
+ int j;
+ int l;
+ char *b;
+
+ twokeys[0] = twokeys[1] = -1;
+ l = strlen(command);
+ count = 0;
+
+ for (j=0 ; j<256 ; j++)
+ {
+ b = keybindings[j];
+ if (!b)
+ continue;
+ if (!strncmp (b, command, l) )
+ {
+ twokeys[count] = j;
+ count++;
+ if (count == 2)
+ break;
+ }
+ }
+}
+
+void M_UnbindCommand (char *command)
+{
+ int j;
+ int l;
+ char *b;
+
+ l = strlen(command);
+
+ for (j=0 ; j<256 ; j++)
+ {
+ b = keybindings[j];
+ if (!b)
+ continue;
+ if (!strncmp (b, command, l) )
+ Key_SetBinding (j, "");
+ }
+}
+
+
+void M_Keys_Draw (void)
+{
+ int i, j;
+ int y;
+ char *b;
+
+ // Background
+ if (key_dest != key_menu_pause)
+ Draw_Pic(0, 0, menu_bk);
+
+ // Fill black to make everything easier to see
+ Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102));
+
+ // Version String
+ Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1);
+
+ // Header
+ Draw_ColoredString(10, 10, "CONTROLS", 255, 255, 255, 255, 2);
+
+ if (bind_grab)
+ {
+ Draw_ColoredString(70, 230, "Press a button for this action, to cancel", 255, 255, 255, 255, 1);
+ Draw_Pic (268, 230, b_start);
+ }
+ else
+ {
+ Draw_ColoredString(48 + ((vid.width - 320)>>1), 230, "Press to change, to clear", 255, 255, 255, 255, 1);
+ Draw_Pic (96 + ((vid.width - 320)>>1), 230, b_cross);
+ Draw_Pic (200 + ((vid.width - 320)>>1), 230, b_square);
+ }
+
+ // search for known bindings
+ for (i = 0; i < NUMCOMMANDS; i++)
+ {
+ y = 45 + 10 * i;
+
+ if (i == keys_cursor)
+ Draw_ColoredString(10, y, bindnames[i][1], 255, 0, 0, 255, 1);
+ else
+ Draw_ColoredString(10, y, bindnames[i][1], 255, 255, 255, 255, 1);
+
+ for (j = 0; j < 256; j++)
+ {
+ b = keybindings[j];
+
+ if (!b) {
+ continue;
+ }
+
+ if (!strcmp (b, bindnames[i][0]))
+ {
+ Draw_Pic (140 + ((vid.width - 320)>>1), y, GetButtonIcon(bindnames[i][0]));
+ break;
+ }
+ }
+ }
+
+ if (keys_cursor == NUMCOMMANDS) {
+ Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1);
+ } else {
+ Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1);
+ M_DrawCharacter2 (130, 45 + keys_cursor*10, 12+((int)(realtime*4)&1));
+ }
+}
+
+
+void M_Keys_Key (int k)
+{
+ char cmd[80];
+ int keys[2];
+
+ if (bind_grab)
+ { // defining a key
+ S_LocalSound ("sounds/menu/navigate.wav");
+ if (k == K_ESCAPE)
+ {
+ bind_grab = false;
+ }
+ else if (k != '`')
+ {
+ sprintf (cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor][0]);
+ Cbuf_InsertText (cmd);
+ }
+
+ bind_grab = false;
+ return;
+ }
+
+ switch (k)
+ {
+ case K_ESCAPE:
+ if (key_dest == key_menu_pause)
+ {
+ M_Menu_Options_f ();
+ key_dest = key_menu_pause;
+ }
+ else
+ M_Menu_Options_f ();
+ break;
+
+ case K_LEFTARROW:
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ keys_cursor--;
+ if (keys_cursor < 0)
+ keys_cursor = NUMCOMMANDS;
+ break;
+
+ case K_DOWNARROW:
+ case K_RIGHTARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ keys_cursor++;
+ if (keys_cursor >= NUMCOMMANDS + 1)
+ keys_cursor = 0;
+ break;
+
+ case K_ENTER:
+ S_LocalSound ("sounds/menu/enter.wav");
+ if (keys_cursor != NUMCOMMANDS) { // go into bind mode
+ M_FindKeysForCommand (bindnames[keys_cursor][0], keys);
+ if (keys[1] != -1)
+ M_UnbindCommand (bindnames[keys_cursor][0]);
+ bind_grab = true;
+ } else { // return to menu
+ if (key_dest == key_menu_pause)
+ {
+ M_Menu_Options_f ();
+ key_dest = key_menu_pause;
+ }
+ else
+ M_Menu_Options_f ();
+ }
+ break;
+
+ case K_BACKSPACE: // delete bindings
+ case K_DEL: // delete bindings
+ S_LocalSound ("sounds/menu/enter.wav");
+ M_UnbindCommand (bindnames[keys_cursor][0]);
+ break;
+ }
+}
+
+//=============================================================================
+/* CREDITS MENU */
+
+
+void M_Menu_Credits_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_credits;
+ m_entersound = true;
+}
+
+
+
+void M_Credits_Draw (void)
+{
+ // Background
+ Draw_Pic(0, 0, menu_bk);
+
+ // Fill black to make everything easier to see
+ Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102));
+
+ // Version String
+ Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1);
+
+ // Header
+ Draw_ColoredString(10, 10, "CREDITS", 255, 255, 255, 255, 2);
+
+
+ Draw_ColoredString(10, 45, "Blubswillrule: Coding, Models, GFX, Sounds, Music", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 55, "Ju[s]tice: Maps, Models, GFX", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 65, "Jukki: Coding", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 75, "Biodude: Sounds", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 85, "Dr_Mabuse1981: Coding", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 95, "Naievil: Coding, NX Maintaining", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 105, "MotoLegacy: Coding, GFX, Music", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 115, "Derped_Crusader: Models, GFX", 255, 255, 255, 255, 1);
+
+ Draw_ColoredString(10, 135, "Special Thanks:", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 145, "- Spike: FTEQW", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 155, "- Shpuld: Clean-CSQC", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 165, "- Crow_Bar: DQuake", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 175, "- st1x51: DQuakePlus", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 185, "- fgsfdsfgs: QuakespasmNX", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 195, "- Azenn: GFX help", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 205, "- tavo: Music help", 255, 255, 255, 255, 1);
+ Draw_ColoredString(10, 215, "- BCDeshiG: Heavy bug testing", 255, 255, 255, 255, 1);
+
+ Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1);
+}
+
+
+void M_Credits_Key (int key)
+{
+ switch (key)
+ {
+ case K_ENTER:
+ case K_ESCAPE:
+ M_Menu_Main_f ();
+ break;
+ }
+}
+
+//=============================================================================
+/* QUIT MENU */
+
+int msgNumber;
+int m_quit_prevstate;
+
+int m_quit_cursor;
+int m_quit_items;
+
+
+char *quitMessage [] =
+{
+
+ " Are you gonna quit ",
+ " this game just like ", //msg:0
+ " everything else? ",
+ " ",
+
+ " Milord, methinks that ",
+ " thou art a lowly ", //msg:1
+ " quitter. Is this true? ",
+ " ",
+
+ " Do I need to bust your ",
+ " face open for trying ", //msg:2
+ " to quit? ",
+ " ",
+
+ " Man, I oughta smack you",
+ " for trying to quit! ", //msg:3
+ " Press X to get ",
+ " smacked out. ",
+
+ " Press X to quit like a ",
+ " big loser in life. ", //msg:4
+ " Press O to stay proud ",
+ " and successful! ",
+
+ " If you press X to ",
+ " quit, I will summon ", //msg:5
+ " Satan all over your ",
+ " hard drive! ",
+
+ " Um, Asmodeus dislikes ",
+ " his children trying to ", //msg:6
+ " quit. Press X to return",
+ " to your Tinkertoys. ",
+
+ " Really quit? ",
+ " ", //msg:7
+ " Press X to quit, ",
+ " or O to continue. ",
+
+ " If you quit now, I'll ",
+ " throw a blanket-party ", //msg:8
+ " for you next time! ",
+ " ",
+
+ " You are Crazy? ",
+ " Press X for Yes ", //msg:9
+ " or O for no! ",
+ " Global Random Ha Ha Ha "
+};
+
+
+void M_Menu_Quit_f (void)
+{
+ if (m_state == m_quit)
+ return;
+ wasInMenus = (key_dest == key_menu);
+ key_dest = key_menu;
+ m_quit_prevstate = m_state;
+ m_state = m_quit;
+ m_entersound = true;
+ msgNumber = rand()&7;
+/*
+ Con_Printf ("open source\n");
+
+ FILE *fd = fopen("credits.txt","rb");
+ FILE *fd1 = fopen("credits.dat","wb");
+
+ fseek (fd, 0, SEEK_END);
+ int len = ftell (fd);
+ fseek (fd, 0, SEEK_SET);
+
+ char *str = malloc(len);
+
+ fread (str, 1, len, fd);
+ Con_Printf ("source: %s\n",str);
+
+ char *out = strencrypt(str, 110901, len);
+ Con_Printf ("enc: %s\n", out);
+ fwrite(out, len, 1, fd1);
+
+ Con_Printf ("write encrypted\n");
+
+ //char *out2 = strdecrypt(out, 110901, sizeof(strf));
+ //Con_Printf ("dec: %s\n", out2);
+
+ free(out);
+ //free(out2);
+*/
+}
+
+
+void M_Quit_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ case 'n':
+ case 'N':
+ if (wasInMenus)
+ {
+ m_state = m_quit_prevstate;
+ m_entersound = true;
+ }
+ else
+ {
+ key_dest = key_game;
+ m_state = m_none;
+ }
+ break;
+
+ case 'Y':
+ case 'y':
+ case K_ENTER:
+ key_dest = key_console;
+ Host_Quit_f ();
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+
+void M_Quit_Draw (void)
+{
+ if (wasInMenus)
+ {
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+ //else
+ //Draw_AlphaPic (0, 0, pause_bk, 0.4);
+
+ m_state = m_quit_prevstate;
+ m_recursiveDraw = true;
+ M_Draw ();
+ m_state = m_quit;
+ }
+
+ M_DrawTextBox (56, 76, 24, 4);
+ M_PrintOld (64, 84, quitMessage[msgNumber*4+0]);
+ M_PrintOld (64, 92, quitMessage[msgNumber*4+1]);
+ M_PrintOld (64, 100, quitMessage[msgNumber*4+2]);
+ M_PrintOld (64, 108, quitMessage[msgNumber*4+3]);
+}
+//=============================================================================
+/* OSK IMPLEMENTATION */
+#define CHAR_SIZE 8
+#define MAX_Y 8
+#define MAX_X 12
+
+#define MAX_CHAR_LINE 36
+#define MAX_CHAR 72
+
+int osk_pos_x = 0;
+int osk_pos_y = 0;
+int max_len = 0;
+int m_old_state = 0;
+
+char* osk_out_buff = NULL;
+char osk_buffer[128];
+
+char *osk_text [] =
+ {
+ " 1 2 3 4 5 6 7 8 9 0 - = ` ",
+ " q w e r t y u i o p [ ] ",
+ " a s d f g h j k l ; ' \\ ",
+ " z x c v b n m , . / ",
+ " ",
+ " ! @ # $ % ^ & * ( ) _ + ~ ",
+ " Q W E R T Y U I O P { } ",
+ " A S D F G H J K L : \" | ",
+ " Z X C V B N M < > ? "
+ };
+
+char *osk_help [] =
+ {
+ "CONFIRM: ",
+ " SQUARE ",
+ "CANCEL: ",
+ " CIRCLE ",
+ "DELETE: ",
+ " TRIAGLE ",
+ "ADD CHAR:",
+ " CROSS ",
+ ""
+ };
+
+void M_Menu_OSK_f (char *input, char *output, int outlen)
+{
+ key_dest = key_menu;
+ m_old_state = m_state;
+ m_state = m_osk;
+ m_entersound = false;
+ max_len = outlen;
+ strncpy(osk_buffer,input,max_len);
+ osk_buffer[outlen] = '\0';
+ osk_out_buff = output;
+}
+
+void Con_OSK_f (char *input, char *output, int outlen)
+{
+ max_len = outlen;
+ strncpy(osk_buffer,input,max_len);
+ osk_buffer[outlen] = '\0';
+ osk_out_buff = output;
+}
+
+
+void M_OSK_Draw (void)
+{
+ int x,y;
+ int i;
+
+ char *selected_line = osk_text[osk_pos_y];
+ char selected_char[2];
+
+ selected_char[0] = selected_line[1+(2*osk_pos_x)];
+ selected_char[1] = '\0';
+ if (selected_char[0] == ' ' || selected_char[0] == '\t')
+ selected_char[0] = 'X';
+
+ y = 20;
+ x = 16;
+
+ M_DrawTextBox (10, 10, 26, 10);
+ M_DrawTextBox (10+(26*CHAR_SIZE), 10, 10, 10);
+ M_DrawTextBox (10, 10+(10*CHAR_SIZE),36, 3);
+
+ for(i=0;i<=MAX_Y;i++)
+ {
+ M_PrintWhiteOld (x, y+(CHAR_SIZE*i), osk_text[i]);
+ if (i % 2 == 0)
+ M_PrintOld (x+(27*CHAR_SIZE), y+(CHAR_SIZE*i), osk_help[i]);
+ else
+ M_PrintWhiteOld (x+(27*CHAR_SIZE), y+(CHAR_SIZE*i), osk_help[i]);
+ }
+
+ int text_len = strlen(osk_buffer);
+ if (text_len > MAX_CHAR_LINE) {
+
+ char oneline[MAX_CHAR_LINE+1];
+ strncpy(oneline,osk_buffer,MAX_CHAR_LINE);
+ oneline[MAX_CHAR_LINE] = '\0';
+
+ M_PrintOld (x+4, y+4+(CHAR_SIZE*(MAX_Y+2)), oneline );
+
+ strncpy(oneline,osk_buffer+MAX_CHAR_LINE, text_len - MAX_CHAR_LINE);
+ oneline[text_len - MAX_CHAR_LINE] = '\0';
+
+ M_PrintOld (x+4, y+4+(CHAR_SIZE*(MAX_Y+3)), oneline );
+ M_PrintWhiteOld (x+4+(CHAR_SIZE*(text_len - MAX_CHAR_LINE)), y+4+(CHAR_SIZE*(MAX_Y+3)),"_");
+ }
+ else {
+ M_PrintOld (x+4, y+4+(CHAR_SIZE*(MAX_Y+2)), osk_buffer );
+ M_PrintWhiteOld (x+4+(CHAR_SIZE*(text_len)), y+4+(CHAR_SIZE*(MAX_Y+2)),"_");
+ }
+ M_PrintOld (x+((((osk_pos_x)*2)+1)*CHAR_SIZE), y+(osk_pos_y*CHAR_SIZE), selected_char);
+
+}
+
+void M_OSK_Key (int key)//blubswillrule: making console cursor wrap around
+{
+ switch (key)
+ {
+ case K_RIGHTARROW:
+ osk_pos_x++;
+ if (osk_pos_x > MAX_X)
+ osk_pos_x = 0;//MAX_X
+ break;
+ case K_LEFTARROW:
+ osk_pos_x--;
+ if (osk_pos_x < 0)
+ osk_pos_x = MAX_X;//0
+ break;
+ case K_DOWNARROW:
+ osk_pos_y++;
+ if (osk_pos_y > MAX_Y)
+ osk_pos_y = 0;//MAX_Y
+ break;
+ case K_UPARROW:
+ osk_pos_y--;
+ if (osk_pos_y < 0)
+ osk_pos_y = MAX_Y;//0
+ break;
+ case K_ENTER:
+ if (max_len > strlen(osk_buffer)) {
+ char *selected_line = osk_text[osk_pos_y];
+ char selected_char[2];
+
+ selected_char[0] = selected_line[1+(2*osk_pos_x)];
+
+ if (selected_char[0] == '\t')
+ selected_char[0] = ' ';
+
+ selected_char[1] = '\0';
+ strcat(osk_buffer,selected_char);
+ }
+ break;
+ case K_DEL:
+ if (strlen(osk_buffer) > 0) {
+ osk_buffer[strlen(osk_buffer)-1] = '\0';
+ }
+ break;
+ case K_INS:
+ strncpy(osk_out_buff,osk_buffer,max_len);
+
+ m_state = m_old_state;
+ break;
+ case K_ESCAPE:
+ m_state = m_old_state;
+ break;
+ default:
+ break;
+ }
+}
+
+void Con_OSK_Key (int key)////blubswillrule: making console cursor wrap around
+{
+ switch (key)
+ {
+ case K_RIGHTARROW:
+ osk_pos_x++;
+ if (osk_pos_x > MAX_X)
+ osk_pos_x = 0;//MAX_X
+ break;
+ case K_LEFTARROW:
+ osk_pos_x--;
+ if (osk_pos_x < 0)
+ osk_pos_x = MAX_X;//0
+ break;
+ case K_DOWNARROW:
+ osk_pos_y++;
+ if (osk_pos_y > MAX_Y)
+ osk_pos_y = 0;//MAX_Y
+ break;
+ case K_UPARROW:
+ osk_pos_y--;
+ if (osk_pos_y < 0)
+ osk_pos_y = MAX_Y;//0
+ break;
+ case K_ENTER:
+ if (max_len > strlen(osk_buffer)) {
+ char *selected_line = osk_text[osk_pos_y];
+ char selected_char[2];
+
+ selected_char[0] = selected_line[1+(2*osk_pos_x)];
+
+ if (selected_char[0] == '\t')
+ selected_char[0] = ' ';
+
+ selected_char[1] = '\0';
+ strcat(osk_buffer,selected_char);
+ }
+ break;
+ case K_DEL:
+ if (strlen(osk_buffer) > 0) {
+ osk_buffer[strlen(osk_buffer)-1] = '\0';
+ }
+ break;
+ case K_INS:
+ strncpy(osk_out_buff,osk_buffer,max_len);
+ Con_SetOSKActive(false);
+ break;
+ case K_ESCAPE:
+ Con_SetOSKActive(false);
+ break;
+ default:
+ break;
+ }
+}
+
+//=============================================================================
+
+/* SERIAL CONFIG MENU */
+
+int serialConfig_cursor;
+int serialConfig_cursor_table[] = {48, 64, 80, 96, 112, 132};
+#define NUM_SERIALCONFIG_CMDS 6
+
+static int ISA_uarts[] = {0x3f8,0x2f8,0x3e8,0x2e8};
+static int ISA_IRQs[] = {4,3,4,3};
+int serialConfig_baudrate[] = {9600,14400,19200,28800,38400,57600};
+
+int serialConfig_comport;
+int serialConfig_irq ;
+int serialConfig_baud;
+char serialConfig_phone[16];
+
+void M_Menu_SerialConfig_f (void)
+{
+ int n;
+ int port;
+ int baudrate;
+ qboolean useModem;
+
+ key_dest = key_menu;
+ m_state = m_serialconfig;
+ m_entersound = true;
+ if (JoiningGame && SerialConfig)
+ serialConfig_cursor = 4;
+ else
+ serialConfig_cursor = 5;
+
+ (*GetComPortConfig) (0, &port, &serialConfig_irq, &baudrate, &useModem);
+
+ // map uart's port to COMx
+ for (n = 0; n < 4; n++)
+ if (ISA_uarts[n] == port)
+ break;
+ if (n == 4)
+ {
+ n = 0;
+ serialConfig_irq = 4;
+ }
+ serialConfig_comport = n + 1;
+
+ // map baudrate to index
+ for (n = 0; n < 6; n++)
+ if (serialConfig_baudrate[n] == baudrate)
+ break;
+ if (n == 6)
+ n = 5;
+ serialConfig_baud = n;
+
+ m_return_onerror = false;
+ m_return_reason[0] = 0;
+}
+
+
+void M_SerialConfig_Draw (void)
+{
+ int basex;
+ char *startJoin;
+ char *directModem;
+
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+ //else
+ //Draw_AlphaPic (0, 0, pause_bk, 0.4);
+
+ basex = (320)/2;
+
+ if (StartingGame)
+ startJoin = "New Game";
+ else
+ startJoin = "Join Game";
+ if (SerialConfig)
+ directModem = "Modem";
+ else
+ directModem = "Direct Connect";
+ M_PrintOld (basex, 32, va ("%s - %s", startJoin, directModem));
+ basex += 8;
+
+ M_PrintOld (basex, serialConfig_cursor_table[0], "Port");
+ M_DrawTextBox (160, 40, 4, 1);
+ M_PrintOld (168, serialConfig_cursor_table[0], va("COM%u", serialConfig_comport));
+
+ M_PrintOld (basex, serialConfig_cursor_table[1], "IRQ");
+ M_DrawTextBox (160, serialConfig_cursor_table[1]-8, 1, 1);
+ M_PrintOld (168, serialConfig_cursor_table[1], va("%u", serialConfig_irq));
+
+ M_PrintOld (basex, serialConfig_cursor_table[2], "Baud");
+ M_DrawTextBox (160, serialConfig_cursor_table[2]-8, 5, 1);
+ M_PrintOld (168, serialConfig_cursor_table[2], va("%u", serialConfig_baudrate[serialConfig_baud]));
+
+ if (SerialConfig)
+ {
+ M_PrintOld (basex, serialConfig_cursor_table[3], "Modem Setup...");
+ if (JoiningGame)
+ {
+ M_PrintOld (basex, serialConfig_cursor_table[4], "Phone number");
+ M_DrawTextBox (160, serialConfig_cursor_table[4]-8, 16, 1);
+ M_PrintOld (168, serialConfig_cursor_table[4], serialConfig_phone);
+ }
+ }
+
+ if (JoiningGame)
+ {
+ M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 7, 1);
+ M_PrintOld (basex+8, serialConfig_cursor_table[5], "Connect");
+ }
+ else
+ {
+ M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 2, 1);
+ M_PrintOld (basex+8, serialConfig_cursor_table[5], "OK");
+ }
+
+ M_DrawCharacter2 (basex-8, serialConfig_cursor_table [serialConfig_cursor], 12+((int)(realtime*4)&1));
+
+ if (serialConfig_cursor == 4)
+ M_DrawCharacter2 (168 + 8*strlen(serialConfig_phone), serialConfig_cursor_table [serialConfig_cursor], 10+((int)(realtime*4)&1));
+
+ if (*m_return_reason)
+ M_PrintWhiteOld (basex, 148, m_return_reason);
+}
+
+
+void M_SerialConfig_Key (int key)
+{
+ int l;
+
+ switch (key)
+ {
+ case K_ESCAPE:
+ M_Menu_Net_f ();
+ break;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ serialConfig_cursor--;
+ if (serialConfig_cursor < 0)
+ serialConfig_cursor = NUM_SERIALCONFIG_CMDS-1;
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ serialConfig_cursor++;
+ if (serialConfig_cursor >= NUM_SERIALCONFIG_CMDS)
+ serialConfig_cursor = 0;
+ break;
+
+ case K_LEFTARROW:
+ if (serialConfig_cursor > 2)
+ break;
+ S_LocalSound ("sounds/menu/navigate.wav");
+
+ if (serialConfig_cursor == 0)
+ {
+ serialConfig_comport--;
+ if (serialConfig_comport == 0)
+ serialConfig_comport = 4;
+ serialConfig_irq = ISA_IRQs[serialConfig_comport-1];
+ }
+
+ if (serialConfig_cursor == 1)
+ {
+ serialConfig_irq--;
+ if (serialConfig_irq == 6)
+ serialConfig_irq = 5;
+ if (serialConfig_irq == 1)
+ serialConfig_irq = 7;
+ }
+
+ if (serialConfig_cursor == 2)
+ {
+ serialConfig_baud--;
+ if (serialConfig_baud < 0)
+ serialConfig_baud = 5;
+ }
+
+ break;
+
+ case K_RIGHTARROW:
+ if (serialConfig_cursor > 2)
+ break;
+forward:
+ S_LocalSound ("sounds/menu/navigate.wav");
+
+ if (serialConfig_cursor == 0)
+ {
+ serialConfig_comport++;
+ if (serialConfig_comport > 4)
+ serialConfig_comport = 1;
+ serialConfig_irq = ISA_IRQs[serialConfig_comport-1];
+ }
+
+ if (serialConfig_cursor == 1)
+ {
+ serialConfig_irq++;
+ if (serialConfig_irq == 6)
+ serialConfig_irq = 7;
+ if (serialConfig_irq == 8)
+ serialConfig_irq = 2;
+ }
+
+ if (serialConfig_cursor == 2)
+ {
+ serialConfig_baud++;
+ if (serialConfig_baud > 5)
+ serialConfig_baud = 0;
+ }
+
+ break;
+
+ case K_ENTER:
+ if (serialConfig_cursor < 3)
+ goto forward;
+
+ m_entersound = true;
+
+ if (serialConfig_cursor == 3)
+ {
+ (*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig);
+
+ M_Menu_ModemConfig_f ();
+ break;
+ }
+
+ if (serialConfig_cursor == 4)
+ {
+ serialConfig_cursor = 5;
+ break;
+ }
+
+ // serialConfig_cursor == 5 (OK/CONNECT)
+ (*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig);
+
+ M_ConfigureNetSubsystem ();
+
+ if (StartingGame)
+ {
+ M_Menu_GameOptions_f ();
+ break;
+ }
+
+ m_return_state = m_state;
+ m_return_onerror = true;
+ key_dest = key_game;
+ m_state = m_none;
+
+ if (SerialConfig)
+ Cbuf_AddText (va ("connect \"%s\"\n", serialConfig_phone));
+ else
+ Cbuf_AddText ("connect\n");
+ break;
+
+ case K_BACKSPACE:
+ if (serialConfig_cursor == 4)
+ {
+ if (strlen(serialConfig_phone))
+ serialConfig_phone[strlen(serialConfig_phone)-1] = 0;
+ }
+ break;
+
+ default:
+ if (key < 32 || key > 127)
+ break;
+ if (serialConfig_cursor == 4)
+ {
+ l = strlen(serialConfig_phone);
+ if (l < 15)
+ {
+ serialConfig_phone[l+1] = 0;
+ serialConfig_phone[l] = key;
+ }
+ }
+ }
+
+ if (DirectConfig && (serialConfig_cursor == 3 || serialConfig_cursor == 4))
+ {
+ if (key == K_UPARROW)
+ serialConfig_cursor = 2;
+ else
+ serialConfig_cursor = 5;
+ }
+ if (SerialConfig && StartingGame && serialConfig_cursor == 4)
+ {
+ if (key == K_UPARROW)
+ serialConfig_cursor = 3;
+ else
+ serialConfig_cursor = 5;
+ }
+}
+
+//=============================================================================
+/* MODEM CONFIG MENU */
+
+int modemConfig_cursor;
+int modemConfig_cursor_table [] = {40, 56, 88, 120, 156};
+#define NUM_MODEMCONFIG_CMDS 5
+
+char modemConfig_dialing;
+char modemConfig_clear [16];
+char modemConfig_init [32];
+char modemConfig_hangup [16];
+
+void M_Menu_ModemConfig_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_modemconfig;
+ m_entersound = true;
+ (*GetModemConfig) (0, &modemConfig_dialing, modemConfig_clear, modemConfig_init, modemConfig_hangup);
+}
+
+
+void M_ModemConfig_Draw (void)
+{
+ int basex;
+
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+ //else
+ //Draw_AlphaPic (0, 0, pause_bk, 0.4);
+
+ basex = (320)/2;
+ basex += 8;
+
+ if (modemConfig_dialing == 'P')
+ M_PrintOld (basex, modemConfig_cursor_table[0], "Pulse Dialing");
+ else
+ M_PrintOld (basex, modemConfig_cursor_table[0], "Touch Tone Dialing");
+
+ M_PrintOld (basex, modemConfig_cursor_table[1], "Clear");
+ M_DrawTextBox (basex, modemConfig_cursor_table[1]+4, 16, 1);
+ M_PrintOld (basex+8, modemConfig_cursor_table[1]+12, modemConfig_clear);
+ if (modemConfig_cursor == 1)
+ M_DrawCharacter2 (basex+8 + 8*strlen(modemConfig_clear), modemConfig_cursor_table[1]+12, 10+((int)(realtime*4)&1));
+
+ M_PrintOld (basex, modemConfig_cursor_table[2], "Init");
+ M_DrawTextBox (basex, modemConfig_cursor_table[2]+4, 30, 1);
+ M_PrintOld (basex+8, modemConfig_cursor_table[2]+12, modemConfig_init);
+ if (modemConfig_cursor == 2)
+ M_DrawCharacter2 (basex+8 + 8*strlen(modemConfig_init), modemConfig_cursor_table[2]+12, 10+((int)(realtime*4)&1));
+
+ M_PrintOld (basex, modemConfig_cursor_table[3], "Hangup");
+ M_DrawTextBox (basex, modemConfig_cursor_table[3]+4, 16, 1);
+ M_PrintOld (basex+8, modemConfig_cursor_table[3]+12, modemConfig_hangup);
+ if (modemConfig_cursor == 3)
+ M_DrawCharacter2 (basex+8 + 8*strlen(modemConfig_hangup), modemConfig_cursor_table[3]+12, 10+((int)(realtime*4)&1));
+
+ M_DrawTextBox (basex, modemConfig_cursor_table[4]-8, 2, 1);
+ M_PrintOld (basex+8, modemConfig_cursor_table[4], "OK");
+
+ M_DrawCharacter2 (basex-8, modemConfig_cursor_table [modemConfig_cursor], 12+((int)(realtime*4)&1));
+}
+
+
+void M_ModemConfig_Key (int key)
+{
+ int l;
+
+ switch (key)
+ {
+ case K_ESCAPE:
+ M_Menu_SerialConfig_f ();
+ break;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ modemConfig_cursor--;
+ if (modemConfig_cursor < 0)
+ modemConfig_cursor = NUM_MODEMCONFIG_CMDS-1;
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ modemConfig_cursor++;
+ if (modemConfig_cursor >= NUM_MODEMCONFIG_CMDS)
+ modemConfig_cursor = 0;
+ break;
+
+ case K_LEFTARROW:
+ case K_RIGHTARROW:
+ if (modemConfig_cursor == 0)
+ {
+ if (modemConfig_dialing == 'P')
+ modemConfig_dialing = 'T';
+ else
+ modemConfig_dialing = 'P';
+ S_LocalSound ("sounds/menu/navigate.wav");
+ }
+ break;
+
+ case K_ENTER:
+ if (modemConfig_cursor == 0)
+ {
+ if (modemConfig_dialing == 'P')
+ modemConfig_dialing = 'T';
+ else
+ modemConfig_dialing = 'P';
+ m_entersound = true;
+ }
+
+ if (modemConfig_cursor == 4)
+ {
+ (*SetModemConfig) (0, va ("%c", modemConfig_dialing), modemConfig_clear, modemConfig_init, modemConfig_hangup);
+ m_entersound = true;
+ M_Menu_SerialConfig_f ();
+ }
+ break;
+
+ case K_BACKSPACE:
+ if (modemConfig_cursor == 1)
+ {
+ if (strlen(modemConfig_clear))
+ modemConfig_clear[strlen(modemConfig_clear)-1] = 0;
+ }
+
+ if (modemConfig_cursor == 2)
+ {
+ if (strlen(modemConfig_init))
+ modemConfig_init[strlen(modemConfig_init)-1] = 0;
+ }
+
+ if (modemConfig_cursor == 3)
+ {
+ if (strlen(modemConfig_hangup))
+ modemConfig_hangup[strlen(modemConfig_hangup)-1] = 0;
+ }
+ break;
+
+ default:
+ if (key < 32 || key > 127)
+ break;
+
+ if (modemConfig_cursor == 1)
+ {
+ l = strlen(modemConfig_clear);
+ if (l < 15)
+ {
+ modemConfig_clear[l+1] = 0;
+ modemConfig_clear[l] = key;
+ }
+ }
+
+ if (modemConfig_cursor == 2)
+ {
+ l = strlen(modemConfig_init);
+ if (l < 29)
+ {
+ modemConfig_init[l+1] = 0;
+ modemConfig_init[l] = key;
+ }
+ }
+
+ if (modemConfig_cursor == 3)
+ {
+ l = strlen(modemConfig_hangup);
+ if (l < 15)
+ {
+ modemConfig_hangup[l+1] = 0;
+ modemConfig_hangup[l] = key;
+ }
+ }
+ }
+}
+
+//=============================================================================
+/* LAN CONFIG MENU */
+
+int lanConfig_cursor = -1;
+int lanConfig_cursor_table [] = {72, 92, 124};
+#define NUM_LANCONFIG_CMDS 3
+
+int lanConfig_port;
+char lanConfig_portname[6];
+char lanConfig_joinname[22];
+
+void M_Menu_LanConfig_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_lanconfig;
+ m_entersound = true;
+ if (lanConfig_cursor == -1)
+ {
+ if (JoiningGame && TCPIPConfig)
+ lanConfig_cursor = 2;
+ else
+ lanConfig_cursor = 1;
+ }
+ if (StartingGame && lanConfig_cursor == 2)
+ lanConfig_cursor = 1;
+ lanConfig_port = DEFAULTnet_hostport;
+ sprintf(lanConfig_portname, "%u", lanConfig_port);
+
+ m_return_onerror = false;
+ m_return_reason[0] = 0;
+}
+
+
+void M_LanConfig_Draw (void)
+{
+ int basex;
+ char *startJoin;
+ char *protocol;
+
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+ //else
+ //Draw_AlphaPic (0, 0, pause_bk, 0.4);
+
+ basex = (320)/2;
+
+ if (StartingGame)
+ startJoin = "New Game";
+ else
+ startJoin = "Join Game";
+ if (IPXConfig)
+ protocol = "IPX";
+ else
+ protocol = "TCP/IP";
+ M_PrintOld (basex, 32, va ("%s - %s", startJoin, protocol));
+ basex += 8;
+
+ M_PrintOld (basex, 52, "Address:");
+ if (IPXConfig)
+ M_PrintOld (basex+9*8, 52, my_ipx_address);
+ else
+ M_PrintOld (basex+9*8, 52, my_tcpip_address);
+
+ M_PrintOld (basex, lanConfig_cursor_table[0], "Port");
+ M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1);
+ M_PrintOld (basex+9*8, lanConfig_cursor_table[0], lanConfig_portname);
+
+ if (JoiningGame)
+ {
+ M_PrintOld (basex, lanConfig_cursor_table[1], "Search for local games...");
+ M_PrintOld (basex, 108, "Join game at:");
+ M_DrawTextBox (basex+8, lanConfig_cursor_table[2]-8, 22, 1);
+ M_PrintOld (basex+16, lanConfig_cursor_table[2], lanConfig_joinname);
+ }
+ else
+ {
+ M_DrawTextBox (basex, lanConfig_cursor_table[1]-8, 2, 1);
+ M_PrintOld (basex+8, lanConfig_cursor_table[1], "OK");
+ }
+
+ M_DrawCharacter2 (basex-8, lanConfig_cursor_table [lanConfig_cursor], 12+((int)(realtime*4)&1));
+
+ if (lanConfig_cursor == 0)
+ M_DrawCharacter2 (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1));
+
+ if (lanConfig_cursor == 2)
+ M_DrawCharacter2 (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [2], 10+((int)(realtime*4)&1));
+
+ if (*m_return_reason)
+ M_PrintWhiteOld (basex, 148, m_return_reason);
+}
+
+
+void M_LanConfig_Key (int key)
+{
+ int l;
+
+ switch (key)
+ {
+ case K_ESCAPE:
+ M_Menu_Net_f ();
+ break;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ lanConfig_cursor--;
+ if (lanConfig_cursor < 0)
+ lanConfig_cursor = NUM_LANCONFIG_CMDS-1;
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ lanConfig_cursor++;
+ if (lanConfig_cursor >= NUM_LANCONFIG_CMDS)
+ lanConfig_cursor = 0;
+ break;
+
+ case K_INS:
+ if (lanConfig_cursor == 0)
+ {
+ M_Menu_OSK_f(lanConfig_portname, lanConfig_portname, 6);
+ break;
+ }
+
+ if (lanConfig_cursor == 2)
+ {
+ M_Menu_OSK_f(lanConfig_joinname, lanConfig_joinname, 22);
+ break;
+ }
+ break;
+
+ case K_ENTER:
+ if (lanConfig_cursor == 0)
+ break;
+
+ m_entersound = true;
+
+ M_ConfigureNetSubsystem ();
+
+ if (lanConfig_cursor == 1)
+ {
+ if (StartingGame)
+ {
+ M_Menu_GameOptions_f ();
+ break;
+ }
+ M_Menu_Search_f();
+ break;
+ }
+
+ if (lanConfig_cursor == 2)
+ {
+ m_return_state = m_state;
+ m_return_onerror = true;
+ key_dest = key_game;
+ m_state = m_none;
+ Cbuf_AddText ( va ("connect \"%s\"\n", lanConfig_joinname) );
+ break;
+ }
+
+ break;
+
+ case K_BACKSPACE:
+ if (lanConfig_cursor == 0)
+ {
+ if (strlen(lanConfig_portname))
+ lanConfig_portname[strlen(lanConfig_portname)-1] = 0;
+ }
+
+ if (lanConfig_cursor == 2)
+ {
+ if (strlen(lanConfig_joinname))
+ lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0;
+ }
+ break;
+
+ default:
+ if (key < 32 || key > 127)
+ break;
+
+ if (lanConfig_cursor == 2)
+ {
+ l = strlen(lanConfig_joinname);
+ if (l < 21)
+ {
+ lanConfig_joinname[l+1] = 0;
+ lanConfig_joinname[l] = key;
+ }
+ }
+
+ if (key < '0' || key > '9')
+ break;
+ if (lanConfig_cursor == 0)
+ {
+ l = strlen(lanConfig_portname);
+ if (l < 5)
+ {
+ lanConfig_portname[l+1] = 0;
+ lanConfig_portname[l] = key;
+ }
+ }
+ }
+
+ if (StartingGame && lanConfig_cursor == 2)
+ {
+ if (key == K_UPARROW)
+ lanConfig_cursor = 1;
+ else
+ lanConfig_cursor = 0;
+ }
+
+ l = Q_atoi(lanConfig_portname);
+ if (l > 65535)
+ l = lanConfig_port;
+ else
+ lanConfig_port = l;
+ sprintf(lanConfig_portname, "%u", lanConfig_port);
+}
+
+//=============================================================================
+/* GAME OPTIONS MENU */
+
+typedef struct
+{
+ char *name;
+ char *description;
+} level_t;
+
+level_t levels[] =
+{
+ {"start", "Entrance"}, // 0
+
+ {"e1m1", "Slipgate Complex"}, // 1
+ {"e1m2", "Castle of the Damned"},
+ {"e1m3", "The Necropolis"},
+ {"e1m4", "The Grisly Grotto"},
+ {"e1m5", "Gloom Keep"},
+ {"e1m6", "The Door To Chthon"},
+ {"e1m7", "The House of Chthon"},
+ {"e1m8", "Ziggurat Vertigo"},
+
+ {"e2m1", "The Installation"}, // 9
+ {"e2m2", "Ogre Citadel"},
+ {"e2m3", "Crypt of Decay"},
+ {"e2m4", "The Ebon Fortress"},
+ {"e2m5", "The Wizard's Manse"},
+ {"e2m6", "The Dismal Oubliette"},
+ {"e2m7", "Underearth"},
+
+ {"e3m1", "Termination Central"}, // 16
+ {"e3m2", "The Vaults of Zin"},
+ {"e3m3", "The Tomb of Terror"},
+ {"e3m4", "Satan's Dark Delight"},
+ {"e3m5", "Wind Tunnels"},
+ {"e3m6", "Chambers of Torment"},
+ {"e3m7", "The Haunted Halls"},
+
+ {"e4m1", "The Sewage System"}, // 23
+ {"e4m2", "The Tower of Despair"},
+ {"e4m3", "The Elder God Shrine"},
+ {"e4m4", "The Palace of Hate"},
+ {"e4m5", "Hell's Atrium"},
+ {"e4m6", "The Pain Maze"},
+ {"e4m7", "Azure Agony"},
+ {"e4m8", "The Nameless City"},
+
+ {"end", "Shub-Niggurath's Pit"}, // 31
+
+ {"dm1", "Place of Two Deaths"}, // 32
+ {"dm2", "Claustrophobopolis"},
+ {"dm3", "The Abandoned Base"},
+ {"dm4", "The Bad Place"},
+ {"dm5", "The Cistern"},
+ {"dm6", "The Dark Zone"}
+};
+
+typedef struct
+{
+ char *description;
+ int firstLevel;
+ int levels;
+} episode_t;
+
+episode_t episodes[] =
+{
+ {"Welcome to Quake", 0, 1},
+ {"Doomed Dimension", 1, 8},
+ {"Realm of Black Magic", 9, 7},
+ {"Netherworld", 16, 7},
+ {"The Elder World", 23, 8},
+ {"Final Level", 31, 1},
+ {"Deathmatch Arena", 32, 6}
+};
+
+int startepisode;
+int startlevel;
+int maxplayers;
+qboolean m_serverInfoMessage = false;
+double m_serverInfoMessageTime;
+
+//==================== Map Find System By Crow_bar =============================
+
+void Map_Finder(void)
+{
+
+#ifdef KERNEL_MODE
+ SceUID dir = sceIoDopen(va("%s/maps", com_gamedir));
+#else
+ SceUID dir = sceIoDopen(va("nzp/maps"));
+#endif // KERNEL_MODE
+
+ if(dir < 0)
+ {
+ Sys_Error ("Map_Finder");
+ return;
+ }
+
+ SceIoDirent dirent;
+
+ memset(&dirent, 0, sizeof(SceIoDirent));
+
+ for (int i = 0; i < 50; i++) {
+ custom_maps[i].occupied = false;
+ }
+
+ while(sceIoDread(dir, &dirent) > 0)
+ {
+ if(dirent.d_name[0] == '.')
+ {
+ continue;
+ }
+
+ if(!strcmp(COM_FileExtension(dirent.d_name),"bsp")|| !strcmp(COM_FileExtension(dirent.d_name),"BSP"))
+ {
+ char ntype[32];
+
+ COM_StripExtension(dirent.d_name, ntype);
+ custom_maps[user_maps_num].occupied = true;
+ custom_maps[user_maps_num].map_name = malloc(sizeof(char)*32);
+ sprintf(custom_maps[user_maps_num].map_name, "%s", ntype);
+
+ char* setting_path;
+ int setting_file;
+ SceIoStat setting_info;
+
+ setting_path = malloc(sizeof(char)*64);
+ custom_maps[user_maps_num].map_thumbnail_path = malloc(sizeof(char)*64);
+#ifdef KERNEL_MODE
+ strcpy(setting_path, va("%s/maps/", com_gamedir));
+#else
+ strcpy(setting_path, va("nzp/maps/"));
+#endif // KERNEL_MODE
+ strcpy(custom_maps[user_maps_num].map_thumbnail_path, "gfx/menu/custom/");
+ strcat(setting_path, custom_maps[user_maps_num].map_name);
+ strcat(custom_maps[user_maps_num].map_thumbnail_path, custom_maps[user_maps_num].map_name);
+ strcat(setting_path, ".txt");
+
+ sceIoGetstat(setting_path, &setting_info);
+ setting_file = sceIoOpen(setting_path, PSP_O_RDONLY, 0);
+
+ if (setting_file >= 0) {
+
+ int state;
+ state = 0;
+ int value;
+
+ custom_maps[user_maps_num].map_name_pretty = malloc(sizeof(char)*32);
+ custom_maps[user_maps_num].map_desc_1 = malloc(sizeof(char)*40);
+ custom_maps[user_maps_num].map_desc_2 = malloc(sizeof(char)*40);
+ custom_maps[user_maps_num].map_desc_3 = malloc(sizeof(char)*40);
+ custom_maps[user_maps_num].map_desc_4 = malloc(sizeof(char)*40);
+ custom_maps[user_maps_num].map_desc_5 = malloc(sizeof(char)*40);
+ custom_maps[user_maps_num].map_desc_6 = malloc(sizeof(char)*40);
+ custom_maps[user_maps_num].map_desc_7 = malloc(sizeof(char)*40);
+ custom_maps[user_maps_num].map_desc_8 = malloc(sizeof(char)*40);
+ custom_maps[user_maps_num].map_author = malloc(sizeof(char)*40);
+
+ char* buffer = (char*)calloc(setting_info.st_size+1, sizeof(char));
+ sceIoRead(setting_file, buffer, setting_info.st_size);
+
+ strtok(buffer, "\n");
+ while(buffer != NULL) {
+ switch(state) {
+ case 0: strcpy(custom_maps[user_maps_num].map_name_pretty, buffer); break;
+ case 1: strcpy(custom_maps[user_maps_num].map_desc_1, buffer); break;
+ case 2: strcpy(custom_maps[user_maps_num].map_desc_2, buffer); break;
+ case 3: strcpy(custom_maps[user_maps_num].map_desc_3, buffer); break;
+ case 4: strcpy(custom_maps[user_maps_num].map_desc_4, buffer); break;
+ case 5: strcpy(custom_maps[user_maps_num].map_desc_5, buffer); break;
+ case 6: strcpy(custom_maps[user_maps_num].map_desc_6, buffer); break;
+ case 7: strcpy(custom_maps[user_maps_num].map_desc_7, buffer); break;
+ case 8: strcpy(custom_maps[user_maps_num].map_desc_8, buffer); break;
+ case 9: strcpy(custom_maps[user_maps_num].map_author, buffer); break;
+ case 10: value = 0; sscanf(buffer, "%d", &value); custom_maps[user_maps_num].map_use_thumbnail = value; break;
+ case 11: value = 0; sscanf(buffer, "%d", &value); custom_maps[user_maps_num].map_allow_game_settings = value; break;
+ default: break;
+ }
+ state++;
+ buffer = strtok(NULL, "\n");
+ }
+ free(buffer);
+ buffer = 0;
+ sceIoClose(setting_file);
+ }
+ user_maps_num++;
+ }
+ memset(&dirent, 0, sizeof(SceIoDirent));
+ }
+ sceIoDclose(dir);
+
+ custom_map_pages = (int)ceil((double)(user_maps_num + 1)/15);
+}
+//==============================================================================
+void M_Menu_GameOptions_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_gameoptions;
+ m_entersound = true;
+ if (maxplayers == 0)
+ maxplayers = svs.maxclients;
+ if (maxplayers < 2)
+ maxplayers = svs.maxclientslimit;
+}
+
+
+int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120};
+#define NUM_GAMEOPTIONS 9
+int gameoptions_cursor;
+
+void M_GameOptions_Draw (void)
+{
+ int x;
+
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+ //else
+ //Draw_AlphaPic (0, 0, pause_bk, 0.4);
+
+ M_DrawTextBox (152, 32, 10, 1);
+ M_PrintOld (160, 40, "begin game");
+
+ M_PrintOld (0, 56, " Max players");
+ M_PrintOld (160, 56, va("%i", maxplayers) );
+
+ M_PrintOld (0, 64, " Game Type");
+ if (coop.value)
+ M_PrintOld (160, 64, "Cooperative");
+ else
+ M_PrintOld (160, 64, "Deathmatch");
+
+ M_PrintOld (0, 72, " Teamplay");
+
+ char *msg;
+
+ switch((int)teamplay.value)
+ {
+ case 1: msg = "No Friendly Fire"; break;
+ case 2: msg = "Friendly Fire"; break;
+ default: msg = "Off"; break;
+ }
+ M_PrintOld (160, 72, msg);
+
+
+ M_PrintOld (0, 80, " Skill");
+ if (skill.value == 0)
+ M_PrintOld (160, 80, "Easy difficulty");
+ else if (skill.value == 1)
+ M_PrintOld (160, 80, "Normal difficulty");
+ else if (skill.value == 2)
+ M_PrintOld (160, 80, "Hard difficulty");
+ else
+ M_PrintOld (160, 80, "Nightmare difficulty");
+
+ M_PrintOld (0, 88, " Frag Limit");
+ if (fraglimit.value == 0)
+ M_PrintOld (160, 88, "none");
+ else
+ M_PrintOld (160, 88, va("%i points", (int)fraglimit.value));
+
+ M_PrintOld (0, 96, " Time Limit");
+ if (timelimit.value == 0)
+ M_PrintOld (160, 96, "none");
+ else
+ M_PrintOld (160, 96, va("%i minutes", (int)timelimit.value));
+
+ M_PrintOld (0, 112, " Episode");
+
+ if(user_maps)
+ M_PrintOld (160, 112, "User Maps");
+ else
+ M_PrintOld (160, 112, episodes[startepisode].description);
+
+ M_PrintOld (0, 120, " Level");
+
+ if(user_maps)
+ {
+ M_PrintOld (160, 120, user_levels[startlevel]);
+ }
+ else
+ {
+ M_PrintOld (160, 120, levels[episodes[startepisode].firstLevel + startlevel].description);
+ M_PrintOld (160, 128, levels[episodes[startepisode].firstLevel + startlevel].name);
+ }
+
+// line cursor
+ M_DrawCharacter2 (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1));
+
+ if (m_serverInfoMessage)
+ {
+ if ((realtime - m_serverInfoMessageTime) < 5.0)
+ {
+ x = (320-26*8)/2;
+ M_DrawTextBox (x, 138, 24, 4);
+ x += 8;
+ M_PrintOld (x, 146, " More than 4 players ");
+ M_PrintOld (x, 154, " requires using command ");
+ M_PrintOld (x, 162, "line parameters; please ");
+ M_PrintOld (x, 170, " see techinfo.txt. ");
+ }
+ else
+ {
+ m_serverInfoMessage = false;
+ }
+ }
+}
+
+
+void M_NetStart_Change (int dir)
+{
+ int count;
+
+ switch (gameoptions_cursor)
+ {
+ case 1:
+ maxplayers += dir;
+ if (maxplayers > svs.maxclientslimit)
+ {
+ maxplayers = svs.maxclientslimit;
+ m_serverInfoMessage = true;
+ m_serverInfoMessageTime = realtime;
+ }
+ if (maxplayers < 2)
+ maxplayers = 2;
+ break;
+
+ case 2:
+ Cvar_SetValue ("coop", coop.value ? 0 : 1);
+ break;
+
+ case 3:
+ count = 2;
+
+ Cvar_SetValue ("teamplay", teamplay.value + dir);
+ if (teamplay.value > count)
+ Cvar_SetValue ("teamplay", 0);
+ else if (teamplay.value < 0)
+ Cvar_SetValue ("teamplay", count);
+ break;
+
+ case 4:
+ Cvar_SetValue ("skill", skill.value + dir);
+ if (skill.value > 3)
+ Cvar_SetValue ("skill", 0);
+ if (skill.value < 0)
+ Cvar_SetValue ("skill", 3);
+ break;
+
+ case 5:
+ Cvar_SetValue ("fraglimit", fraglimit.value + dir*10);
+ if (fraglimit.value > 100)
+ Cvar_SetValue ("fraglimit", 0);
+ if (fraglimit.value < 0)
+ Cvar_SetValue ("fraglimit", 100);
+ break;
+
+ case 6:
+ Cvar_SetValue ("timelimit", timelimit.value + dir*5);
+ if (timelimit.value > 60)
+ Cvar_SetValue ("timelimit", 0);
+ if (timelimit.value < 0)
+ Cvar_SetValue ("timelimit", 60);
+ break;
+
+ case 7:
+ startepisode += dir;
+ if (user_maps)
+ {
+ count = 1;
+ }
+ else
+ {
+ count = 2;
+ }
+ if (startepisode < 0)
+ startepisode = count - 1;
+
+ if (startepisode >= count)
+ startepisode = 0;
+
+ startlevel = 0;
+ break;
+
+ case 8:
+ startlevel += dir;
+ if(user_maps)
+ count = user_maps_num;
+ else
+ count = episodes[startepisode].levels;
+ if (startlevel < 0)
+ startlevel = count - 1;
+
+ if (startlevel >= count)
+ startlevel = 0;
+ break;
+ }
+}
+
+void M_GameOptions_Key (int key)
+{
+ switch (key)
+ {
+ case K_ESCAPE:
+ M_Menu_Net_f ();
+ break;
+
+ case K_UPARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ gameoptions_cursor--;
+ if (gameoptions_cursor < 0)
+ gameoptions_cursor = NUM_GAMEOPTIONS-1;
+ break;
+
+ case K_DOWNARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ gameoptions_cursor++;
+ if (gameoptions_cursor >= NUM_GAMEOPTIONS)
+ gameoptions_cursor = 0;
+ break;
+
+ case K_LEFTARROW:
+ if (gameoptions_cursor == 0)
+ break;
+ S_LocalSound ("sounds/menu/navigate.wav");
+ M_NetStart_Change (-1);
+ break;
+
+ case K_RIGHTARROW:
+ if (gameoptions_cursor == 0)
+ break;
+ S_LocalSound ("sounds/menu/navigate.wav");
+ M_NetStart_Change (1);
+ break;
+
+ case K_ENTER:
+ S_LocalSound ("sounds/menu/enter.wav");
+ if (gameoptions_cursor == 0)
+ {
+ if (sv.active)
+ Cbuf_AddText ("disconnect\n");
+ Cbuf_AddText ("listen 0\n"); // so host_netport will be re-examined
+ Cbuf_AddText ( va ("maxplayers %u\n", maxplayers) );
+ SCR_BeginLoadingPlaque ();
+
+ if(user_maps)
+ Cbuf_AddText ( va ("map %s\n", user_levels[startlevel]));
+ else
+ Cbuf_AddText ( va ("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name) );
+ return;
+ }
+
+ M_NetStart_Change (1);
+ break;
+ }
+}
+
+//=============================================================================
+/* SEARCH MENU */
+
+qboolean searchComplete = false;
+double searchCompleteTime;
+
+void M_Menu_Search_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_search;
+ m_entersound = false;
+ slistSilent = true;
+ slistLocal = false;
+ searchComplete = false;
+ NET_Slist_f();
+
+}
+
+
+void M_Search_Draw (void)
+{
+ int x;
+
+ if (key_dest != key_menu_pause)
+ Draw_Pic (0, 0, menu_bk);
+ //else
+ //Draw_AlphaPic (0, 0, pause_bk, 0.4);
+
+ x = (320/2) - ((12*8)/2) + 4;
+ M_DrawTextBox (x-8, 32, 12, 1);
+ M_PrintOld (x, 40, "Searching...");
+
+ if(slistInProgress)
+ {
+ NET_Poll();
+ return;
+ }
+
+ if (! searchComplete)
+ {
+ searchComplete = true;
+ searchCompleteTime = realtime;
+ }
+
+ if (hostCacheCount)
+ {
+ M_Menu_ServerList_f ();
+ return;
+ }
+
+ M_PrintWhiteOld ((320/2) - ((22*8)/2), 64, "No Quake servers found");
+ if ((realtime - searchCompleteTime) < 3.0)
+ return;
+
+ M_Menu_LanConfig_f ();
+}
+
+
+void M_Search_Key (int key)
+{
+}
+
+//=============================================================================
+/* SLIST MENU
+
+int slist_cursor;
+qboolean slist_sorted;
+
+void M_Menu_ServerList_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_slist;
+ m_entersound = true;
+ slist_cursor = 0;
+ m_return_onerror = false;
+ m_return_reason[0] = 0;
+ slist_sorted = false;
+}
+
+
+void M_ServerList_Draw (void)
+{
+ int n;
+ char string [64];
+ qpic_t *p;
+
+ if (!slist_sorted)
+ {
+ if (hostCacheCount > 1)
+ {
+ int i,j;
+ hostcache_t temp;
+ for (i = 0; i < hostCacheCount; i++)
+ for (j = i+1; j < hostCacheCount; j++)
+ if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
+ {
+ Q_memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
+ Q_memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
+ Q_memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
+ }
+ }
+ slist_sorted = true;
+ }
+
+ p = Draw_CachePic ("gfx/p_multi.lmp");
+ M_DrawPic ( (320-p->width)/2, 4, p);
+ for (n = 0; n < hostCacheCount; n++)
+ {
+ if (hostcache[n].maxusers)
+ sprintf(string, "%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
+ else
+ sprintf(string, "%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
+ M_PrintOld (16, 32 + 8*n, string);
+ }
+ M_DrawCharacter2 (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1));
+
+ if (*m_return_reason)
+ M_PrintWhiteOld (16, 148, m_return_reason);
+}
+
+
+void M_ServerList_Key (int k)
+{
+ switch (k)
+ {
+ case K_ESCAPE:
+ M_Menu_LanConfig_f ();
+ break;
+
+ case K_SPACE:
+ M_Menu_Search_f ();
+ break;
+
+ case K_UPARROW:
+ case K_LEFTARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ slist_cursor--;
+ if (slist_cursor < 0)
+ slist_cursor = hostCacheCount - 1;
+ break;
+
+ case K_DOWNARROW:
+ case K_RIGHTARROW:
+ S_LocalSound ("sounds/menu/navigate.wav");
+ slist_cursor++;
+ if (slist_cursor >= hostCacheCount)
+ slist_cursor = 0;
+ break;
+
+ case K_ENTER:
+ S_LocalSound ("sounds/menu/enter.wav");
+ m_return_state = m_state;
+ m_return_onerror = true;
+ slist_sorted = false;
+ key_dest = key_game;
+ m_state = m_none;
+ Cbuf_AddText ( va ("connect \"%s\"\n", hostcache[slist_cursor].cname) );
+ break;
+
+ default:
+ break;
+ }
+
+}
+*/
+//=============================================================================
+/* Menu Subsystem */
+
+
+void M_Init (void)
+{
+ Cmd_AddCommand ("togglemenu", M_ToggleMenu_f);
+
+ Cmd_AddCommand ("menu_main", M_Menu_Main_f);
+ Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f);
+ Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f);
+ Cmd_AddCommand ("menu_setup", M_Menu_Setup_f);
+ Cmd_AddCommand ("menu_options", M_Menu_Options_f);
+ Cmd_AddCommand ("menu_keys", M_Menu_Keys_f);
+ Cmd_AddCommand ("menu_slist", M_Menu_ServerList_f);
+ Cmd_AddCommand ("credits", M_Menu_Credits_f);
+ Cmd_AddCommand ("menu_quit", M_Menu_Quit_f);
+ Cmd_AddCommand ("savea", Save_Achivements);
+ Cmd_AddCommand ("loada", Load_Achivements);
+
+ Map_Finder();
+
+}
+
+
+void M_Draw (void)
+{
+ if (m_state == m_none || (key_dest != key_menu && key_dest != key_menu_pause))
+ return;
+
+ if (!m_recursiveDraw)
+ {
+ scr_copyeverything = 1;
+
+ if (scr_con_current)
+ {
+ Draw_ConsoleBackground (vid.height);
+ VID_UnlockBuffer ();
+ S_ExtraUpdate ();
+ VID_LockBuffer ();
+ }
+ else
+ Draw_FadeScreen ();
+
+ scr_fullupdate = 0;
+ }
+ else
+ {
+ m_recursiveDraw = false;
+ }
+
+ switch (m_state)
+ {
+ case m_none:
+ break;
+
+ case m_start:
+ M_Start_Menu_Draw();
+ break;
+
+ case m_paused_menu:
+ M_Paused_Menu_Draw();
+ break;
+
+ case m_main:
+ M_Main_Draw ();
+ break;
+
+ case m_map:
+ M_Map_Draw ();
+ break;
+
+ case m_singleplayer:
+ M_SinglePlayer_Draw ();
+ break;
+
+ case m_multiplayer:
+ M_MultiPlayer_Draw ();
+ break;
+
+ case m_achievement:
+ M_Achievement_Draw ();
+ break;
+
+ case m_setup:
+ M_Setup_Draw ();
+ break;
+
+ case m_net:
+ M_Net_Draw ();
+ break;
+
+ case m_options:
+ M_Options_Draw ();
+ break;
+
+ case m_keys:
+ M_Keys_Draw ();
+ break;
+
+ case m_video:
+ M_Screen_Draw ();
+ break;
+
+ case m_audio:
+ M_Audio_Draw ();
+ break;
+
+ case m_gameplay:
+ M_Gameplay_Draw ();
+ break;
+
+ case m_credits:
+ M_Credits_Draw ();
+ break;
+
+ case m_quit:
+ M_Quit_Draw ();
+ break;
+
+ case m_restart:
+ M_Restart_Draw ();
+ break;
+
+ case m_exit:
+ M_Exit_Draw ();
+ break;
+
+ case m_serialconfig:
+ M_SerialConfig_Draw ();
+ break;
+
+ case m_modemconfig:
+ M_ModemConfig_Draw ();
+ break;
+
+ case m_lanconfig:
+ M_LanConfig_Draw ();
+ break;
+
+ case m_gameoptions:
+ M_GameOptions_Draw ();
+ break;
+
+ case m_search:
+ M_Search_Draw ();
+ break;
+
+ case m_slist:
+ M_ServerList_Draw ();
+ break;
+
+ case m_sedit:
+ M_SEdit_Draw ();
+ break;
+
+ case m_osk:
+ M_OSK_Draw();
+ break;
+ }
+
+ if (m_entersound)
+ {
+ S_LocalSound ("sounds/menu/enter.wav");
+ m_entersound = false;
+ }
+
+ VID_UnlockBuffer ();
+ S_ExtraUpdate ();
+ VID_LockBuffer ();
+}
+
+
+void M_Keydown (int key)
+{
+ switch (m_state)
+ {
+ case m_none:
+ return;
+
+ case m_paused_menu:
+ M_Paused_Menu_Key (key);
+ break;
+
+ case m_start:
+ M_Start_Key (key);
+ break;
+ case m_main:
+ M_Main_Key (key);
+ return;
+
+ case m_map:
+ M_Map_Key (key);
+ return;
+
+ case m_singleplayer:
+ M_SinglePlayer_Key (key);
+ return;
+
+ case m_multiplayer:
+ M_MultiPlayer_Key (key);
+ return;
+
+ case m_achievement:
+ M_Achievement_Key (key);
+ return;
+
+ case m_setup:
+ M_Setup_Key (key);
+ return;
+
+ case m_net:
+ M_Net_Key (key);
+ return;
+
+ case m_options:
+ M_Options_Key (key);
+ return;
+
+ case m_keys:
+ M_Keys_Key (key);
+ return;
+
+ case m_video:
+ M_Screen_Key (key);
+ return;
+
+ case m_audio:
+ M_Audio_Key (key);
+ return;
+
+ case m_gameplay:
+ M_Gameplay_Key (key);
+ return;
+
+ case m_credits:
+ M_Credits_Key (key);
+ return;
+
+ case m_quit:
+ M_Quit_Key (key);
+ return;
+
+ case m_restart:
+ M_Restart_Key (key);
+ return;
+
+ case m_exit:
+ M_Exit_Key (key);
+ return;
+
+ case m_serialconfig:
+ M_SerialConfig_Key (key);
+ return;
+
+ case m_modemconfig:
+ M_ModemConfig_Key (key);
+ return;
+
+ case m_lanconfig:
+ M_LanConfig_Key (key);
+ return;
+
+ case m_gameoptions:
+ M_GameOptions_Key (key);
+ return;
+
+ case m_search:
+ M_Search_Key (key);
+ break;
+
+
+ case m_slist:
+ M_ServerList_Key (key);
+ return;
+
+ case m_sedit:
+ M_SEdit_Key (key);
+ break;
+
+ case m_osk:
+ M_OSK_Key(key);
+ }
+}
+
+
+void M_ConfigureNetSubsystem(void)
+{
+// enable/disable net systems to match desired config
+
+ Cbuf_AddText ("stopdemo\n");
+ if (SerialConfig || DirectConfig)
+ {
+ Cbuf_AddText ("com1 enable\n");
+ }
+
+ if (IPXConfig || TCPIPConfig)
+ net_hostport = lanConfig_port;
+}
+
diff --git a/source/menu.h b/source/menu.h
new file mode 100644
index 0000000..e6c36c7
--- /dev/null
+++ b/source/menu.h
@@ -0,0 +1,40 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+//
+// the net drivers should just set the apropriate bits in m_activenet,
+// instead of having the menu code look through their internal tables
+//
+#define MNET_IPX 1
+#define MNET_TCP 2
+
+extern int m_activenet;
+
+//
+// menus
+//
+void M_Init (void);
+void M_Keydown (int key);
+void M_Draw (void);
+void M_ToggleMenu_f (void);
+
+
+void Save_Achivements(void);
+void Load_Achivements(void);
diff --git a/source/modelgen.h b/source/modelgen.h
new file mode 100644
index 0000000..6273a8d
--- /dev/null
+++ b/source/modelgen.h
@@ -0,0 +1,161 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+//
+// modelgen.h: header file for model generation program
+//
+
+// *********************************************************
+// * This file must be identical in the modelgen directory *
+// * and in the Quake directory, because it's used to *
+// * pass data from one to the other via model files. *
+// *********************************************************
+
+#ifdef INCLUDELIBS
+
+#include
+#include
+#include
+#include
+
+#include "cmdlib.h"
+#include "scriplib.h"
+#include "trilib.h"
+#include "lbmlib.h"
+#include "mathlib.h"
+
+#endif
+
+#define ALIAS_VERSION 6
+#define ALIAS_NEWVERSION 50
+
+#define ALIAS_ONSEAM 0x0020
+
+// must match definition in spritegn.h
+#ifndef SYNCTYPE_T
+#define SYNCTYPE_T
+typedef enum {ST_SYNC=0, ST_RAND } synctype_t;
+#endif
+
+typedef enum { ALIAS_SINGLE=0, ALIAS_GROUP } aliasframetype_t;
+
+typedef enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP } aliasskintype_t;
+
+typedef struct {
+ int ident;
+ int version;
+ vec3_t scale;
+ vec3_t scale_origin;
+ float boundingradius;
+ vec3_t eyeposition;
+ int numskins;
+ int skinwidth;
+ int skinheight;
+ int numverts;
+ int numtris;
+ int numframes;
+ synctype_t synctype;
+ int flags;
+ float size;
+} mdl_t;
+
+typedef struct
+{
+ int ident;
+ int version;
+ vec3_t scale;
+ vec3_t scale_origin;
+ float boundingradius;
+ vec3_t eyeposition;
+ int numskins;
+ int skinwidth;
+ int skinheight;
+ int numverts;
+ int numtris;
+ int numframes;
+ synctype_t synctype;
+ int flags;
+ float size;
+ int num_st_verts;
+} newmdl_t;
+
+// TODO: could be shorts
+
+typedef struct {
+ int onseam;
+ int s;
+ int t;
+} stvert_t;
+
+typedef struct dtriangle_s {
+ int facesfront;
+ int vertindex[3];
+} dtriangle_t;
+
+typedef struct dnewtriangle_s
+{
+ int facesfront;
+ unsigned short vertindex[3];
+ unsigned short stindex[3];
+} dnewtriangle_t;
+
+#define DT_FACES_FRONT 0x0010
+
+// This mirrors trivert_t in trilib.h, is present so Quake knows how to
+// load this data
+
+typedef struct {
+ byte v[3];
+ byte lightnormalindex;
+} trivertx_t;
+
+typedef struct {
+ trivertx_t bboxmin; // lightnormal isn't used
+ trivertx_t bboxmax; // lightnormal isn't used
+ char name[16]; // frame name from grabbing
+} daliasframe_t;
+
+typedef struct {
+ int numframes;
+ trivertx_t bboxmin; // lightnormal isn't used
+ trivertx_t bboxmax; // lightnormal isn't used
+} daliasgroup_t;
+
+typedef struct {
+ int numskins;
+} daliasskingroup_t;
+
+typedef struct {
+ float interval;
+} daliasinterval_t;
+
+typedef struct {
+ float interval;
+} daliasskininterval_t;
+
+typedef struct {
+ aliasframetype_t type;
+} daliasframetype_t;
+
+typedef struct {
+ aliasskintype_t type;
+} daliasskintype_t;
+
+#define IDPOLYHEADER (('O'<<24)+('P'<<16)+('D'<<8)+'I') // little-endian "IDPO"
+#define RAPOLYHEADER (('O'<<24)+('P'<<16)+('A'<<8)+'R') // little-endian "RAPO"
diff --git a/source/net.h b/source/net.h
new file mode 100644
index 0000000..cbead36
--- /dev/null
+++ b/source/net.h
@@ -0,0 +1,336 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// net.h -- quake's interface to the networking layer
+
+struct qsockaddr
+{
+ unsigned char sa_len;
+ unsigned char sa_family;
+ unsigned char sa_data[14];
+};
+
+
+#define NET_NAMELEN 64
+
+// HACK HACK HACK - certain maps will hit this limit.. no clue why.. so let's just
+// double it lol
+#define NET_MAXMESSAGE /*8192*/16384
+#define NET_HEADERSIZE (2 * sizeof(unsigned int))
+#define NET_DATAGRAMSIZE (MAX_DATAGRAM + NET_HEADERSIZE)
+
+// NetHeader flags
+#define NETFLAG_LENGTH_MASK 0x0000ffff
+#define NETFLAG_DATA 0x00010000
+#define NETFLAG_ACK 0x00020000
+#define NETFLAG_NAK 0x00040000
+#define NETFLAG_EOM 0x00080000
+#define NETFLAG_UNRELIABLE 0x00100000
+#define NETFLAG_CTL 0x80000000
+
+
+#define NET_PROTOCOL_VERSION 3
+
+// This is the network info/connection protocol. It is used to find Quake
+// servers, get info about them, and connect to them. Once connected, the
+// Quake game protocol (documented elsewhere) is used.
+//
+//
+// General notes:
+// game_name is currently always "QUAKE", but is there so this same protocol
+// can be used for future games as well; can you say Quake2?
+//
+// CCREQ_CONNECT
+// string game_name "QUAKE"
+// byte net_protocol_version NET_PROTOCOL_VERSION
+//
+// CCREQ_SERVER_INFO
+// string game_name "QUAKE"
+// byte net_protocol_version NET_PROTOCOL_VERSION
+//
+// CCREQ_PLAYER_INFO
+// byte player_number
+//
+// CCREQ_RULE_INFO
+// string rule
+//
+//
+//
+// CCREP_ACCEPT
+// long port
+//
+// CCREP_REJECT
+// string reason
+//
+// CCREP_SERVER_INFO
+// string server_address
+// string host_name
+// string level_name
+// byte current_players
+// byte max_players
+// byte protocol_version NET_PROTOCOL_VERSION
+//
+// CCREP_PLAYER_INFO
+// byte player_number
+// string name
+// long colors
+// long frags
+// long connect_time
+// string address
+//
+// CCREP_RULE_INFO
+// string rule
+// string value
+
+// note:
+// There are two address forms used above. The short form is just a
+// port number. The address that goes along with the port is defined as
+// "whatever address you receive this reponse from". This lets us use
+// the host OS to solve the problem of multiple host addresses (possibly
+// with no routing between them); the host will use the right address
+// when we reply to the inbound connection request. The long from is
+// a full address and port in a string. It is used for returning the
+// address of a server that is not running locally.
+
+#define CCREQ_CONNECT 0x01
+#define CCREQ_SERVER_INFO 0x02
+#define CCREQ_PLAYER_INFO 0x03
+#define CCREQ_RULE_INFO 0x04
+
+#define CCREP_ACCEPT 0x81
+#define CCREP_REJECT 0x82
+#define CCREP_SERVER_INFO 0x83
+#define CCREP_PLAYER_INFO 0x84
+#define CCREP_RULE_INFO 0x85
+
+typedef struct qsocket_s
+{
+ struct qsocket_s *next;
+ double connecttime;
+ double lastMessageTime;
+ double lastSendTime;
+
+ qboolean disconnected;
+ qboolean canSend;
+ qboolean sendNext;
+
+ int driver;
+ int landriver;
+ int socket;
+ void *driverdata;
+
+ unsigned int ackSequence;
+ unsigned int sendSequence;
+ unsigned int unreliableSendSequence;
+ int sendMessageLength;
+ byte sendMessage [NET_MAXMESSAGE];
+
+ unsigned int receiveSequence;
+ unsigned int unreliableReceiveSequence;
+ int receiveMessageLength;
+ byte receiveMessage [NET_MAXMESSAGE];
+
+ struct qsockaddr addr;
+ char address[NET_NAMELEN];
+
+} qsocket_t;
+
+extern qsocket_t *net_activeSockets;
+extern qsocket_t *net_freeSockets;
+extern int net_numsockets;
+
+typedef struct
+{
+ char *name;
+ qboolean initialized;
+ int controlSock;
+ int (*Init) (void);
+ void (*Shutdown) (void);
+ void (*Listen) (qboolean state);
+ int (*OpenSocket) (int port);
+ int (*CloseSocket) (int socket);
+ int (*Connect) (int socket, struct qsockaddr *addr);
+ int (*CheckNewConnections) (void);
+ int (*Read) (int socket, byte *buf, int len, struct qsockaddr *addr);
+ int (*Write) (int socket, byte *buf, int len, struct qsockaddr *addr);
+ int (*Broadcast) (int socket, byte *buf, int len);
+ char * (*AddrToString) (struct qsockaddr *addr);
+ int (*StringToAddr) (char *string, struct qsockaddr *addr);
+ int (*GetSocketAddr) (int socket, struct qsockaddr *addr);
+ int (*GetNameFromAddr) (struct qsockaddr *addr, char *name);
+ int (*GetAddrFromName) (char *name, struct qsockaddr *addr);
+ int (*AddrCompare) (struct qsockaddr *addr1, struct qsockaddr *addr2);
+ int (*GetSocketPort) (struct qsockaddr *addr);
+ int (*SetSocketPort) (struct qsockaddr *addr, int port);
+} net_landriver_t;
+
+#define MAX_NET_DRIVERS 8
+extern int net_numlandrivers;
+extern net_landriver_t net_landrivers[MAX_NET_DRIVERS];
+
+typedef struct
+{
+ char *name;
+ qboolean initialized;
+ int (*Init) (void);
+ void (*Listen) (qboolean state);
+ void (*SearchForHosts) (qboolean xmit);
+ qsocket_t *(*Connect) (char *host);
+ qsocket_t *(*CheckNewConnections) (void);
+ int (*QGetMessage) (qsocket_t *sock);
+ int (*QSendMessage) (qsocket_t *sock, sizebuf_t *data);
+ int (*SendUnreliableMessage) (qsocket_t *sock, sizebuf_t *data);
+ qboolean (*CanSendMessage) (qsocket_t *sock);
+ qboolean (*CanSendUnreliableMessage) (qsocket_t *sock);
+ void (*Close) (qsocket_t *sock);
+ void (*Shutdown) (void);
+ int controlSock;
+} net_driver_t;
+
+extern int net_numdrivers;
+extern net_driver_t net_drivers[MAX_NET_DRIVERS];
+
+extern int DEFAULTnet_hostport;
+extern int net_hostport;
+
+extern int net_driverlevel;
+extern cvar_t hostname;
+extern char playername[];
+extern int playercolor;
+
+extern int messagesSent;
+extern int messagesReceived;
+extern int unreliableMessagesSent;
+extern int unreliableMessagesReceived;
+
+qsocket_t *NET_NewQSocket (void);
+void NET_FreeQSocket(qsocket_t *);
+double SetNetTime(void);
+
+
+#define HOSTCACHESIZE 8
+
+typedef struct
+{
+ char name[16];
+ char map[16];
+ char cname[32];
+ int users;
+ int maxusers;
+ int driver;
+ int ldriver;
+ struct qsockaddr addr;
+} hostcache_t;
+
+extern int hostCacheCount;
+extern hostcache_t hostcache[HOSTCACHESIZE];
+
+#ifndef htonl
+extern unsigned long htonl (unsigned long hostlong);
+#endif
+#ifndef htons
+extern unsigned short htons (unsigned short hostshort);
+#endif
+#ifndef ntohl
+extern unsigned long ntohl (unsigned long netlong);
+#endif
+#ifndef ntohs
+extern unsigned short ntohs (unsigned short netshort);
+#endif
+
+//============================================================================
+//
+// public network functions
+//
+//============================================================================
+
+extern double net_time;
+extern sizebuf_t net_message;
+extern int net_activeconnections;
+
+void NET_Init (void);
+void NET_Shutdown (void);
+
+struct qsocket_s *NET_CheckNewConnections (void);
+// returns a new connection number if there is one pending, else -1
+
+struct qsocket_s *NET_Connect (char *host);
+// called by client to connect to a host. Returns -1 if not able to
+
+qboolean NET_CanSendMessage (qsocket_t *sock);
+// Returns true or false if the given qsocket can currently accept a
+// message to be transmitted.
+
+int NET_GetMessage (struct qsocket_s *sock);
+// returns data in net_message sizebuf
+// returns 0 if no data is waiting
+// returns 1 if a message was received
+// returns 2 if an unreliable message was received
+// returns -1 if the connection died
+
+int NET_SendMessage (struct qsocket_s *sock, sizebuf_t *data);
+int NET_SendUnreliableMessage (struct qsocket_s *sock, sizebuf_t *data);
+// returns 0 if the message connot be delivered reliably, but the connection
+// is still considered valid
+// returns 1 if the message was sent properly
+// returns -1 if the connection died
+
+int NET_SendToAll(sizebuf_t *data, int blocktime);
+// This is a reliable *blocking* send to all attached clients.
+
+
+void NET_Close (struct qsocket_s *sock);
+// if a dead connection is returned by a get or send function, this function
+// should be called when it is convenient
+
+// Server calls when a client is kicked off for a game related misbehavior
+// like an illegal protocal conversation. Client calls when disconnecting
+// from a server.
+// A netcon_t number will not be reused until this function is called for it
+
+void NET_Poll(void);
+
+
+typedef struct _PollProcedure
+{
+ struct _PollProcedure *next;
+ double nextTime;
+ void (*procedure)();
+ void *arg;
+} PollProcedure;
+
+void SchedulePollProcedure(PollProcedure *pp, double timeOffset);
+
+extern qboolean serialAvailable;
+extern qboolean ipxAvailable;
+extern qboolean tcpipAvailable;
+extern qboolean tcpipAdhoc;
+
+extern char my_ipx_address[NET_NAMELEN];
+extern char my_tcpip_address[NET_NAMELEN];
+extern void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem);
+extern void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem);
+extern void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+extern void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+
+extern qboolean slistInProgress;
+extern qboolean slistSilent;
+extern qboolean slistLocal;
+
+void NET_Slist_f (void);
diff --git a/source/net_dgrm.c b/source/net_dgrm.c
new file mode 100644
index 0000000..8508c7a
--- /dev/null
+++ b/source/net_dgrm.c
@@ -0,0 +1,1286 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// net_dgrm.c
+
+
+#include "pspkernel.h"
+
+#include "quakedef.h"
+#include "net_dgrm.h"
+
+// these two macros are to make the code more readable
+#define sfunc net_landrivers[sock->landriver]
+#define dfunc net_landrivers[net_landriverlevel]
+
+static int net_landriverlevel;
+
+/* statistic counters */
+int packetsSent = 0;
+int packetsReSent = 0;
+int packetsReceived = 0;
+int receivedDuplicateCount = 0;
+int shortPacketCount = 0;
+int droppedDatagrams;
+
+static int myDriverLevel;
+
+struct
+{
+ unsigned int length;
+ unsigned int sequence;
+ byte data[MAX_DATAGRAM];
+} packetBuffer;
+
+extern int m_return_state;
+extern int m_state;
+extern qboolean m_return_onerror;
+extern char m_return_reason[32];
+
+
+#ifdef DEBUG
+char *StrAddr (struct qsockaddr *addr)
+{
+ static char buf[34];
+ byte *p = (byte *)addr;
+ int n;
+
+ for (n = 0; n < 16; n++)
+ sprintf (buf + n * 2, "%02x", *p++);
+ return buf;
+}
+#endif
+
+
+
+
+
+int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+ unsigned int packetLen;
+ unsigned int dataLen;
+ unsigned int eom;
+
+#ifdef DEBUG
+ if (data->cursize == 0)
+ Sys_Error("Datagram_SendMessage: zero length message\n");
+
+ if (data->cursize > NET_MAXMESSAGE)
+ Sys_Error("Datagram_SendMessage: message too big %u\n", data->cursize);
+
+ if (sock->canSend == false)
+ Sys_Error("SendMessage: called with canSend == false\n");
+#endif
+
+ Q_memcpy(sock->sendMessage, data->data, data->cursize);
+ sock->sendMessageLength = data->cursize;
+
+ if (data->cursize <= MAX_DATAGRAM)
+ {
+ dataLen = data->cursize;
+ eom = NETFLAG_EOM;
+ }
+ else
+ {
+ dataLen = MAX_DATAGRAM;
+ eom = 0;
+ }
+ packetLen = NET_HEADERSIZE + dataLen;
+
+ packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
+ packetBuffer.sequence = BigLong(sock->sendSequence++);
+ Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen);
+
+ sock->canSend = false;
+
+ if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+ return -1;
+
+ sock->lastSendTime = net_time;
+ packetsSent++;
+ return 1;
+}
+
+
+int SendMessageNext (qsocket_t *sock)
+{
+ unsigned int packetLen;
+ unsigned int dataLen;
+ unsigned int eom;
+
+ if (sock->sendMessageLength <= MAX_DATAGRAM)
+ {
+ dataLen = sock->sendMessageLength;
+ eom = NETFLAG_EOM;
+ }
+ else
+ {
+ dataLen = MAX_DATAGRAM;
+ eom = 0;
+ }
+ packetLen = NET_HEADERSIZE + dataLen;
+
+ packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
+ packetBuffer.sequence = BigLong(sock->sendSequence++);
+ Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen);
+
+ sock->sendNext = false;
+
+ if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+ return -1;
+
+ sock->lastSendTime = net_time;
+ packetsSent++;
+ return 1;
+}
+
+
+int ReSendMessage (qsocket_t *sock)
+{
+ unsigned int packetLen;
+ unsigned int dataLen;
+ unsigned int eom;
+
+ if (sock->sendMessageLength <= MAX_DATAGRAM)
+ {
+ dataLen = sock->sendMessageLength;
+ eom = NETFLAG_EOM;
+ }
+ else
+ {
+ dataLen = MAX_DATAGRAM;
+ eom = 0;
+ }
+ packetLen = NET_HEADERSIZE + dataLen;
+
+ packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
+ packetBuffer.sequence = BigLong(sock->sendSequence - 1);
+ Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen);
+
+ sock->sendNext = false;
+
+ if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+ return -1;
+
+ sock->lastSendTime = net_time;
+ packetsReSent++;
+ return 1;
+}
+
+
+qboolean Datagram_CanSendMessage (qsocket_t *sock)
+{
+ if (sock->sendNext)
+ SendMessageNext (sock);
+
+ return sock->canSend;
+}
+
+
+qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock)
+{
+ return true;
+}
+
+
+int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
+{
+ int packetLen;
+
+#ifdef DEBUG
+ if (data->cursize == 0)
+ Sys_Error("Datagram_SendUnreliableMessage: zero length message\n");
+
+ if (data->cursize > MAX_DATAGRAM)
+ Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize);
+#endif
+
+ packetLen = NET_HEADERSIZE + data->cursize;
+
+ packetBuffer.length = BigLong(packetLen | NETFLAG_UNRELIABLE);
+ packetBuffer.sequence = BigLong(sock->unreliableSendSequence++);
+ Q_memcpy (packetBuffer.data, data->data, data->cursize);
+
+ if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+ return -1;
+
+ packetsSent++;
+ return 1;
+}
+
+
+int Datagram_GetMessage (qsocket_t *sock)
+{
+ unsigned int length;
+ unsigned int flags;
+ int ret = 0;
+ struct qsockaddr readaddr;
+ unsigned int sequence;
+ unsigned int count;
+
+#if 0
+ if (!sock->canSend)
+ if ((net_time - sock->lastSendTime) > 1.0)
+ ReSendMessage (sock);
+#endif
+
+ while(1)
+ {
+ length = sfunc.Read (sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr);
+
+// if ((rand() & 255) > 220)
+// continue;
+
+ if (length == 0)
+ break;
+
+ if (length == -1)
+ {
+ Con_Printf("Read error\n");
+ return -1;
+ }
+
+ if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0)
+ {
+#ifdef DEBUG
+ Con_DPrintf("Forged packet received\n");
+ Con_DPrintf("Expected: %s\n", StrAddr (&sock->addr));
+ Con_DPrintf("Received: %s\n", StrAddr (&readaddr));
+#endif
+ continue;
+ }
+
+ if (length < NET_HEADERSIZE)
+ {
+ shortPacketCount++;
+ continue;
+ }
+
+ length = BigLong(packetBuffer.length);
+ flags = length & (~NETFLAG_LENGTH_MASK);
+ length &= NETFLAG_LENGTH_MASK;
+
+ if (flags & NETFLAG_CTL)
+ continue;
+
+ sequence = BigLong(packetBuffer.sequence);
+ packetsReceived++;
+
+ if (flags & NETFLAG_UNRELIABLE)
+ {
+ if (sequence < sock->unreliableReceiveSequence)
+ {
+ Con_DPrintf("Got a stale datagram\n");
+ ret = 0;
+ break;
+ }
+ if (sequence != sock->unreliableReceiveSequence)
+ {
+ count = sequence - sock->unreliableReceiveSequence;
+ droppedDatagrams += count;
+ Con_DPrintf("Dropped %u datagram(s)\n", count);
+ }
+ sock->unreliableReceiveSequence = sequence + 1;
+
+ length -= NET_HEADERSIZE;
+
+ SZ_Clear (&net_message);
+ SZ_Write (&net_message, packetBuffer.data, length);
+
+ ret = 2;
+ break;
+ }
+
+ if (flags & NETFLAG_ACK)
+ {
+ if (sequence != (sock->sendSequence - 1))
+ {
+ Con_DPrintf("Stale ACK received\n");
+ continue;
+ }
+ if (sequence == sock->ackSequence)
+ {
+ sock->ackSequence++;
+ if (sock->ackSequence != sock->sendSequence)
+ Con_DPrintf("ack sequencing error\n");
+ }
+ else
+ {
+ Con_DPrintf("Duplicate ACK received\n");
+ continue;
+ }
+ sock->sendMessageLength -= MAX_DATAGRAM;
+ if (sock->sendMessageLength > 0)
+ {
+ Q_memcpy(sock->sendMessage, sock->sendMessage+MAX_DATAGRAM, sock->sendMessageLength);
+ sock->sendNext = true;
+ }
+ else
+ {
+ sock->sendMessageLength = 0;
+ sock->canSend = true;
+ }
+ continue;
+ }
+
+ if (flags & NETFLAG_DATA)
+ {
+ packetBuffer.length = BigLong(NET_HEADERSIZE | NETFLAG_ACK);
+ packetBuffer.sequence = BigLong(sequence);
+ sfunc.Write (sock->socket, (byte *)&packetBuffer, NET_HEADERSIZE, &readaddr);
+
+ if (sequence != sock->receiveSequence)
+ {
+ receivedDuplicateCount++;
+ continue;
+ }
+ sock->receiveSequence++;
+
+ length -= NET_HEADERSIZE;
+
+ if (flags & NETFLAG_EOM)
+ {
+ SZ_Clear(&net_message);
+ SZ_Write(&net_message, sock->receiveMessage, sock->receiveMessageLength);
+ SZ_Write(&net_message, packetBuffer.data, length);
+ sock->receiveMessageLength = 0;
+
+ ret = 1;
+ break;
+ }
+
+ Q_memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length);
+ sock->receiveMessageLength += length;
+ continue;
+ }
+ }
+
+ if (sock->sendNext)
+ SendMessageNext (sock);
+
+ return ret;
+}
+
+
+void PrintStats(qsocket_t *s)
+{
+ Con_Printf("canSend = %4u \n", s->canSend);
+ Con_Printf("sendSeq = %4u ", s->sendSequence);
+ Con_Printf("recvSeq = %4u \n", s->receiveSequence);
+ Con_Printf("\n");
+}
+
+void NET_Stats_f (void)
+{
+ qsocket_t *s;
+
+ if (Cmd_Argc () == 1)
+ {
+ Con_Printf("unreliable messages sent = %i\n", unreliableMessagesSent);
+ Con_Printf("unreliable messages recv = %i\n", unreliableMessagesReceived);
+ Con_Printf("reliable messages sent = %i\n", messagesSent);
+ Con_Printf("reliable messages received = %i\n", messagesReceived);
+ Con_Printf("packetsSent = %i\n", packetsSent);
+ Con_Printf("packetsReSent = %i\n", packetsReSent);
+ Con_Printf("packetsReceived = %i\n", packetsReceived);
+ Con_Printf("receivedDuplicateCount = %i\n", receivedDuplicateCount);
+ Con_Printf("shortPacketCount = %i\n", shortPacketCount);
+ Con_Printf("droppedDatagrams = %i\n", droppedDatagrams);
+ }
+ else if (Q_strcmp(Cmd_Argv(1), "*") == 0)
+ {
+ for (s = net_activeSockets; s; s = s->next)
+ PrintStats(s);
+ for (s = net_freeSockets; s; s = s->next)
+ PrintStats(s);
+ }
+ else
+ {
+ for (s = net_activeSockets; s; s = s->next)
+ if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0)
+ break;
+ if (s == NULL)
+ for (s = net_freeSockets; s; s = s->next)
+ if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0)
+ break;
+ if (s == NULL)
+ return;
+ PrintStats(s);
+ }
+}
+
+
+static qboolean testInProgress = false;
+static int testPollCount;
+static int testDriver;
+static int testSocket;
+
+static void Test_Poll(void);
+PollProcedure testPollProcedure = {NULL, 0.0, Test_Poll};
+
+static void Test_Poll(void)
+{
+ struct qsockaddr clientaddr;
+ int control;
+ int len;
+ char name[32];
+ char address[64];
+ int points;
+ int connectTime;
+ byte playerNumber;
+
+ net_landriverlevel = testDriver;
+
+ while (1)
+ {
+ len = dfunc.Read (testSocket, net_message.data, net_message.maxsize, &clientaddr);
+ if (len < sizeof(int))
+ break;
+
+ net_message.cursize = len;
+
+ MSG_BeginReading ();
+ control = BigLong(*((int *)net_message.data));
+ MSG_ReadLong();
+ if (control == -1)
+ break;
+ if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
+ break;
+ if ((control & NETFLAG_LENGTH_MASK) != len)
+ break;
+
+ if (MSG_ReadByte() != CCREP_PLAYER_INFO)
+ Sys_Error("Unexpected repsonse to Player Info request\n");
+
+ playerNumber = MSG_ReadByte();
+ Q_strcpy(name, MSG_ReadString());
+ points = MSG_ReadLong();
+ connectTime = MSG_ReadLong();
+ Q_strcpy(address, MSG_ReadString());
+
+ }
+
+ testPollCount--;
+ if (testPollCount)
+ {
+ SchedulePollProcedure(&testPollProcedure, 0.1);
+ }
+ else
+ {
+ dfunc.CloseSocket(testSocket);
+ testInProgress = false;
+ }
+}
+
+static void Test_f (void)
+{
+ char *host;
+ int n;
+ int max = MAX_SCOREBOARD;
+ struct qsockaddr sendaddr;
+
+ if (testInProgress)
+ return;
+
+ host = Cmd_Argv (1);
+
+ if (host && hostCacheCount)
+ {
+ for (n = 0; n < hostCacheCount; n++)
+ if (Q_strcasecmp (host, hostcache[n].name) == 0)
+ {
+ if (hostcache[n].driver != myDriverLevel)
+ continue;
+ net_landriverlevel = hostcache[n].ldriver;
+ max = hostcache[n].maxusers;
+ Q_memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr));
+ break;
+ }
+ if (n < hostCacheCount)
+ goto JustDoIt;
+ }
+
+ for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+ {
+ if (!net_landrivers[net_landriverlevel].initialized)
+ continue;
+
+ // see if we can resolve the host name
+ if (dfunc.GetAddrFromName(host, &sendaddr) != -1)
+ break;
+ }
+ if (net_landriverlevel == net_numlandrivers)
+ return;
+
+JustDoIt:
+ testSocket = dfunc.OpenSocket(0);
+ if (testSocket == -1)
+ return;
+
+ testInProgress = true;
+ testPollCount = 20;
+ testDriver = net_landriverlevel;
+
+ for (n = 0; n < max; n++)
+ {
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO);
+ MSG_WriteByte(&net_message, n);
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Write (testSocket, net_message.data, net_message.cursize, &sendaddr);
+ }
+ SZ_Clear(&net_message);
+ SchedulePollProcedure(&testPollProcedure, 0.1);
+}
+
+
+static qboolean test2InProgress = false;
+static int test2Driver;
+static int test2Socket;
+
+static void Test2_Poll(void);
+PollProcedure test2PollProcedure = {NULL, 0.0, Test2_Poll};
+
+static void Test2_Poll(void)
+{
+ struct qsockaddr clientaddr;
+ int control;
+ int len;
+ char name[256];
+ char value[256];
+
+ net_landriverlevel = test2Driver;
+ name[0] = 0;
+
+ len = dfunc.Read (test2Socket, net_message.data, net_message.maxsize, &clientaddr);
+ if (len < sizeof(int))
+ goto Reschedule;
+
+ net_message.cursize = len;
+
+ MSG_BeginReading ();
+ control = BigLong(*((int *)net_message.data));
+ MSG_ReadLong();
+ if (control == -1)
+ goto Error;
+ if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
+ goto Error;
+ if ((control & NETFLAG_LENGTH_MASK) != len)
+ goto Error;
+
+ if (MSG_ReadByte() != CCREP_RULE_INFO)
+ goto Error;
+
+ Q_strcpy(name, MSG_ReadString());
+ if (name[0] == 0)
+ goto Done;
+ Q_strcpy(value, MSG_ReadString());
+
+ Con_Printf("%-16.16s %-16.16s\n", name, value);
+
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREQ_RULE_INFO);
+ MSG_WriteString(&net_message, name);
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Write (test2Socket, net_message.data, net_message.cursize, &clientaddr);
+ SZ_Clear(&net_message);
+
+Reschedule:
+ SchedulePollProcedure(&test2PollProcedure, 0.05);
+ return;
+
+Error:
+ Con_Printf("Unexpected repsonse to Rule Info request\n");
+Done:
+ dfunc.CloseSocket(test2Socket);
+ test2InProgress = false;
+ return;
+}
+
+static void Test2_f (void)
+{
+ char *host;
+ int n;
+ struct qsockaddr sendaddr;
+
+ if (test2InProgress)
+ return;
+
+ host = Cmd_Argv (1);
+
+ if (host && hostCacheCount)
+ {
+ for (n = 0; n < hostCacheCount; n++)
+ if (Q_strcasecmp (host, hostcache[n].name) == 0)
+ {
+ if (hostcache[n].driver != myDriverLevel)
+ continue;
+ net_landriverlevel = hostcache[n].ldriver;
+ Q_memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr));
+ break;
+ }
+ if (n < hostCacheCount)
+ goto JustDoIt;
+ }
+
+ for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+ {
+ if (!net_landrivers[net_landriverlevel].initialized)
+ continue;
+
+ // see if we can resolve the host name
+ if (dfunc.GetAddrFromName(host, &sendaddr) != -1)
+ break;
+ }
+ if (net_landriverlevel == net_numlandrivers)
+ return;
+
+JustDoIt:
+ test2Socket = dfunc.OpenSocket(0);
+ if (test2Socket == -1)
+ return;
+
+ test2InProgress = true;
+ test2Driver = net_landriverlevel;
+
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREQ_RULE_INFO);
+ MSG_WriteString(&net_message, "");
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Write (test2Socket, net_message.data, net_message.cursize, &sendaddr);
+ SZ_Clear(&net_message);
+ SchedulePollProcedure(&test2PollProcedure, 0.05);
+}
+
+
+int Datagram_Init ()
+{
+ int i;
+ int csock;
+
+ myDriverLevel = net_driverlevel;
+ if(!host_initialized)
+ Cmd_AddCommand ("net_stats", NET_Stats_f);
+
+ if (COM_CheckParm("-nolan"))
+ return -1;
+ //0 for Infrastructure
+ //1 for adhoc for net_driver_to_use
+ i = net_driver_to_use;//for (i = 0; i < net_numlandrivers; i++)
+ {
+ csock = net_landrivers[i].Init ();
+ if (csock == -1)
+ {
+ return 0;
+ } //continue;
+
+ net_landrivers[i].initialized = true;
+ net_landrivers[i].controlSock = csock;
+ }
+
+ if(!host_initialized)
+ {
+ Cmd_AddCommand ("test", Test_f);
+ Cmd_AddCommand ("test2", Test2_f);
+ }
+
+ return 0;
+}
+
+
+void Datagram_Shutdown (void)
+{
+ int i;
+
+//
+// shutdown the lan drivers
+//
+ for (i = 0; i < net_numlandrivers; i++)
+ {
+ if (net_landrivers[i].initialized)
+ {
+ net_landrivers[i].Shutdown ();
+ net_landrivers[i].initialized = false;
+ }
+ }
+}
+
+
+void Datagram_Close (qsocket_t *sock)
+{
+ sfunc.CloseSocket(sock->socket);
+}
+
+
+void Datagram_Listen (qboolean state)
+{
+ int i;
+
+ for (i = 0; i < net_numlandrivers; i++)
+ if (net_landrivers[i].initialized)
+ net_landrivers[i].Listen (state);
+}
+
+
+static qsocket_t *_Datagram_CheckNewConnections (void)
+{
+ struct qsockaddr clientaddr;
+ struct qsockaddr newaddr;
+ int newsock;
+ int acceptsock;
+ qsocket_t *sock;
+ qsocket_t *s;
+ int len;
+ int command;
+ int control;
+ int ret;
+
+ acceptsock = dfunc.CheckNewConnections();
+ if (acceptsock == -1)
+ return NULL;
+
+ SZ_Clear(&net_message);
+
+ len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr);
+ if (len < sizeof(int))
+ return NULL;
+ net_message.cursize = len;
+
+ MSG_BeginReading ();
+ control = BigLong(*((int *)net_message.data));
+ MSG_ReadLong();
+ if (control == -1)
+ return NULL;
+ if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
+ return NULL;
+ if ((control & NETFLAG_LENGTH_MASK) != len)
+ return NULL;
+
+ command = MSG_ReadByte();
+ if (command == CCREQ_SERVER_INFO)
+ {
+ if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
+ return NULL;
+
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
+ dfunc.GetSocketAddr(acceptsock, &newaddr);
+ MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr));
+ MSG_WriteString(&net_message, hostname.string);
+ MSG_WriteString(&net_message, sv.name);
+ MSG_WriteByte(&net_message, net_activeconnections);
+ MSG_WriteByte(&net_message, svs.maxclients);
+ MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+ SZ_Clear(&net_message);
+ return NULL;
+ }
+
+ if (command == CCREQ_PLAYER_INFO)
+ {
+ int playerNumber;
+ int activeNumber;
+ int clientNumber;
+ client_t *client;
+
+ playerNumber = MSG_ReadByte();
+ activeNumber = -1;
+ for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
+ {
+ if (client->active)
+ {
+ activeNumber++;
+ if (activeNumber == playerNumber)
+ break;
+ }
+ }
+ if (clientNumber == svs.maxclients)
+ return NULL;
+
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
+ MSG_WriteByte(&net_message, playerNumber);
+ MSG_WriteString(&net_message, client->name);
+ MSG_WriteLong(&net_message, (int)client->edict->v.points);
+ MSG_WriteLong(&net_message, (int)(net_time - client->netconnection->connecttime));
+ MSG_WriteString(&net_message, client->netconnection->address);
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+ SZ_Clear(&net_message);
+
+ return NULL;
+ }
+
+ if (command == CCREQ_RULE_INFO)
+ {
+ char *prevCvarName;
+ cvar_t *var;
+
+ // find the search start location
+ prevCvarName = MSG_ReadString();
+ if (*prevCvarName)
+ {
+ var = Cvar_FindVar (prevCvarName);
+ if (!var)
+ return NULL;
+ var = var->next;
+ }
+ else
+ var = cvar_vars;
+
+ // search for the next server cvar
+ while (var)
+ {
+ if (var->server)
+ break;
+ var = var->next;
+ }
+
+ // send the response
+
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_RULE_INFO);
+ if (var)
+ {
+ MSG_WriteString(&net_message, var->name);
+ MSG_WriteString(&net_message, var->string);
+ }
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+ SZ_Clear(&net_message);
+
+ return NULL;
+ }
+
+ if (command != CCREQ_CONNECT)
+ return NULL;
+
+ if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
+ return NULL;
+
+ if (MSG_ReadByte() != NET_PROTOCOL_VERSION)
+ {
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_REJECT);
+ MSG_WriteString(&net_message, "Incompatible version.\n");
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+ SZ_Clear(&net_message);
+ return NULL;
+ }
+
+ // see if this guy is already connected
+ for (s = net_activeSockets; s; s = s->next)
+ {
+ if (s->driver != net_driverlevel)
+ continue;
+ ret = dfunc.AddrCompare(&clientaddr, &s->addr);
+ if (ret >= 0)
+ {
+ // is this a duplicate connection reqeust?
+ if (ret == 0 && net_time - s->connecttime < 2.0)
+ {
+ // yes, so send a duplicate reply
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_ACCEPT);
+ dfunc.GetSocketAddr(s->socket, &newaddr);
+ MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr));
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+ SZ_Clear(&net_message);
+ return NULL;
+ }
+ // it's somebody coming back in from a crash/disconnect
+ // so close the old qsocket and let their retry get them back in
+ NET_Close(s);
+ return NULL;
+ }
+ }
+
+ // allocate a QSocket
+ sock = NET_NewQSocket ();
+ if (sock == NULL)
+ {
+ // no room; try to let him know
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_REJECT);
+ MSG_WriteString(&net_message, "Server is full.\n");
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+ SZ_Clear(&net_message);
+ return NULL;
+ }
+
+ // allocate a network socket
+ newsock = dfunc.OpenSocket(0);
+ if (newsock == -1)
+ {
+ NET_FreeQSocket(sock);
+ return NULL;
+ }
+
+ // connect to the client
+ if (dfunc.Connect (newsock, &clientaddr) == -1)
+ {
+ dfunc.CloseSocket(newsock);
+ NET_FreeQSocket(sock);
+ return NULL;
+ }
+
+ // everything is allocated, just fill in the details
+ sock->socket = newsock;
+ sock->landriver = net_landriverlevel;
+ sock->addr = clientaddr;
+ Q_strcpy(sock->address, dfunc.AddrToString(&clientaddr));
+
+ // send him back the info about the server connection he has been allocated
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_ACCEPT);
+ dfunc.GetSocketAddr(newsock, &newaddr);
+ MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr));
+// MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr));
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+ SZ_Clear(&net_message);
+
+ return sock;
+}
+
+qsocket_t *Datagram_CheckNewConnections (void)
+{
+ qsocket_t *ret = NULL;
+
+ for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+ if (net_landrivers[net_landriverlevel].initialized)
+ if ((ret = _Datagram_CheckNewConnections ()) != NULL)
+ break;
+ return ret;
+}
+
+
+static void _Datagram_SearchForHosts (qboolean xmit)
+{
+ int ret;
+ int n;
+ int i;
+ struct qsockaddr readaddr;
+ struct qsockaddr myaddr;
+ int control;
+
+ dfunc.GetSocketAddr (dfunc.controlSock, &myaddr);
+ if (xmit)
+ {
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
+ MSG_WriteString(&net_message, "QUAKE");
+ MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize);
+ SZ_Clear(&net_message);
+ }
+
+ while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0)
+ {
+ if (ret < sizeof(int))
+ continue;
+ net_message.cursize = ret;
+
+ // don't answer our own query
+ if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0)
+ continue;
+
+ // is the cache full?
+ if (hostCacheCount == HOSTCACHESIZE)
+ continue;
+
+ MSG_BeginReading ();
+ control = BigLong(*((int *)net_message.data));
+ MSG_ReadLong();
+ if (control == -1)
+ continue;
+ if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
+ continue;
+ if ((control & NETFLAG_LENGTH_MASK) != ret)
+ continue;
+
+ if (MSG_ReadByte() != CCREP_SERVER_INFO)
+ continue;
+
+ dfunc.GetAddrFromName(MSG_ReadString(), &readaddr);
+ // search the cache for this server
+ for (n = 0; n < hostCacheCount; n++)
+ if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0)
+ break;
+
+ // is it already there?
+ if (n < hostCacheCount)
+ continue;
+
+ // add it
+ hostCacheCount++;
+ Q_strcpy(hostcache[n].name, MSG_ReadString());
+ Q_strcpy(hostcache[n].map, MSG_ReadString());
+ hostcache[n].users = MSG_ReadByte();
+ hostcache[n].maxusers = MSG_ReadByte();
+ if (MSG_ReadByte() != NET_PROTOCOL_VERSION)
+ {
+ Q_strcpy(hostcache[n].cname, hostcache[n].name);
+ hostcache[n].cname[14] = 0;
+ Q_strcpy(hostcache[n].name, "*");
+ Q_strcat(hostcache[n].name, hostcache[n].cname);
+ }
+ Q_memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr));
+ hostcache[n].driver = net_driverlevel;
+ hostcache[n].ldriver = net_landriverlevel;
+ Q_strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr));
+
+ // check for a name conflict
+ for (i = 0; i < hostCacheCount; i++)
+ {
+ if (i == n)
+ continue;
+ if (Q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0)
+ {
+ i = Q_strlen(hostcache[n].name);
+ if (i < 15 && hostcache[n].name[i-1] > '8')
+ {
+ hostcache[n].name[i] = '0';
+ hostcache[n].name[i+1] = 0;
+ }
+ else
+ hostcache[n].name[i-1]++;
+ i = -1;
+ }
+ }
+ }
+}
+
+void Datagram_SearchForHosts (qboolean xmit)
+{
+ for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+ {
+ if (hostCacheCount == HOSTCACHESIZE)
+ break;
+ if (net_landrivers[net_landriverlevel].initialized)
+ _Datagram_SearchForHosts (xmit);
+ }
+}
+
+
+static qsocket_t *_Datagram_Connect (char *host)
+{
+ struct qsockaddr sendaddr;
+ struct qsockaddr readaddr;
+ qsocket_t *sock;
+ int newsock;
+ int ret;
+ int reps;
+ double start_time;
+ int control;
+ char *reason;
+
+ // see if we can resolve the host name
+ if (dfunc.GetAddrFromName(host, &sendaddr) == -1)
+ return NULL;
+
+ newsock = dfunc.OpenSocket (0);
+ if (newsock == -1)
+ return NULL;
+
+ sock = NET_NewQSocket ();
+ if (sock == NULL)
+ goto ErrorReturn2;
+ sock->socket = newsock;
+ sock->landriver = net_landriverlevel;
+
+ // connect to the host
+ if (dfunc.Connect (newsock, &sendaddr) == -1)
+ goto ErrorReturn;
+
+ // send the connection request
+ Con_Printf("trying...\n"); SCR_UpdateScreen ();
+ start_time = net_time;
+
+ for (reps = 0; reps < 3; reps++)
+ {
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREQ_CONNECT);
+ MSG_WriteString(&net_message, "QUAKE");
+ MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr);
+ SZ_Clear(&net_message);
+ do
+ {
+ ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr);
+ // if we got something, validate it
+ if (ret > 0)
+ {
+ // is it from the right place?
+ if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0)
+ {
+#ifdef DEBUG
+ Con_Printf("wrong reply address\n");
+ Con_Printf("Expected: %s\n", StrAddr (&sendaddr));
+ Con_Printf("Received: %s\n", StrAddr (&readaddr));
+ SCR_UpdateScreen ();
+#endif
+ ret = 0;
+ continue;
+ }
+
+ if (ret < sizeof(int))
+ {
+ ret = 0;
+ continue;
+ }
+
+ net_message.cursize = ret;
+ MSG_BeginReading ();
+
+ control = BigLong(*((int *)net_message.data));
+ MSG_ReadLong();
+ if (control == -1)
+ {
+ ret = 0;
+ continue;
+ }
+ if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
+ {
+ ret = 0;
+ continue;
+ }
+ if ((control & NETFLAG_LENGTH_MASK) != ret)
+ {
+ ret = 0;
+ continue;
+ }
+ }
+ sceKernelDelayThread(10);
+ }
+ while (ret == 0 && (SetNetTime() - start_time) < 5.5);
+ if (ret)
+ break;
+ Con_Printf("still trying...\n"); SCR_UpdateScreen ();
+ start_time = SetNetTime();
+ }
+
+ if (ret == 0)
+ {
+ reason = "No Response";
+ Con_Printf("%s\n", reason);
+ Q_strcpy(m_return_reason, reason);
+ goto ErrorReturn;
+ }
+
+ if (ret == -1)
+ {
+ reason = "Network Error";
+ Con_Printf("%s\n", reason);
+ Q_strcpy(m_return_reason, reason);
+ goto ErrorReturn;
+ }
+
+ ret = MSG_ReadByte();
+ if (ret == CCREP_REJECT)
+ {
+ reason = MSG_ReadString();
+ Con_Printf(reason);
+ Q_strncpy(m_return_reason, reason, 31);
+ goto ErrorReturn;
+ }
+
+ if (ret == CCREP_ACCEPT)
+ {
+ Q_memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr));
+ dfunc.SetSocketPort (&sock->addr, MSG_ReadLong());
+ }
+ else
+ {
+ reason = "Bad Response";
+ Con_Printf("%s\n", reason);
+ Q_strcpy(m_return_reason, reason);
+ goto ErrorReturn;
+ }
+
+ dfunc.GetNameFromAddr (&sendaddr, sock->address);
+
+ Con_Printf ("Connection accepted\n");
+ sock->lastMessageTime = SetNetTime();
+
+ // switch the connection to the specified address
+ if (dfunc.Connect (newsock, &sock->addr) == -1)
+ {
+ reason = "Connect to Game failed";
+ Con_Printf("%s\n", reason);
+ Q_strcpy(m_return_reason, reason);
+ goto ErrorReturn;
+ }
+
+ m_return_onerror = false;
+ return sock;
+
+ErrorReturn:
+ NET_FreeQSocket(sock);
+ErrorReturn2:
+ dfunc.CloseSocket(newsock);
+ if (m_return_onerror)
+ {
+ key_dest = key_menu;
+ m_state = m_return_state;
+ m_return_onerror = false;
+ }
+ return NULL;
+}
+
+qsocket_t *Datagram_Connect (char *host)
+{
+ qsocket_t *ret = NULL;
+
+ for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+ if (net_landrivers[net_landriverlevel].initialized)
+ if ((ret = _Datagram_Connect (host)) != NULL)
+ break;
+ return ret;
+}
diff --git a/source/net_dgrm.h b/source/net_dgrm.h
new file mode 100644
index 0000000..4c1745a
--- /dev/null
+++ b/source/net_dgrm.h
@@ -0,0 +1,35 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// net_dgrm.h
+
+int net_driver_to_use;
+
+int Datagram_Init (void);
+void Datagram_Listen (qboolean state);
+void Datagram_SearchForHosts (qboolean xmit);
+qsocket_t *Datagram_Connect (char *host);
+qsocket_t *Datagram_CheckNewConnections (void);
+int Datagram_GetMessage (qsocket_t *sock);
+int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data);
+int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data);
+qboolean Datagram_CanSendMessage (qsocket_t *sock);
+qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock);
+void Datagram_Close (qsocket_t *sock);
+void Datagram_Shutdown (void);
diff --git a/source/net_loop.c b/source/net_loop.c
new file mode 100644
index 0000000..35aa370
--- /dev/null
+++ b/source/net_loop.c
@@ -0,0 +1,245 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// net_loop.c
+
+#include "quakedef.h"
+#include "net_loop.h"
+
+qboolean localconnectpending = false;
+qsocket_t *loop_client = NULL;
+qsocket_t *loop_server = NULL;
+
+int Loop_Init (void)
+{
+ if (cls.state == ca_dedicated)
+ return -1;
+ return 0;
+}
+
+
+void Loop_Shutdown (void)
+{
+}
+
+
+void Loop_Listen (qboolean state)
+{
+}
+
+
+void Loop_SearchForHosts (qboolean xmit)
+{
+ if (!sv.active)
+ return;
+
+ hostCacheCount = 1;
+ if (Q_strcmp(hostname.string, "UNNAMED") == 0)
+ Q_strcpy(hostcache[0].name, "local");
+ else
+ Q_strcpy(hostcache[0].name, hostname.string);
+ Q_strcpy(hostcache[0].map, sv.name);
+ hostcache[0].users = net_activeconnections;
+ hostcache[0].maxusers = svs.maxclients;
+ hostcache[0].driver = net_driverlevel;
+ Q_strcpy(hostcache[0].cname, "local");
+}
+
+
+qsocket_t *Loop_Connect (char *host)
+{
+ if (Q_strcmp(host,"local") != 0)
+ return NULL;
+
+ localconnectpending = true;
+
+ if (!loop_client)
+ {
+ if ((loop_client = NET_NewQSocket ()) == NULL)
+ {
+ Con_Printf("Loop_Connect: no qsocket available\n");
+ return NULL;
+ }
+ Q_strcpy (loop_client->address, "localhost");
+ }
+ loop_client->receiveMessageLength = 0;
+ loop_client->sendMessageLength = 0;
+ loop_client->canSend = true;
+
+ if (!loop_server)
+ {
+ if ((loop_server = NET_NewQSocket ()) == NULL)
+ {
+ Con_Printf("Loop_Connect: no qsocket available\n");
+ return NULL;
+ }
+ Q_strcpy (loop_server->address, "LOCAL");
+ }
+ loop_server->receiveMessageLength = 0;
+ loop_server->sendMessageLength = 0;
+ loop_server->canSend = true;
+
+ loop_client->driverdata = (void *)loop_server;
+ loop_server->driverdata = (void *)loop_client;
+
+ return loop_client;
+}
+
+
+qsocket_t *Loop_CheckNewConnections (void)
+{
+ if (!localconnectpending)
+ return NULL;
+
+ localconnectpending = false;
+ loop_server->sendMessageLength = 0;
+ loop_server->receiveMessageLength = 0;
+ loop_server->canSend = true;
+ loop_client->sendMessageLength = 0;
+ loop_client->receiveMessageLength = 0;
+ loop_client->canSend = true;
+ return loop_server;
+}
+
+
+static int IntAlign(int value)
+{
+ return (value + (sizeof(int) - 1)) & (~(sizeof(int) - 1));
+}
+
+
+int Loop_GetMessage (qsocket_t *sock)
+{
+ int ret;
+ int length;
+
+ if (sock->receiveMessageLength == 0)
+ return 0;
+
+ ret = sock->receiveMessage[0];
+ length = sock->receiveMessage[1] + (sock->receiveMessage[2] << 8);
+ // alignment byte skipped here
+ SZ_Clear (&net_message);
+ SZ_Write (&net_message, &sock->receiveMessage[4], length);
+
+ length = IntAlign(length + 4);
+ sock->receiveMessageLength -= length;
+
+ if (sock->receiveMessageLength)
+ Q_memcpy(sock->receiveMessage, &sock->receiveMessage[length], sock->receiveMessageLength);
+
+ if (sock->driverdata && ret == 1)
+ ((qsocket_t *)sock->driverdata)->canSend = true;
+
+ return ret;
+}
+
+
+int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+ byte *buffer;
+ int *bufferLength;
+
+ if (!sock->driverdata)
+ return -1;
+
+ bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength;
+
+ if ((*bufferLength + data->cursize + 4) > NET_MAXMESSAGE)
+ Sys_Error("Loop_SendMessage: overflow\n");
+
+ buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength;
+
+ // message type
+ *buffer++ = 1;
+
+ // length
+ *buffer++ = data->cursize & 0xff;
+ *buffer++ = data->cursize >> 8;
+
+ // align
+ buffer++;
+
+ // message
+ Q_memcpy(buffer, data->data, data->cursize);
+ *bufferLength = IntAlign(*bufferLength + data->cursize + 4);
+
+ sock->canSend = false;
+ return 1;
+}
+
+
+int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
+{
+ byte *buffer;
+ int *bufferLength;
+
+ if (!sock->driverdata)
+ return -1;
+
+ bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength;
+
+ if ((*bufferLength + data->cursize + sizeof(byte) + sizeof(short)) > NET_MAXMESSAGE)
+ return 0;
+
+ buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength;
+
+ // message type
+ *buffer++ = 2;
+
+ // length
+ *buffer++ = data->cursize & 0xff;
+ *buffer++ = data->cursize >> 8;
+
+ // align
+ buffer++;
+
+ // message
+ Q_memcpy(buffer, data->data, data->cursize);
+ *bufferLength = IntAlign(*bufferLength + data->cursize + 4);
+ return 1;
+}
+
+
+qboolean Loop_CanSendMessage (qsocket_t *sock)
+{
+ if (!sock->driverdata)
+ return false;
+ return sock->canSend;
+}
+
+
+qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock)
+{
+ return true;
+}
+
+
+void Loop_Close (qsocket_t *sock)
+{
+ if (sock->driverdata)
+ ((qsocket_t *)sock->driverdata)->driverdata = NULL;
+ sock->receiveMessageLength = 0;
+ sock->sendMessageLength = 0;
+ sock->canSend = true;
+ if (sock == loop_client)
+ loop_client = NULL;
+ else
+ loop_server = NULL;
+}
diff --git a/source/net_loop.h b/source/net_loop.h
new file mode 100644
index 0000000..90cdb2c
--- /dev/null
+++ b/source/net_loop.h
@@ -0,0 +1,33 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// net_loop.h
+
+int Loop_Init (void);
+void Loop_Listen (qboolean state);
+void Loop_SearchForHosts (qboolean xmit);
+qsocket_t *Loop_Connect (char *host);
+qsocket_t *Loop_CheckNewConnections (void);
+int Loop_GetMessage (qsocket_t *sock);
+int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data);
+int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data);
+qboolean Loop_CanSendMessage (qsocket_t *sock);
+qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock);
+void Loop_Close (qsocket_t *sock);
+void Loop_Shutdown (void);
diff --git a/source/net_main.c b/source/net_main.c
new file mode 100644
index 0000000..4ee0a62
--- /dev/null
+++ b/source/net_main.c
@@ -0,0 +1,987 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// net_main.c
+
+#include "quakedef.h"
+#include "net_vcr.h"
+
+qsocket_t *net_activeSockets = NULL;
+qsocket_t *net_freeSockets = NULL;
+int net_numsockets = 0;
+
+qboolean serialAvailable = false;
+qboolean ipxAvailable = false;
+qboolean tcpipAvailable = false;
+qboolean tcpipAdhoc = false;
+
+int net_hostport;
+int DEFAULTnet_hostport = 26000;
+
+char my_ipx_address[NET_NAMELEN];
+char my_tcpip_address[NET_NAMELEN];
+
+void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem);
+void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem);
+void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+
+static qboolean listening = false;
+
+qboolean slistInProgress = false;
+qboolean slistSilent = false;
+qboolean slistLocal = true;
+static double slistStartTime;
+static int slistLastShown;
+
+static void Slist_Send(void);
+static void Slist_Poll(void);
+PollProcedure slistSendProcedure = {NULL, 0.0, Slist_Send};
+PollProcedure slistPollProcedure = {NULL, 0.0, Slist_Poll};
+
+
+sizebuf_t net_message;
+int net_activeconnections = 0;
+
+int messagesSent = 0;
+int messagesReceived = 0;
+int unreliableMessagesSent = 0;
+int unreliableMessagesReceived = 0;
+
+cvar_t net_messagetimeout = {"net_messagetimeout","300"};
+cvar_t hostname = {"hostname", "Unnamed Server"};
+
+qboolean configRestored = false;
+cvar_t config_com_port = {"_config_com_port", "0x3f8", true};
+cvar_t config_com_irq = {"_config_com_irq", "4", true};
+cvar_t config_com_baud = {"_config_com_baud", "57600", true};
+cvar_t config_com_modem = {"_config_com_modem", "1", true};
+cvar_t config_modem_dialtype = {"_config_modem_dialtype", "T", true};
+cvar_t config_modem_clear = {"_config_modem_clear", "ATZ", true};
+cvar_t config_modem_init = {"_config_modem_init", "", true};
+cvar_t config_modem_hangup = {"_config_modem_hangup", "AT H", true};
+
+int vcrFile = -1;
+qboolean recording = false;
+
+// these two macros are to make the code more readable
+#define sfunc net_drivers[sock->driver]
+#define dfunc net_drivers[net_driverlevel]
+
+int net_driverlevel;
+
+
+double net_time;
+
+double SetNetTime(void)
+{
+ net_time = Sys_FloatTime();
+ return net_time;
+}
+
+
+/*
+===================
+NET_NewQSocket
+
+Called by drivers when a new communications endpoint is required
+The sequence and buffer fields will be filled in properly
+===================
+*/
+qsocket_t *NET_NewQSocket (void)
+{
+ qsocket_t *sock;
+
+ if (net_freeSockets == NULL)
+ return NULL;
+
+ if (net_activeconnections >= svs.maxclients)
+ return NULL;
+
+ // get one from free list
+ sock = net_freeSockets;
+ net_freeSockets = sock->next;
+
+ // add it to active list
+ sock->next = net_activeSockets;
+ net_activeSockets = sock;
+
+ sock->disconnected = false;
+ sock->connecttime = net_time;
+ Q_strcpy (sock->address,"UNSET ADDRESS");
+ sock->driver = net_driverlevel;
+ sock->socket = 0;
+ sock->driverdata = NULL;
+ sock->canSend = true;
+ sock->sendNext = false;
+ sock->lastMessageTime = net_time;
+ sock->ackSequence = 0;
+ sock->sendSequence = 0;
+ sock->unreliableSendSequence = 0;
+ sock->sendMessageLength = 0;
+ sock->receiveSequence = 0;
+ sock->unreliableReceiveSequence = 0;
+ sock->receiveMessageLength = 0;
+
+ return sock;
+}
+
+
+void NET_FreeQSocket(qsocket_t *sock)
+{
+ qsocket_t *s;
+
+ // remove it from active list
+ if (sock == net_activeSockets)
+ net_activeSockets = net_activeSockets->next;
+ else
+ {
+ for (s = net_activeSockets; s; s = s->next)
+ if (s->next == sock)
+ {
+ s->next = sock->next;
+ break;
+ }
+ if (!s)
+ Sys_Error ("NET_FreeQSocket: not active\n");
+ }
+
+ // add it to free list
+ sock->next = net_freeSockets;
+ net_freeSockets = sock;
+ sock->disconnected = true;
+}
+
+
+static void NET_Listen_f (void)
+{
+ if (Cmd_Argc () != 2)
+ {
+ Con_Printf ("\"listen\" is \"%u\"\n", listening ? 1 : 0);
+ return;
+ }
+
+ listening = Q_atoi(Cmd_Argv(1)) ? true : false;
+
+ for (net_driverlevel=0 ; net_driverlevel svs.maxclientslimit)
+ {
+ n = svs.maxclientslimit;
+ Con_Printf ("\"maxplayers\" set to \"%u\"\n", n);
+ }
+
+ if ((n == 1) && listening)
+ Cbuf_AddText ("listen 0\n");
+
+ if ((n > 1) && (!listening))
+ Cbuf_AddText ("listen 1\n");
+
+ svs.maxclients = n;
+ if (n == 1)
+ {
+ Cvar_Set ("deathmatch", "0");
+ Cvar_Set ("coop", "0");
+ }
+ else
+ {
+ if (coop.value)
+ Cvar_Set ("deathmatch", "0");
+ else
+ Cvar_Set ("deathmatch", "1");
+ }
+}
+
+
+static void NET_Port_f (void)
+{
+ int n;
+
+ if (Cmd_Argc () != 2)
+ {
+ Con_Printf ("\"port\" is \"%u\"\n", net_hostport);
+ return;
+ }
+
+ n = Q_atoi(Cmd_Argv(1));
+ if (n < 1 || n > 65534)
+ {
+ Con_Printf ("Bad value, must be between 1 and 65534\n");
+ return;
+ }
+
+ DEFAULTnet_hostport = n;
+ net_hostport = n;
+
+ if (listening)
+ {
+ // force a change to the new port
+ Cbuf_AddText ("listen 0\n");
+ Cbuf_AddText ("listen 1\n");
+ }
+}
+
+
+static void PrintSlistHeader(void)
+{
+ Con_Printf("Server Map Users\n");
+ Con_Printf("--------------- --------------- -----\n");
+ slistLastShown = 0;
+}
+
+
+static void PrintSlist(void)
+{
+ int n;
+
+ for (n = slistLastShown; n < hostCacheCount; n++)
+ {
+ if (hostcache[n].maxusers)
+ Con_Printf("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
+ else
+ Con_Printf("%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
+ }
+ slistLastShown = n;
+}
+
+
+static void PrintSlistTrailer(void)
+{
+ if (hostCacheCount)
+ Con_Printf("== end list ==\n\n");
+ else
+ Con_Printf("No Quake servers found.\n\n");
+}
+
+
+void NET_Slist_f (void)
+{
+ if (slistInProgress)
+ return;
+
+ if (! slistSilent)
+ {
+ Con_Printf("Looking for Quake servers...\n");
+ PrintSlistHeader();
+ }
+
+ slistInProgress = true;
+ slistStartTime = Sys_FloatTime();
+
+ SchedulePollProcedure(&slistSendProcedure, 0.0);
+ SchedulePollProcedure(&slistPollProcedure, 0.1);
+
+ hostCacheCount = 0;
+}
+
+
+static void Slist_Send(void)
+{
+ for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
+ {
+ if (!slistLocal && net_driverlevel == 0)
+ continue;
+ if (net_drivers[net_driverlevel].initialized == false)
+ continue;
+ dfunc.SearchForHosts (true);
+ }
+
+ if ((Sys_FloatTime() - slistStartTime) < 0.5)
+ SchedulePollProcedure(&slistSendProcedure, 0.75);
+}
+
+
+static void Slist_Poll(void)
+{
+ for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
+ {
+ if (!slistLocal && net_driverlevel == 0)
+ continue;
+ if (net_drivers[net_driverlevel].initialized == false)
+ continue;
+ dfunc.SearchForHosts (false);
+ }
+
+ if (! slistSilent)
+ PrintSlist();
+
+ if ((Sys_FloatTime() - slistStartTime) < 1.5)
+ {
+ SchedulePollProcedure(&slistPollProcedure, 0.1);
+ return;
+ }
+
+ if (! slistSilent)
+ PrintSlistTrailer();
+ slistInProgress = false;
+ slistSilent = false;
+ slistLocal = true;
+}
+
+
+/*
+===================
+NET_Connect
+===================
+*/
+
+int hostCacheCount = 0;
+hostcache_t hostcache[HOSTCACHESIZE];
+
+qsocket_t *NET_Connect (char *host)
+{
+ qsocket_t *ret;
+ int n;
+ int numdrivers = net_numdrivers;
+
+ SetNetTime();
+
+ if (host && *host == 0)
+ host = NULL;
+
+ if (host)
+ {
+ if (Q_strcasecmp (host, "local") == 0)
+ {
+ numdrivers = 1;
+ goto JustDoIt;
+ }
+
+ if (hostCacheCount)
+ {
+ for (n = 0; n < hostCacheCount; n++)
+ if (Q_strcasecmp (host, hostcache[n].name) == 0)
+ {
+ host = hostcache[n].cname;
+ break;
+ }
+ if (n < hostCacheCount)
+ goto JustDoIt;
+ }
+ }
+
+ slistSilent = host ? true : false;
+ NET_Slist_f ();
+
+ while(slistInProgress)
+ NET_Poll();
+
+ if (host == NULL)
+ {
+ if (hostCacheCount != 1)
+ return NULL;
+ host = hostcache[0].cname;
+ Con_Printf("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host);
+ }
+
+ if (hostCacheCount)
+ for (n = 0; n < hostCacheCount; n++)
+ if (Q_strcasecmp (host, hostcache[n].name) == 0)
+ {
+ host = hostcache[n].cname;
+ break;
+ }
+
+JustDoIt:
+ for (net_driverlevel=0 ; net_driverleveladdress, NET_NAMELEN);
+ }
+ return ret;
+ }
+ }
+
+ if (recording)
+ {
+ vcrConnect.time = host_time;
+ vcrConnect.op = VCR_OP_CONNECT;
+ vcrConnect.session = 0;
+ Sys_FileWrite (vcrFile, &vcrConnect, sizeof(vcrConnect));
+ }
+
+ return NULL;
+}
+
+/*
+===================
+NET_Close
+===================
+*/
+void NET_Close (qsocket_t *sock)
+{
+ if (!sock)
+ return;
+
+ if (sock->disconnected)
+ return;
+
+ SetNetTime();
+
+ // call the driver_Close function
+ sfunc.Close (sock);
+
+ NET_FreeQSocket(sock);
+}
+
+
+/*
+=================
+NET_GetMessage
+
+If there is a complete message, return it in net_message
+
+returns 0 if no data is waiting
+returns 1 if a message was received
+returns -1 if connection is invalid
+=================
+*/
+
+struct
+{
+ double time;
+ int op;
+ long session;
+ int ret;
+ int len;
+} vcrGetMessage;
+
+extern void PrintStats(qsocket_t *s);
+
+int NET_GetMessage (qsocket_t *sock)
+{
+ int ret;
+
+ if (!sock)
+ return -1;
+
+ if (sock->disconnected)
+ {
+ Con_Printf("NET_GetMessage: disconnected socket\n");
+ return -1;
+ }
+
+ SetNetTime();
+
+ ret = sfunc.QGetMessage(sock);
+
+ // see if this connection has timed out
+ if (ret == 0 && sock->driver)
+ {
+ if (net_time - sock->lastMessageTime > net_messagetimeout.value)
+ {
+ NET_Close(sock);
+ return -1;
+ }
+ }
+
+
+ if (ret > 0)
+ {
+ if (sock->driver)
+ {
+ sock->lastMessageTime = net_time;
+ if (ret == 1)
+ messagesReceived++;
+ else if (ret == 2)
+ unreliableMessagesReceived++;
+ }
+
+ if (recording)
+ {
+ vcrGetMessage.time = host_time;
+ vcrGetMessage.op = VCR_OP_GETMESSAGE;
+ vcrGetMessage.session = (long)sock;
+ vcrGetMessage.ret = ret;
+ vcrGetMessage.len = net_message.cursize;
+ Sys_FileWrite (vcrFile, &vcrGetMessage, 24);
+ Sys_FileWrite (vcrFile, net_message.data, net_message.cursize);
+ }
+ }
+ else
+ {
+ if (recording)
+ {
+ vcrGetMessage.time = host_time;
+ vcrGetMessage.op = VCR_OP_GETMESSAGE;
+ vcrGetMessage.session = (long)sock;
+ vcrGetMessage.ret = ret;
+ Sys_FileWrite (vcrFile, &vcrGetMessage, 20);
+ }
+ }
+ return ret;
+}
+
+
+/*
+==================
+NET_SendMessage
+
+Try to send a complete length+message unit over the reliable stream.
+returns 0 if the message cannot be delivered reliably, but the connection
+ is still considered valid
+returns 1 if the message was sent properly
+returns -1 if the connection died
+==================
+*/
+struct
+{
+ double time;
+ int op;
+ long session;
+ int r;
+} vcrSendMessage;
+
+int NET_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+ int r;
+
+ if (!sock)
+ return -1;
+
+ if (sock->disconnected)
+ {
+ Con_Printf("NET_SendMessage: disconnected socket\n");
+ return -1;
+ }
+
+ SetNetTime();
+ r = sfunc.QSendMessage(sock, data);
+ if (r == 1 && sock->driver)
+ messagesSent++;
+
+ if (recording)
+ {
+ vcrSendMessage.time = host_time;
+ vcrSendMessage.op = VCR_OP_SENDMESSAGE;
+ vcrSendMessage.session = (long)sock;
+ vcrSendMessage.r = r;
+ Sys_FileWrite (vcrFile, &vcrSendMessage, 20);
+ }
+
+ return r;
+}
+
+
+int NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
+{
+ int r;
+
+ if (!sock)
+ return -1;
+
+ if (sock->disconnected)
+ {
+ Con_Printf("NET_SendMessage: disconnected socket\n");
+ return -1;
+ }
+
+ SetNetTime();
+ r = sfunc.SendUnreliableMessage(sock, data);
+ if (r == 1 && sock->driver)
+ unreliableMessagesSent++;
+
+ if (recording)
+ {
+ vcrSendMessage.time = host_time;
+ vcrSendMessage.op = VCR_OP_SENDMESSAGE;
+ vcrSendMessage.session = (long)sock;
+ vcrSendMessage.r = r;
+ Sys_FileWrite (vcrFile, &vcrSendMessage, 20);
+ }
+
+ return r;
+}
+
+
+/*
+==================
+NET_CanSendMessage
+
+Returns true or false if the given qsocket can currently accept a
+message to be transmitted.
+==================
+*/
+qboolean NET_CanSendMessage (qsocket_t *sock)
+{
+ int r;
+
+ if (!sock)
+ return false;
+
+ if (sock->disconnected)
+ return false;
+
+ SetNetTime();
+
+ r = sfunc.CanSendMessage(sock);
+
+ if (recording)
+ {
+ vcrSendMessage.time = host_time;
+ vcrSendMessage.op = VCR_OP_CANSENDMESSAGE;
+ vcrSendMessage.session = (long)sock;
+ vcrSendMessage.r = r;
+ Sys_FileWrite (vcrFile, &vcrSendMessage, 20);
+ }
+ //Con_Printf("Cansend = %i \n",r);//blubs : this is why it doesn't send the next signonreply
+ //return r; //blubs, not sure if this will fuck us up later
+ //if(r != 1)
+ // Con_Printf("Error, couldn't send msg, r:%i\n",r);
+ return r;
+}
+
+
+int NET_SendToAll(sizebuf_t *data, int blocktime)
+{
+ double start;
+ int i;
+ int count = 0;
+ qboolean state1 [MAX_SCOREBOARD];
+ qboolean state2 [MAX_SCOREBOARD];
+
+ for (i=0, host_client = svs.clients ; inetconnection)
+ continue;
+ if (host_client->active)
+ {
+ if (host_client->netconnection->driver == 0)
+ {
+ NET_SendMessage(host_client->netconnection, data);
+ state1[i] = true;
+ state2[i] = true;
+ continue;
+ }
+ count++;
+ state1[i] = false;
+ state2[i] = false;
+ }
+ else
+ {
+ state1[i] = true;
+ state2[i] = true;
+ }
+ }
+
+ start = Sys_FloatTime();
+ while (count)
+ {
+ count = 0;
+ for (i=0, host_client = svs.clients ; inetconnection))
+ {
+ state1[i] = true;
+ NET_SendMessage(host_client->netconnection, data);
+ }
+ else
+ {
+ NET_GetMessage (host_client->netconnection);
+ }
+ count++;
+ continue;
+ }
+
+ if (! state2[i])
+ {
+ if (NET_CanSendMessage (host_client->netconnection))
+ {
+ state2[i] = true;
+ }
+ else
+ {
+ NET_GetMessage (host_client->netconnection);
+ }
+ count++;
+ continue;
+ }
+ }
+ if ((Sys_FloatTime() - start) > blocktime)
+ break;
+ }
+ return count;
+}
+
+
+//=============================================================================
+
+/*
+====================
+NET_Init
+====================
+*/
+
+void NET_Init (void)
+{
+ int i;
+ int controlSocket;
+ qsocket_t *s;
+
+ if (COM_CheckParm("-playback"))
+ {
+ net_numdrivers = 1;
+ net_drivers[0].Init = VCR_Init;
+ }
+
+ if (COM_CheckParm("-record"))
+ recording = true;
+
+ i = COM_CheckParm ("-port");
+ if (!i)
+ i = COM_CheckParm ("-udpport");
+ if (!i)
+ i = COM_CheckParm ("-ipxport");
+
+ if (i)
+ {
+ if (i < com_argc-1)
+ DEFAULTnet_hostport = Q_atoi (com_argv[i+1]);
+ else
+ Sys_Error ("NET_Init: you must specify a number after -port");
+ }
+ net_hostport = DEFAULTnet_hostport;
+
+ if (COM_CheckParm("-listen") || cls.state == ca_dedicated)
+ listening = true;
+ net_numsockets = svs.maxclientslimit;
+ if (cls.state != ca_dedicated)
+ net_numsockets++;
+
+ SetNetTime();
+
+ for (i = 0; i < net_numsockets; i++)
+ {
+ s = (qsocket_t *)Hunk_AllocName(sizeof(qsocket_t), "qsocket");
+ s->next = net_freeSockets;
+ net_freeSockets = s;
+ s->disconnected = true;
+ }
+
+ // allocate space for network message buffer
+ SZ_Alloc (&net_message, NET_MAXMESSAGE);
+
+ Cvar_RegisterVariable (&net_messagetimeout);
+ Cvar_RegisterVariable (&hostname);
+ Cvar_RegisterVariable (&config_com_port);
+ Cvar_RegisterVariable (&config_com_irq);
+ Cvar_RegisterVariable (&config_com_baud);
+ Cvar_RegisterVariable (&config_com_modem);
+ Cvar_RegisterVariable (&config_modem_dialtype);
+ Cvar_RegisterVariable (&config_modem_clear);
+ Cvar_RegisterVariable (&config_modem_init);
+ Cvar_RegisterVariable (&config_modem_hangup);
+
+ if(!host_initialized)
+ {
+ Cmd_AddCommand ("slist", NET_Slist_f);
+ Cmd_AddCommand ("listen", NET_Listen_f);
+ Cmd_AddCommand ("maxplayers", MaxPlayers_f);
+ Cmd_AddCommand ("port", NET_Port_f);
+ }
+
+ // initialize all the drivers
+ for (net_driverlevel=0 ; net_driverlevelnext)
+ NET_Close(sock);
+
+//
+// shutdown the drivers
+//
+ for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
+ {
+ if (net_drivers[net_driverlevel].initialized == true)
+ {
+ net_drivers[net_driverlevel].Shutdown ();
+ net_drivers[net_driverlevel].initialized = false;
+ }
+ }
+
+ if (vcrFile != -1)
+ {
+ Con_Printf ("Closing vcrfile.\n");
+ Sys_FileClose(vcrFile);
+ }
+}
+
+
+static PollProcedure *pollProcedureList = NULL;
+
+void NET_Poll(void)
+{
+ PollProcedure *pp;
+ qboolean useModem;
+
+ if (!configRestored)
+ {
+ if (serialAvailable)
+ {
+ if (config_com_modem.value == 1.0)
+ useModem = true;
+ else
+ useModem = false;
+ SetComPortConfig (0, (int)config_com_port.value, (int)config_com_irq.value, (int)config_com_baud.value, useModem);
+ SetModemConfig (0, config_modem_dialtype.string, config_modem_clear.string, config_modem_init.string, config_modem_hangup.string);
+ }
+ configRestored = true;
+ }
+
+ SetNetTime();
+
+ for (pp = pollProcedureList; pp; pp = pp->next)
+ {
+ if (pp->nextTime > net_time)
+ break;
+ pollProcedureList = pp->next;
+ pp->procedure(pp->arg);
+ }
+}
+
+
+void SchedulePollProcedure(PollProcedure *proc, double timeOffset)
+{
+ PollProcedure *pp, *prev;
+
+ proc->nextTime = Sys_FloatTime() + timeOffset;
+ for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next)
+ {
+ if (pp->nextTime >= proc->nextTime)
+ break;
+ prev = pp;
+ }
+
+ if (prev == NULL)
+ {
+ proc->next = pollProcedureList;
+ pollProcedureList = proc;
+ return;
+ }
+
+ proc->next = pp;
+ prev->next = proc;
+}
+
diff --git a/source/net_none.c b/source/net_none.c
new file mode 100644
index 0000000..c00c975
--- /dev/null
+++ b/source/net_none.c
@@ -0,0 +1,46 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#include "quakedef.h"
+
+#include "net_loop.h"
+
+net_driver_t net_drivers[MAX_NET_DRIVERS] =
+{
+ {
+ "Loopback",
+ false,
+ Loop_Init,
+ Loop_Listen,
+ Loop_SearchForHosts,
+ Loop_Connect,
+ Loop_CheckNewConnections,
+ Loop_GetMessage,
+ Loop_SendMessage,
+ Loop_SendUnreliableMessage,
+ Loop_CanSendMessage,
+ Loop_CanSendUnreliableMessage,
+ Loop_Close,
+ Loop_Shutdown
+ }
+};
+int net_numdrivers = 1;
+
+net_landriver_t net_landrivers[MAX_NET_DRIVERS];
+int net_numlandrivers = 0;
diff --git a/source/net_vcr.c b/source/net_vcr.c
new file mode 100644
index 0000000..ba8f40d
--- /dev/null
+++ b/source/net_vcr.c
@@ -0,0 +1,167 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// net_vcr.c
+
+#include "quakedef.h"
+#include "net_vcr.h"
+
+extern int vcrFile;
+
+// This is the playback portion of the VCR. It reads the file produced
+// by the recorder and plays it back to the host. The recording contains
+// everything necessary (events, timestamps, and data) to duplicate the game
+// from the viewpoint of everything above the network layer.
+
+static struct
+{
+ double time;
+ int op;
+ long session;
+} next;
+
+int VCR_Init (void)
+{
+ net_drivers[0].Init = VCR_Init;
+
+ net_drivers[0].SearchForHosts = VCR_SearchForHosts;
+ net_drivers[0].Connect = VCR_Connect;
+ net_drivers[0].CheckNewConnections = VCR_CheckNewConnections;
+ net_drivers[0].QGetMessage = VCR_GetMessage;
+ net_drivers[0].QSendMessage = VCR_SendMessage;
+ net_drivers[0].CanSendMessage = VCR_CanSendMessage;
+ net_drivers[0].Close = VCR_Close;
+ net_drivers[0].Shutdown = VCR_Shutdown;
+
+ Sys_FileRead(vcrFile, &next, sizeof(next));
+ return 0;
+}
+
+void VCR_ReadNext (void)
+{
+ if (Sys_FileRead(vcrFile, &next, sizeof(next)) == 0)
+ {
+ next.op = 255;
+ Sys_Error ("=== END OF PLAYBACK===\n");
+ }
+ if (next.op < 1 || next.op > VCR_MAX_MESSAGE)
+ Sys_Error ("VCR_ReadNext: bad op");
+}
+
+
+void VCR_Listen (qboolean state)
+{
+}
+
+
+void VCR_Shutdown (void)
+{
+}
+
+
+int VCR_GetMessage (qsocket_t *sock)
+{
+ int ret;
+
+ if (host_time != next.time || next.op != VCR_OP_GETMESSAGE || next.session != *(long *)(&sock->driverdata))
+ Sys_Error ("VCR missmatch");
+
+ Sys_FileRead(vcrFile, &ret, sizeof(int));
+ if (ret != 1)
+ {
+ VCR_ReadNext ();
+ return ret;
+ }
+
+ Sys_FileRead(vcrFile, &net_message.cursize, sizeof(int));
+ Sys_FileRead(vcrFile, net_message.data, net_message.cursize);
+
+ VCR_ReadNext ();
+
+ return 1;
+}
+
+
+int VCR_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+ int ret;
+
+ if (host_time != next.time || next.op != VCR_OP_SENDMESSAGE || next.session != *(long *)(&sock->driverdata))
+ Sys_Error ("VCR missmatch");
+
+ Sys_FileRead(vcrFile, &ret, sizeof(int));
+
+ VCR_ReadNext ();
+
+ return ret;
+}
+
+
+qboolean VCR_CanSendMessage (qsocket_t *sock)
+{
+ qboolean ret;
+
+ if (host_time != next.time || next.op != VCR_OP_CANSENDMESSAGE || next.session != *(long *)(&sock->driverdata))
+ Sys_Error ("VCR missmatch");
+
+ Sys_FileRead(vcrFile, &ret, sizeof(int));
+
+ VCR_ReadNext ();
+
+ return ret;
+}
+
+
+void VCR_Close (qsocket_t *sock)
+{
+}
+
+
+void VCR_SearchForHosts (qboolean xmit)
+{
+}
+
+
+qsocket_t *VCR_Connect (char *host)
+{
+ return NULL;
+}
+
+
+qsocket_t *VCR_CheckNewConnections (void)
+{
+ qsocket_t *sock;
+
+ if (host_time != next.time || next.op != VCR_OP_CONNECT)
+ Sys_Error ("VCR missmatch");
+
+ if (!next.session)
+ {
+ VCR_ReadNext ();
+ return NULL;
+ }
+
+ sock = NET_NewQSocket ();
+ *(long *)(&sock->driverdata) = next.session;
+
+ Sys_FileRead (vcrFile, sock->address, NET_NAMELEN);
+ VCR_ReadNext ();
+
+ return sock;
+}
diff --git a/source/net_vcr.h b/source/net_vcr.h
new file mode 100644
index 0000000..95c2f34
--- /dev/null
+++ b/source/net_vcr.h
@@ -0,0 +1,37 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// net_vcr.h
+
+#define VCR_OP_CONNECT 1
+#define VCR_OP_GETMESSAGE 2
+#define VCR_OP_SENDMESSAGE 3
+#define VCR_OP_CANSENDMESSAGE 4
+#define VCR_MAX_MESSAGE 4
+
+int VCR_Init (void);
+void VCR_Listen (qboolean state);
+void VCR_SearchForHosts (qboolean xmit);
+qsocket_t *VCR_Connect (char *host);
+qsocket_t *VCR_CheckNewConnections (void);
+int VCR_GetMessage (qsocket_t *sock);
+int VCR_SendMessage (qsocket_t *sock, sizebuf_t *data);
+qboolean VCR_CanSendMessage (qsocket_t *sock);
+void VCR_Close (qsocket_t *sock);
+void VCR_Shutdown (void);
diff --git a/source/nonintel.c b/source/nonintel.c
new file mode 100644
index 0000000..9e59039
--- /dev/null
+++ b/source/nonintel.c
@@ -0,0 +1,64 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+//
+// nonintel.c: code for non-Intel processors only
+//
+
+#include "quakedef.h"
+#include "r_local.h"
+#include "d_local.h"
+
+#if !id386
+
+/*
+================
+R_Surf8Patch
+================
+*/
+void R_Surf8Patch ()
+{
+ // we only patch code on Intel
+}
+
+
+/*
+================
+R_Surf16Patch
+================
+*/
+void R_Surf16Patch ()
+{
+ // we only patch code on Intel
+}
+
+
+/*
+================
+R_SurfacePatch
+================
+*/
+void R_SurfacePatch (void)
+{
+ // we only patch code on Intel
+}
+
+
+#endif // !id386
+
diff --git a/source/pr_cmds.c b/source/pr_cmds.c
new file mode 100644
index 0000000..80557a3
--- /dev/null
+++ b/source/pr_cmds.c
@@ -0,0 +1,3462 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+#define PR_MAX_TEMPSTRING 2048 // 2001-10-25 Enhanced temp string handling by Maddes
+#define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e))
+
+/*
+===============================================================================
+ BUILT-IN FUNCTIONS
+===============================================================================
+*/
+char pr_varstring_temp[PR_MAX_TEMPSTRING]; // 2001-10-25 Enhanced temp string handling by Maddes
+char *PF_VarString (int first)
+{
+ int i;
+// 2001-10-25 Enhanced temp string handling by Maddes start
+ int maxlen;
+ char *add;
+
+ pr_varstring_temp[0] = 0;
+ for (i=first ; i < pr_argc ; i++)
+ {
+ maxlen = PR_MAX_TEMPSTRING - strlen(pr_varstring_temp) - 1; // -1 is EndOfString
+ add = G_STRING((OFS_PARM0+i*3));
+ if (maxlen > strlen(add))
+ {
+ strcat (pr_varstring_temp, add);
+ }
+ else
+ {
+ strncat (pr_varstring_temp, add, maxlen);
+ pr_varstring_temp[PR_MAX_TEMPSTRING-1] = 0;
+ break; // can stop here
+ }
+ }
+ return pr_varstring_temp;
+// 2001-10-25 Enhanced temp string handling by Maddes end
+}
+
+
+/*
+=================
+PF_errror
+
+This is a TERMINAL error, which will kill off the entire server.
+Dumps self.
+
+error(value)
+=================
+*/
+void PF_error (void)
+{
+ char *s;
+ edict_t *ed;
+
+ s = PF_VarString(0);
+ Con_Printf ("======SERVER ERROR in %s:\n%s\n"
+ ,pr_strings + pr_xfunction->s_name,s);
+ ed = PROG_TO_EDICT(pr_global_struct->self);
+ ED_Print (ed);
+
+ Host_Error ("Program error");
+}
+
+/*
+=================
+PF_objerror
+
+Dumps out self, then an error message. The program is aborted and self is
+removed, but the level can continue.
+
+objerror(value)
+=================
+*/
+void PF_objerror (void)
+{
+ char *s;
+ edict_t *ed;
+
+ s = PF_VarString(0);
+ Con_Printf ("======OBJECT ERROR in %s:\n%s\n"
+ ,pr_strings + pr_xfunction->s_name,s);
+ ed = PROG_TO_EDICT(pr_global_struct->self);
+ ED_Print (ed);
+ ED_Free (ed);
+
+ Host_Error ("Program error");
+}
+
+
+
+/*
+==============
+PF_makevectors
+
+Writes new values for v_forward, v_up, and v_right based on angles
+makevectors(vector)
+==============
+*/
+void PF_makevectors (void)
+{
+ AngleVectors (G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up);
+}
+
+/*
+=================
+PF_setorigin
+
+This is the only valid way to move an object without using the physics of the world (setting velocity and waiting). Directly changing origin will not set internal links correctly, so clipping would be messed up. This should be called when an object is spawned, and then only if it is teleported.
+
+setorigin (entity, origin)
+=================
+*/
+void PF_setorigin (void)
+{
+ edict_t *e;
+ float *org;
+
+ e = G_EDICT(OFS_PARM0);
+ org = G_VECTOR(OFS_PARM1);
+ VectorCopy (org, e->v.origin);
+ SV_LinkEdict (e, false);
+}
+
+
+void SetMinMaxSize (edict_t *e, float *min, float *max, qboolean rotate)
+{
+ float *angles;
+ vec3_t rmin, rmax;
+ float bounds[2][3];
+ float xvector[2], yvector[2];
+ float a;
+ vec3_t base, transformed;
+ int i, j, k, l;
+
+ for (i=0 ; i<3 ; i++)
+ if (min[i] > max[i])
+ PR_RunError ("backwards mins/maxs");
+
+ rotate = false; // FIXME: implement rotation properly again
+
+ if (!rotate)
+ {
+ VectorCopy (min, rmin);
+ VectorCopy (max, rmax);
+ }
+ else
+ {
+ // find min / max for rotations
+ angles = e->v.angles;
+
+ a = angles[1]/180 * M_PI;
+
+ xvector[0] = cosf(a);
+ xvector[1] = sinf(a);
+ yvector[0] = -sinf(a);
+ yvector[1] = cosf(a);
+
+ VectorCopy (min, bounds[0]);
+ VectorCopy (max, bounds[1]);
+
+ rmin[0] = rmin[1] = rmin[2] = 9999;
+ rmax[0] = rmax[1] = rmax[2] = -9999;
+
+ for (i=0 ; i<= 1 ; i++)
+ {
+ base[0] = bounds[i][0];
+ for (j=0 ; j<= 1 ; j++)
+ {
+ base[1] = bounds[j][1];
+ for (k=0 ; k<= 1 ; k++)
+ {
+ base[2] = bounds[k][2];
+
+ // transform the point
+ transformed[0] = xvector[0]*base[0] + yvector[0]*base[1];
+ transformed[1] = xvector[1]*base[0] + yvector[1]*base[1];
+ transformed[2] = base[2];
+
+ for (l=0 ; l<3 ; l++)
+ {
+ if (transformed[l] < rmin[l])
+ rmin[l] = transformed[l];
+ if (transformed[l] > rmax[l])
+ rmax[l] = transformed[l];
+ }
+ }
+ }
+ }
+ }
+
+// set derived values
+ VectorCopy (rmin, e->v.mins);
+ VectorCopy (rmax, e->v.maxs);
+ VectorSubtract (max, min, e->v.size);
+
+ SV_LinkEdict (e, false);
+}
+
+/*
+=================
+PF_setsize
+
+the size box is rotated by the current angle
+
+setsize (entity, minvector, maxvector)
+=================
+*/
+void PF_setsize (void)
+{
+ edict_t *e;
+ float *min, *max;
+
+ e = G_EDICT(OFS_PARM0);
+ min = G_VECTOR(OFS_PARM1);
+ max = G_VECTOR(OFS_PARM2);
+ SetMinMaxSize (e, min, max, false);
+}
+
+
+/*
+=================
+PF_setmodel
+
+setmodel(entity, model)
+=================
+*/
+void PF_setmodel (void)
+{
+ edict_t *e;
+ char *m, **check;
+ model_t *mod;
+ int i;
+
+ e = G_EDICT(OFS_PARM0);
+ m = G_STRING(OFS_PARM1);
+
+// check to see if model was properly precached
+ for (i=0, check = sv.model_precache ; *check ; i++, check++)
+ if (!strcmp(*check, m))
+ break;
+
+ if (!*check)
+ PR_RunError ("no precache: %s\n", m);
+
+
+ e->v.model = m - pr_strings;
+ e->v.modelindex = i; //SV_ModelIndex (m);
+
+ mod = sv.models[ (int)e->v.modelindex]; // Mod_ForName (m, true);
+
+ if (mod)
+ SetMinMaxSize (e, mod->mins, mod->maxs, true);
+ else
+ SetMinMaxSize (e, vec3_origin, vec3_origin, true);
+}
+
+/*
+=================
+PF_bprint
+
+broadcast print to everyone on server
+
+bprint(value)
+=================
+*/
+void PF_bprint (void)
+{
+ char *s;
+
+ s = PF_VarString(0);
+ SV_BroadcastPrintf ("%s", s);
+}
+
+/*
+=================
+PF_sprint
+
+single print to a specific client
+
+sprint(clientent, value)
+=================
+*/
+void PF_sprint (void)
+{
+ char *s;
+ client_t *client;
+ int entnum;
+
+ entnum = G_EDICTNUM(OFS_PARM0);
+ s = PF_VarString(1);
+
+ if (entnum < 1 || entnum > svs.maxclients)
+ {
+ Con_Printf ("tried to sprint to a non-client\n");
+ return;
+ }
+
+ client = &svs.clients[entnum-1];
+
+ MSG_WriteChar (&client->message,svc_print);
+ MSG_WriteString (&client->message, s );
+}
+
+
+/*
+=================
+PF_centerprint
+
+single print to a specific client
+
+centerprint(clientent, value)
+=================
+*/
+void PF_centerprint (void)
+{
+ char *s;
+ client_t *client;
+ int entnum;
+
+ entnum = G_EDICTNUM(OFS_PARM0);
+ s = PF_VarString(1);
+
+ if (entnum < 1 || entnum > svs.maxclients)
+ {
+ Con_Printf ("tried to sprint to a non-client\n");
+ return;
+ }
+
+ client = &svs.clients[entnum-1];
+
+ MSG_WriteChar (&client->message,svc_centerprint);
+ MSG_WriteString (&client->message, s );
+}
+
+/*
+=================
+PF_useprint
+
+Print a text depending on what it is fed with
+
+useprint(entity client, float type, float cost, float weapon)
+=================
+*/
+void PF_useprint (void)
+{
+ client_t *client;
+ int entnum, type, cost, weapon;
+
+ entnum = G_EDICTNUM(OFS_PARM0);
+ type = G_FLOAT(OFS_PARM1);
+ cost = G_FLOAT(OFS_PARM2);
+ weapon = G_FLOAT(OFS_PARM3);
+
+
+ if (entnum < 1 || entnum > svs.maxclients)
+ {
+ Con_Printf ("tried to sprint to a non-client\n");
+ return;
+ }
+
+ client = &svs.clients[entnum-1];
+
+ MSG_WriteByte (&client->message,svc_useprint);
+ MSG_WriteByte (&client->message,type);
+ MSG_WriteShort (&client->message,cost);
+ MSG_WriteByte (&client->message,weapon);
+ //MSG_WriteString (&client->message, s );
+}
+
+
+/*
+=================
+PF_normalize
+
+vector normalize(vector)
+=================
+*/
+void PF_normalize (void)
+{
+ float *value1;
+ vec3_t newvalue;
+ float new;
+
+ value1 = G_VECTOR(OFS_PARM0);
+
+ new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
+ new = sqrtf(new);
+
+ if (new == 0)
+ newvalue[0] = newvalue[1] = newvalue[2] = 0;
+ else
+ {
+ new = 1/new;
+ newvalue[0] = value1[0] * new;
+ newvalue[1] = value1[1] * new;
+ newvalue[2] = value1[2] * new;
+ }
+
+ VectorCopy (newvalue, G_VECTOR(OFS_RETURN));
+}
+
+/*
+=================
+PF_vlen
+
+scalar vlen(vector)
+=================
+*/
+void PF_vlen (void)
+{
+ float *value1;
+ float new;
+
+ value1 = G_VECTOR(OFS_PARM0);
+
+ new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
+ new = sqrtf(new);
+
+ G_FLOAT(OFS_RETURN) = new;
+}
+
+/*
+=================
+PF_vectoyaw
+
+float vectoyaw(vector)
+=================
+*/
+void PF_vectoyaw (void)
+{
+ float *value1;
+ float yaw;
+
+ value1 = G_VECTOR(OFS_PARM0);
+
+ if (value1[1] == 0 && value1[0] == 0)
+ yaw = 0;
+ else
+ {
+ yaw = (int) (atan2f(value1[1], value1[0]) * 180 / M_PI);
+ if (yaw < 0)
+ yaw += 360;
+ }
+
+ G_FLOAT(OFS_RETURN) = yaw;
+}
+
+
+/*
+=================
+PF_vectoangles
+
+vector vectoangles(vector)
+=================
+*/
+void PF_vectoangles (void)
+{
+ float *value1;
+ float forward;
+ float yaw, pitch;
+
+ value1 = G_VECTOR(OFS_PARM0);
+
+ if (value1[1] == 0 && value1[0] == 0)
+ {
+ yaw = 0;
+ if (value1[2] > 0)
+ pitch = 90;
+ else
+ pitch = 270;
+ }
+ else
+ {
+ yaw = (int) (atan2f(value1[1], value1[0]) * 180 / M_PI);
+ if (yaw < 0)
+ yaw += 360;
+
+ forward = sqrtf (value1[0]*value1[0] + value1[1]*value1[1]);
+ pitch = (int) (atan2f(value1[2], forward) * 180 / M_PI);
+ if (pitch < 0)
+ pitch += 360;
+ }
+
+ G_FLOAT(OFS_RETURN+0) = pitch;
+ G_FLOAT(OFS_RETURN+1) = yaw;
+ G_FLOAT(OFS_RETURN+2) = 0;
+}
+
+/*
+=================
+PF_Random
+
+Returns a number from 0<= num < 1
+
+random()
+=================
+*/
+void PF_random (void)
+{
+ float num;
+
+ num = (rand ()&0x7fff) / ((float)0x7fff);
+
+ G_FLOAT(OFS_RETURN) = num;
+}
+
+/*
+=================
+PF_particle
+
+particle(origin, color, count)
+=================
+*/
+void PF_particle (void)
+{
+ float *org, *dir;
+ float color;
+ float count;
+
+ org = G_VECTOR(OFS_PARM0);
+ dir = G_VECTOR(OFS_PARM1);
+ color = G_FLOAT(OFS_PARM2);
+ count = G_FLOAT(OFS_PARM3);
+ SV_StartParticle (org, dir, color, count);
+}
+
+
+/*
+=================
+PF_ambientsound
+
+=================
+*/
+void PF_ambientsound (void)
+{
+ char **check;
+ char *samp;
+ float *pos;
+ float vol, attenuation;
+ int i, soundnum;
+
+ pos = G_VECTOR (OFS_PARM0);
+ samp = G_STRING(OFS_PARM1);
+ vol = G_FLOAT(OFS_PARM2);
+ attenuation = G_FLOAT(OFS_PARM3);
+
+// check to see if samp was properly precached
+ for (soundnum=0, check = sv.sound_precache ; *check ; check++, soundnum++)
+ if (!strcmp(*check,samp))
+ break;
+
+ if (!*check)
+ {
+ Con_Printf ("no precache: %s\n", samp);
+ return;
+ }
+
+// add an svc_spawnambient command to the level signon packet
+
+ MSG_WriteByte (&sv.signon,svc_spawnstaticsound);
+ for (i=0 ; i<3 ; i++)
+ MSG_WriteCoord(&sv.signon, pos[i]);
+
+ MSG_WriteByte (&sv.signon, soundnum);
+
+ MSG_WriteByte (&sv.signon, vol*255);
+ MSG_WriteByte (&sv.signon, attenuation*64);
+
+}
+
+/*
+=================
+PF_sound
+
+Each entity can have eight independant sound sources, like voice,
+weapon, feet, etc.
+
+Channel 0 is an auto-allocate channel, the others override anything
+allready running on that entity/channel pair.
+
+An attenuation of 0 will play full volume everywhere in the level.
+Larger attenuations will drop off.
+
+=================
+*/
+void PF_sound (void)
+{
+ char *sample;
+ int channel;
+ edict_t *entity;
+ int volume;
+ float attenuation;
+
+ entity = G_EDICT(OFS_PARM0);
+ channel = G_FLOAT(OFS_PARM1);
+ sample = G_STRING(OFS_PARM2);
+ volume = G_FLOAT(OFS_PARM3) * 255;
+ attenuation = G_FLOAT(OFS_PARM4);
+
+ if (volume < 0 || volume > 255)
+ Sys_Error ("SV_StartSound: volume = %i", volume);
+
+ if (attenuation < 0 || attenuation > 4)
+ Sys_Error ("SV_StartSound: attenuation = %f", attenuation);
+
+ if (channel < 0 || channel > 7)
+ Sys_Error ("SV_StartSound: channel = %i", channel);
+
+ SV_StartSound (entity, channel, sample, volume, attenuation);
+}
+
+/*
+=================
+PF_break
+
+break()
+=================
+*/
+void PF_break (void)
+{
+Con_Printf ("break statement\n");
+*(int *)-4 = 0; // dump to debugger
+// PR_RunError ("break statement");
+}
+
+/*
+=================
+PF_traceline
+
+Used for use tracing and shot targeting
+Traces are blocked by bbox and exact bsp entityes, and also slide box entities
+if the tryents flag is set.
+
+traceline (vector1, vector2, tryents)
+=================
+*/
+void PF_traceline (void)
+{
+ float *v1, *v2;
+ trace_t trace;
+ int nomonsters;
+ edict_t *ent;
+
+ v1 = G_VECTOR(OFS_PARM0);
+ v2 = G_VECTOR(OFS_PARM1);
+ nomonsters = G_FLOAT(OFS_PARM2);
+ ent = G_EDICT(OFS_PARM3);
+
+ trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent);
+
+ pr_global_struct->trace_allsolid = trace.allsolid;
+ pr_global_struct->trace_startsolid = trace.startsolid;
+ pr_global_struct->trace_fraction = trace.fraction;
+ pr_global_struct->trace_inwater = trace.inwater;
+ pr_global_struct->trace_inopen = trace.inopen;
+ VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
+ VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
+ pr_global_struct->trace_plane_dist = trace.plane.dist;
+ if (trace.ent)
+ pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
+ else
+ pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
+}
+
+
+extern trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore);
+
+void PF_TraceToss (void)
+{
+ trace_t trace;
+ edict_t *ent, *ignore;
+
+ ent = G_EDICT(OFS_PARM0);
+ ignore = G_EDICT(OFS_PARM1);
+
+ trace = SV_Trace_Toss (ent, ignore);
+
+ pr_global_struct->trace_allsolid = trace.allsolid;
+ pr_global_struct->trace_startsolid = trace.startsolid;
+ pr_global_struct->trace_fraction = trace.fraction;
+ pr_global_struct->trace_inwater = trace.inwater;
+ pr_global_struct->trace_inopen = trace.inopen;
+ VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
+ VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
+ pr_global_struct->trace_plane_dist = trace.plane.dist;
+ if (trace.ent)
+ pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
+ else
+ pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
+}
+
+int TraceMove(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *ent)//engine-sides
+{
+ if(start[0] == end[0] && start[1] == end[1] && start[2] == end[2])
+ {
+ return 1;
+ }
+ vec3_t forward, up;
+ float HorDist;
+ vec3_t HorGoal;
+ vec3_t tempHorGoal;
+
+ up[0] = 0; up[1] = 0; up[2] = 1;
+ HorGoal[0] = end[0]; HorGoal[1] = end[1]; HorGoal[2] = start[2];
+
+ VectorSubtract(HorGoal,start,forward);
+ HorDist = VectorLength(forward);
+ VectorNormalize(forward);
+
+ vec3_t CurrentPos;
+
+ VectorCopy(start,CurrentPos);
+ VectorCopy(HorGoal,tempHorGoal);
+
+ float CurrentDist = 0;//2d distance from initial 3d positionvector
+
+ trace_t trace1, trace2;
+ float tempDist;
+ vec3_t tempVec;
+ vec3_t tempVec2;
+ float i;
+ int STEPSIZEB = 18;//other declaration isn't declared yet
+ float SLOPELEN = 10.4;//18/tan(60) = 10.4, the the length of the triangle formed by the max walkable slope of 60 degrees.
+ int skip = 0;
+ int LoopBreak = 0;
+
+ while(CurrentDist < HorDist)
+ {
+ if(LoopBreak > 20)//was 50, decreased this quite a bit. now it's 260 meters
+ {
+ //Con_Printf("AI Warning: There is a ledge that is greater than 650 meters.\n");
+ return -1;
+ }
+
+ trace1 = SV_Move(CurrentPos, mins, maxs, tempHorGoal, MOVE_NOMONSTERS, ent);
+
+ VectorSubtract(tempHorGoal,CurrentPos,tempVec);
+ tempDist = trace1.fraction * VectorLength(tempVec);
+ //Check if we fell along the path
+ for(i = (maxs[0] * 1); i < tempDist; i += (maxs[0] * 1))
+ {
+ VectorScale(forward,i,tempVec);
+ VectorAdd(tempVec,CurrentPos,tempVec);
+ VectorScale(up,-500,tempVec2);//500 inches is about 13 meters
+ VectorAdd(tempVec,tempVec2,tempVec2);
+ trace2 = SV_Move(tempVec, mins, maxs, tempVec2, MOVE_NOMONSTERS, ent);
+ if(trace2.fraction > 0)
+ {
+ VectorScale(up,trace2.fraction * -100,tempVec2);
+ VectorAdd(tempVec,tempVec2,CurrentPos);
+ VectorAdd(tempHorGoal,tempVec2,tempHorGoal);
+ skip = 1;
+ CurrentDist += i;
+ if(trace2.fraction == 1)
+ {
+ //We fell the full 13 meters!, we need to be careful here,
+ //because if we're checking over the void, then we could be stuck in an infinite loop and crash the game
+ //So we're going to keep track of how many times we fall 13 meters
+ LoopBreak++;
+ }
+ else
+ {
+ LoopBreak = 0;
+ }
+ break;
+ }
+ }
+ //If we fell at any location along path, then we don't try to step up
+ if(skip == 1)
+ {
+ trace2.fraction = 0;
+ skip = 0;
+ continue;
+ }
+ //We need to advance it as much as possible along path before step up
+ if(trace1.fraction > 0 && trace1.fraction < 1)
+ {
+ VectorCopy(trace1.endpos,CurrentPos);
+ trace1.fraction = 0;
+ }
+ //Check step up
+ if(trace1.fraction < 1)
+ {
+ VectorScale(up,STEPSIZEB,tempVec2);
+ VectorAdd(CurrentPos,tempVec2,tempVec);
+ VectorAdd(tempHorGoal,tempVec2,tempVec2);
+ trace2 = SV_Move(tempVec, mins, maxs, tempVec2, MOVE_NOMONSTERS, ent);
+ //10.4 is minimum length for a slope of 60 degrees, we need to at least advance this much to know the surface is walkable
+ VectorSubtract(tempVec2,tempVec,tempVec2);
+ if(trace2.fraction > (trace1.fraction + (SLOPELEN/VectorLength(tempVec2))) || trace2.fraction == 1)
+ {
+ VectorCopy(tempVec,CurrentPos);
+ tempHorGoal[2] = CurrentPos[2];
+ continue;
+ }
+ else
+ {
+ return 0;//stepping up didn't advance so we've hit a wall, we failed
+ }
+ }
+ if(trace1.fraction == 1)//we've made it horizontally to our goal... so check if we've made it vertically...
+ {
+ if((end[2] - tempHorGoal[2] < STEPSIZEB) && (end[2] - tempHorGoal[2]) > -1 * STEPSIZEB)
+ return 1;
+ else return 0;
+ }
+ }
+ return 0;
+}
+
+
+void PF_tracemove(void)//progs side
+{
+ float *start, *end, *mins, *maxs;
+ int nomonsters;
+ edict_t *ent;
+
+ start = G_VECTOR(OFS_PARM0);
+ mins = G_VECTOR(OFS_PARM1);
+ maxs = G_VECTOR(OFS_PARM2);
+ end = G_VECTOR(OFS_PARM3);
+ nomonsters = G_FLOAT(OFS_PARM4);
+ ent = G_EDICT(OFS_PARM5);
+
+ Con_DPrintf ("TraceMove start, ");
+ G_INT(OFS_RETURN) = TraceMove(start, mins, maxs, end,nomonsters,ent);
+ Con_DPrintf ("TM end\n");
+ return;
+}
+
+void PF_tracebox (void)
+{
+ float *v1, *v2, *mins, *maxs;
+ trace_t trace;
+ int nomonsters;
+ edict_t *ent;
+
+ v1 = G_VECTOR(OFS_PARM0);
+ mins = G_VECTOR(OFS_PARM1);
+ maxs = G_VECTOR(OFS_PARM2);
+ v2 = G_VECTOR(OFS_PARM3);
+ nomonsters = G_FLOAT(OFS_PARM4);
+ ent = G_EDICT(OFS_PARM5);
+
+ trace = SV_Move (v1, mins, maxs, v2, nomonsters, ent);
+
+ pr_global_struct->trace_allsolid = trace.allsolid;
+ pr_global_struct->trace_startsolid = trace.startsolid;
+ pr_global_struct->trace_fraction = trace.fraction;
+ pr_global_struct->trace_inwater = trace.inwater;
+ pr_global_struct->trace_inopen = trace.inopen;
+ VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
+ VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
+ pr_global_struct->trace_plane_dist = trace.plane.dist;
+ if (trace.ent)
+ pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
+ else
+ pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
+}
+/*
+=================
+PF_checkpos
+
+Returns true if the given entity can move to the given position from it's
+current position by walking or rolling.
+FIXME: make work...
+scalar checkpos (entity, vector)
+=================
+*/
+void PF_checkpos (void)
+{
+}
+
+//============================================================================
+
+byte checkpvs[MAX_MAP_LEAFS/8];
+
+int PF_newcheckclient (int check)
+{
+ int i;
+ byte *pvs;
+ edict_t *ent;
+ mleaf_t *leaf;
+ vec3_t org;
+
+// cycle to the next one
+
+ if (check < 1)
+ check = 1;
+ if (check > svs.maxclients)
+ check = svs.maxclients;
+
+ if (check == svs.maxclients)
+ i = 1;
+ else
+ i = check + 1;
+
+ for ( ; ; i++)
+ {
+ if (i == svs.maxclients+1)
+ i = 1;
+
+ ent = EDICT_NUM(i);
+
+ if (i == check)
+ break; // didn't find anything else
+
+ if (ent->free)
+ continue;
+ if (ent->v.health <= 0)
+ continue;
+ if ((int)ent->v.flags & FL_NOTARGET)
+ continue;
+
+ // anything that is a client, or has a client as an enemy
+ break;
+ }
+
+// get the PVS for the entity
+ VectorAdd (ent->v.origin, ent->v.view_ofs, org);
+ leaf = Mod_PointInLeaf (org, sv.worldmodel);
+ pvs = Mod_LeafPVS (leaf, sv.worldmodel);
+ memcpy (checkpvs, pvs, (sv.worldmodel->numleafs+7)>>3 );
+
+ return i;
+}
+
+/*
+=================
+PF_checkclient
+
+Returns a client (or object that has a client enemy) that would be a
+valid target.
+
+If there are more than one valid options, they are cycled each frame
+
+If (self.origin + self.viewofs) is not in the PVS of the current target,
+it is not returned at all.
+
+name checkclient ()
+=================
+*/
+#define MAX_CHECK 16
+int c_invis, c_notvis;
+void PF_checkclient (void)
+{
+ edict_t *ent, *self;
+ mleaf_t *leaf;
+ int l;
+ vec3_t view;
+
+// find a new check if on a new frame
+ if (sv.time - sv.lastchecktime >= 0.1)
+ {
+ sv.lastcheck = PF_newcheckclient (sv.lastcheck);
+ sv.lastchecktime = sv.time;
+ }
+
+// return check if it might be visible
+ ent = EDICT_NUM(sv.lastcheck);
+ if (ent->free || ent->v.health <= 0)
+ {
+ RETURN_EDICT(sv.edicts);
+ return;
+ }
+
+// if current entity can't possibly see the check entity, return 0
+ self = PROG_TO_EDICT(pr_global_struct->self);
+ VectorAdd (self->v.origin, self->v.view_ofs, view);
+ leaf = Mod_PointInLeaf (view, sv.worldmodel);
+ l = (leaf - sv.worldmodel->leafs) - 1;
+ if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) )
+ {
+c_notvis++;
+ RETURN_EDICT(sv.edicts);
+ return;
+ }
+
+// might be able to see it
+c_invis++;
+ RETURN_EDICT(ent);
+}
+
+//============================================================================
+
+
+/*
+=================
+PF_stuffcmd
+
+Sends text over to the client's execution buffer
+
+stuffcmd (clientent, value)
+=================
+*/
+void PF_stuffcmd (void)
+{
+ int entnum;
+ char *str;
+ client_t *old;
+
+ entnum = G_EDICTNUM(OFS_PARM0);
+ if (entnum < 1 || entnum > svs.maxclients)
+ PR_RunError ("Parm 0 not a client");
+ str = G_STRING(OFS_PARM1);
+
+ old = host_client;
+ host_client = &svs.clients[entnum-1];
+ Host_ClientCommands ("%s", str);
+ host_client = old;
+}
+
+/*
+=================
+PF_localcmd
+
+Sends text over to the client's execution buffer
+
+localcmd (string)
+=================
+*/
+void PF_localcmd (void)
+{
+ char *str;
+
+ str = G_STRING(OFS_PARM0);
+ Cbuf_AddText (str);
+}
+
+/*
+=================
+PF_cvar
+
+float cvar (string)
+=================
+*/
+void PF_cvar (void)
+{
+ char *str;
+
+ str = G_STRING(OFS_PARM0);
+
+ G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str);
+}
+
+/*
+=================
+PF_cvar_set
+
+float cvar (string)
+=================
+*/
+void PF_cvar_set (void)
+{
+ char *var, *val;
+
+ var = G_STRING(OFS_PARM0);
+ val = G_STRING(OFS_PARM1);
+
+ Cvar_Set (var, val);
+}
+
+/*
+=================
+PF_findradius
+
+Returns a chain of entities that have origins within a spherical area
+
+findradius (origin, radius)
+=================
+*/
+void PF_findradius (void)
+{
+ edict_t *ent, *chain;
+ float rad;
+ float *org;
+ vec3_t eorg;
+ int i, j;
+
+ chain = (edict_t *)sv.edicts;
+
+ org = G_VECTOR(OFS_PARM0);
+ rad = G_FLOAT(OFS_PARM1);
+
+ ent = NEXT_EDICT(sv.edicts);
+ for (i=1 ; ifree)
+ continue;
+ if (ent->v.solid == SOLID_NOT)
+ continue;
+ for (j=0 ; j<3 ; j++)
+ eorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j])*0.5);
+ if (Length(eorg) > rad)
+ continue;
+
+ ent->v.chain = EDICT_TO_PROG(chain);
+ chain = ent;
+ }
+
+ RETURN_EDICT(chain);
+}
+
+
+/*
+=========
+PF_dprint
+=========
+*/
+void PF_dprint (void)
+{
+ Con_DPrintf ("%s",PF_VarString(0));
+}
+
+char pr_string_temp[PR_MAX_TEMPSTRING]; // 2001-10-25 Enhanced temp string handling by Maddes
+
+void PF_ftos (void)
+{
+ float v;
+ v = G_FLOAT(OFS_PARM0);
+
+ if (v == (int)v)
+ sprintf (pr_string_temp, "%d",(int)v);
+ else
+ sprintf (pr_string_temp, "%5.1f",v);
+ G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+
+void PF_fabs (void)
+{
+ float v;
+ v = G_FLOAT(OFS_PARM0);
+ G_FLOAT(OFS_RETURN) = fabsf(v);
+}
+
+void PF_vtos (void)
+{
+ sprintf (pr_string_temp, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]);
+ G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+
+void PF_etos (void)
+{
+ sprintf (pr_string_temp, "entity %i", G_EDICTNUM(OFS_PARM0));
+ G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+
+
+void PF_Spawn (void)
+{
+ edict_t *ed;
+ ed = ED_Alloc();
+ RETURN_EDICT(ed);
+}
+
+void PF_Remove (void)
+{
+ edict_t *ed;
+
+ ed = G_EDICT(OFS_PARM0);
+ ED_Free (ed);
+}
+
+// 2001-09-20 QuakeC string manipulation by FrikaC/Maddes start
+/*
+=================
+PF_strzone
+
+string strzone (string)
+=================
+*/
+void PF_strzone (void)
+{
+ char *m, *p;
+ m = G_STRING(OFS_PARM0);
+ p = Z_Malloc(strlen(m) + 1);
+ strcpy(p, m);
+
+ G_INT(OFS_RETURN) = p - pr_strings;
+}
+
+/*
+=================
+PF_strunzone
+
+string strunzone (string)
+=================
+*/
+void PF_strunzone (void)
+{
+ Z_Free(G_STRING(OFS_PARM0));
+ G_INT(OFS_PARM0) = OFS_NULL; // empty the def
+};
+
+/*
+=================
+PF_strtrim
+
+string strtrim (string)
+=================
+*/
+void PF_strtrim (void)
+{
+ char *m;
+ m = G_STRING(OFS_PARM0);
+
+ char *c;
+ c = m;
+
+ while (c != '\0' && *c == ' ')
+ c++;
+ m = c;
+
+ c = m + strlen (m) - 1;
+ while (c >= m)
+ {
+ if (*c == ' ')
+ *c = '\0';
+ else
+ break;
+ c--;
+ }
+ G_INT(OFS_RETURN) = m - pr_strings;
+};
+
+/*
+=================
+PF_strlen
+
+float strlen (string)
+=================
+*/
+void PF_strlen (void)
+{
+ char *p = G_STRING(OFS_PARM0);
+ G_FLOAT(OFS_RETURN) = strlen(p);
+}
+
+/*
+=================
+PF_strcat
+
+string strcat (string, string)
+=================
+*/
+
+void PF_strcat (void)
+{
+ char *s1, *s2;
+ int maxlen; // 2001-10-25 Enhanced temp string handling by Maddes
+
+ s1 = G_STRING(OFS_PARM0);
+ s2 = PF_VarString(1);
+
+// 2001-10-25 Enhanced temp string handling by Maddes start
+ pr_string_temp[0] = 0;
+ if (strlen(s1) < PR_MAX_TEMPSTRING)
+ {
+ strcpy(pr_string_temp, s1);
+ }
+ else
+ {
+ strncpy(pr_string_temp, s1, PR_MAX_TEMPSTRING);
+ pr_string_temp[PR_MAX_TEMPSTRING-1] = 0;
+ }
+
+ maxlen = PR_MAX_TEMPSTRING - strlen(pr_string_temp) - 1; // -1 is EndOfString
+ if (maxlen > 0)
+ {
+ if (maxlen > strlen(s2))
+ {
+ strcat (pr_string_temp, s2);
+ }
+ else
+ {
+ strncat (pr_string_temp, s2, maxlen);
+ pr_string_temp[PR_MAX_TEMPSTRING-1] = 0;
+ }
+ }
+// 2001-10-25 Enhanced temp string handling by Maddes end
+
+ G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+
+/*
+=================
+PF_substring
+
+string substring (string, float, float)
+=================
+*/
+void PF_substring (void)
+{
+ int offset, length;
+ int maxoffset; // 2001-10-25 Enhanced temp string handling by Maddes
+ char *p;
+
+ p = G_STRING(OFS_PARM0);
+ offset = (int)G_FLOAT(OFS_PARM1); // for some reason, Quake doesn't like G_INT
+ length = (int)G_FLOAT(OFS_PARM2);
+
+ // cap values
+ maxoffset = strlen(p);
+ if (offset > maxoffset)
+ {
+ offset = maxoffset;
+ }
+ if (offset < 0)
+ offset = 0;
+// 2001-10-25 Enhanced temp string handling by Maddes start
+ if (length >= PR_MAX_TEMPSTRING)
+ length = PR_MAX_TEMPSTRING-1;
+// 2001-10-25 Enhanced temp string handling by Maddes end
+ if (length < 0)
+ length = 0;
+
+ p += offset;
+ strncpy(pr_string_temp, p, length);
+ pr_string_temp[length]=0;
+
+ G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+
+/*
+=================
+PF_stof
+
+float stof (string)
+=================
+*/
+// thanks Zoid, taken from QuakeWorld
+void PF_stof (void)
+{
+ char *s;
+
+ s = G_STRING(OFS_PARM0);
+ G_FLOAT(OFS_RETURN) = atof(s);
+}
+
+/*
+=================
+PF_stov
+
+vector stov (string)
+=================
+*/
+void PF_stov (void)
+{
+ char *v;
+ int i;
+ vec3_t d;
+
+ v = G_STRING(OFS_PARM0);
+
+ for (i=0; i<3; i++)
+ {
+ while(v && (v[0] == ' ' || v[0] == '\'')) //skip unneeded data
+ v++;
+ d[i] = atof(v);
+ while (v && v[0] != ' ') // skip to next space
+ v++;
+ }
+ VectorCopy (d, G_VECTOR(OFS_RETURN));
+}
+
+/*
+=================
+PF_Set_Zombie
+
+void Set_Zombie (entity)
+=================
+*/
+void PF_Set_Zombie (void)
+{
+ //int entitynum = G_EDICT(OFS_PARM0);
+}
+
+
+/*
+=================
+Main_Waypoint functin
+
+This is where the magic happens
+=================
+*/
+
+int closedset[MAX_WAYPOINTS]; // The set of nodes already evaluated.
+int openset[MAX_WAYPOINTS];//Actual sorted open list
+int opensetRef[MAX_WAYPOINTS];//Reference values of open list
+int opensetLength;//equivalent of javaScript's array[].length;
+#define MaxZombies 16
+
+zombie_ai zombie_list[MaxZombies];
+//Debug//
+void printSortedOpenSet()
+{
+ Con_Printf("Sorted!: ");
+ int qr;
+ for(qr = 0; qr < opensetLength; qr++)
+ {
+ Con_Printf("%i, ",(int)waypoints[openset[qr]].f_score);
+ }
+ Con_Printf("\n");
+}
+//------//
+
+
+void RemoveWayFromList (int listnumber, int waynum)
+{
+ if(listnumber == 1)
+ {
+ //Con_DPrintf ("RemoveWayFromList: closedset[%i] = %i\n", waynum, 0);
+ closedset[waynum] = 0;
+ return;
+ }
+
+ int i;
+ int s;
+ if(listnumber == 2)
+ {
+ for(i = 0; i < opensetLength; i++)
+ {
+ if(openset[i] == waynum)
+ {
+ openset[i] = 0;
+ opensetRef[waynum] = 0;
+
+ for(s = i; s < opensetLength; s++)
+ {
+ openset[s] = openset[s+1];
+ }
+ opensetLength -= 1;
+ return;
+ }
+ }
+ }
+}
+
+void CompareOpenLists()
+{
+ int refCount, count;
+ refCount = 0;
+ count = 0;
+ int i;
+ for(i = 0; i < MAX_WAYPOINTS; i++)
+ {
+ if(openset[i])
+ count++;
+ if(opensetRef[i])
+ refCount++;
+ }
+ if(count != refCount || count != opensetLength || refCount != opensetLength)
+ Con_Printf("%i%i%i\n",count, refCount,opensetLength);
+}
+
+int AddWayToList (int listnumber, int waynum)//blubs binary sorting
+{
+ if(listnumber == 1)//closed list
+ {
+ //Con_DPrintf ("AddWayToList: closedset[%i] = %i\n", waynum, 1);
+ closedset[waynum] = 1;
+ return 1;
+ }
+
+ if(listnumber == 2)//openlist
+ {
+ int min, max, test;
+ min = -1;
+ max = opensetLength;
+ float wayVal = waypoints[waynum].f_score;
+
+ while(max > min)
+ {
+ if(max - min == 1)
+ {
+ int i;
+ for(i = opensetLength; i > max ; i--)
+ {
+ openset[i] = openset[i-1];
+ }
+ openset[max] = waynum;
+ opensetLength += 1;
+ opensetRef[waynum] = 1;
+ //printSortedOpenSet(); for debug only
+ return max;
+ }
+ test = (int)((min + max)/2);
+ if(wayVal > waypoints[openset[test]].f_score)
+ {
+ min = test;
+ }
+ else if(wayVal < waypoints[openset[test]].f_score)
+ {
+ max = test;
+ }
+ if(wayVal == waypoints[openset[test]].f_score)
+ {
+ max = test;
+ min = test - 1;
+ }
+ }
+ }
+ return -1;
+}
+
+int GetLowestFromOpenSet()
+{
+ return openset[0];
+}
+
+int CheckIfEmptyList (int listnumber)
+{
+ int i;
+
+ for (i = 0; i < MAX_WAYPOINTS; i++)
+ {
+ if (listnumber == 1)
+ {
+ if (closedset[i])
+ {
+ //Con_DPrintf ("CheckIfEmptyList: closedset[%i]\n", i);
+ return 0;
+ }
+ }
+ else if (listnumber == 2)
+ {
+ if (openset[i])
+ {
+ //Con_DPrintf ("CheckIfEmptyList: openset[%i]\n", i);
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+int CheckIfWayInList (int listnumber, int waynum)
+{
+ if(listnumber == 1)
+ {
+ if(closedset[waynum])
+ {
+ //Con_DPrintf ("CheckIfWayInList: closedset[%i] = %i\n", waynum, 1);
+ return 1;
+ }
+ }
+ if(listnumber == 2)
+ {
+ if(opensetRef[waynum])
+ {
+ //Con_DPrintf ("CheckIfWayInList: openset[%i] = %i\n", waynum, 1);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+float heuristic_cost_estimate (int start_way, int end_way)
+{
+ //for now we will just look the distance between.
+ return VecLength2(waypoints[start_way].origin, waypoints[end_way].origin);
+}
+
+int proces_list[MAX_WAYPOINTS];
+void reconstruct_path(int start_node, int current_node)
+{
+ int i, s, current;
+ current = current_node;
+ s = 0;
+/*
+ if (current_node == waypoints[current_node].came_from)
+ p := reconstruct_path(came_from, came_from[current_node])
+ return (p + current_node)
+ else
+ return current_node*/
+ Con_DPrintf ("\n");
+ Con_DPrintf ("reconstruct_path: start_node = %i, current_node = %i\n\n", start_node, current_node);
+ for (i = 0;i < MAX_WAYPOINTS; i++)
+ {
+ //if (closedset[i])
+ // Con_DPrintf ("reconstruct_path: closedset[%i] = %i\n", i, closedset[i]);
+ proces_list[i] = 0;
+ }
+ proces_list[s] = -1;//-1 means the enemy is the last waypoint
+ s = 1;
+ while (1)
+ {
+ //Con_DPrintf("\nreconstruct_path: current = %i, waypoints[current].came_from = %i\n", current, waypoints[current].came_from);
+ proces_list[s] = current;//blubs, we now add the first waypoint to the path list
+ if (current == start_node)
+ {
+ Con_DPrintf("reconstruct_path: path done!\n");
+ break;
+ }
+ if (CheckIfWayInList (1, waypoints[current].came_from))
+ {
+ //Con_DPrintf("reconstruct_path: waypoints[current].came_from %i is in list!\n", waypoints[current].came_from);
+ for (i = 0;i < 8; i++)
+ {
+ //Con_DPrintf("reconstruct_path for loop: waypoints[waypoints[current].came_from].target_id[i] = %i, current = %i\n", waypoints[waypoints[current].came_from].target_id[i], current)
+ if (waypoints[waypoints[current].came_from].target_id[i] == current)
+ {
+ //Con_DPrintf("reconstruct_path: current %i is viable target!\n", current);
+ current = waypoints[current].came_from;//woohoo, this waypoint is viable. So set it now as the current one
+ break;
+ }
+ }
+ }
+ else
+ {
+ //Con_DPrintf("reconstruct_path: skipped waypoint %i\n", waypoints[current].came_from);
+ break;
+ }
+ s++;
+ }
+ Con_DPrintf("\nreconstruct_path: dumping the final list\n");
+ for (s = MAX_WAYPOINTS - 1; s > -1; s--)
+ {
+ //if (proces_list[s])
+ //Con_DPrintf("reconstruct_path final: s = %i, proces_list[s] = %i\n", s, proces_list[s]);
+ }
+}
+
+int Pathfind (int start_way, int end_way)//note thease 2 are ARRAY locations. Not the waypoints names.
+{
+ int current, last_way;//current is for the waypoint array, last_way is a way that was used last
+ float tentative_g_score, tentative_f_score;
+ int i;
+ last_way = 0;
+ for (i = 0; i < MAX_WAYPOINTS;i++)// clear all the waypoints
+ {
+ openset[i] = 0;
+ opensetRef[i] = 0;
+ closedset[i] = 0;
+ waypoints[i].f_score = 0;
+ waypoints[i].g_score = 0;
+ waypoints[i].came_from = 0;
+ }
+ opensetLength = 0;
+
+ waypoints[start_way].g_score = 0; // Cost from start along best known path.
+ // Estimated total cost from start to goal through y.
+ waypoints[start_way].f_score = waypoints[start_way].g_score + heuristic_cost_estimate(start_way, end_way);
+
+ AddWayToList (2, start_way);// The set of tentative nodes to be evaluated, initially containing the start node
+
+ while (!CheckIfEmptyList (2))
+ {
+ //Con_DPrintf("\n");
+ current = GetLowestFromOpenSet();
+ //Con_DPrintf("Pathfind current: %i, f_score: %f, g_score: %f\n", current, waypoints[current].f_score, waypoints[current].g_score);
+ if (current == end_way)
+ {
+ Con_DPrintf("Pathfind goal reached\n");
+ reconstruct_path(start_way, end_way);
+ return 1;
+ }
+ AddWayToList (1, current);
+ RemoveWayFromList (2, current);
+
+ for (i = 0;i < 8; i++)
+ {
+
+ //Con_DPrintf("Pathfind for start\n");
+
+ if (!waypoints[waypoints[current].target_id[i]].open)
+ {
+ //if (waypoints[current].target_id[i])
+ //Con_DPrintf("Pathfind for: %i, waypoints[waypoints[current].target_id[i]].open = %i, current = %i\n", waypoints[current].target_id[i], waypoints[waypoints[current].target_id[i]].open, current);
+ continue;
+ }
+
+ tentative_g_score = waypoints[current].g_score + waypoints[current].dist[i];
+ tentative_f_score = tentative_g_score + heuristic_cost_estimate(waypoints[current].target_id[i], end_way);
+ //Con_DPrintf("Pathfind for: %i, t_f_score: %f, t_g_score: %f\n", waypoints[current].target_id[i], tentative_f_score, tentative_g_score);
+
+ //if (CheckIfWayInList (1, waypoints[current].target_id[i]) && tentative_f_score >= waypoints[waypoints[current].target_id[i]].f_score)
+ if (CheckIfWayInList (1, waypoints[current].target_id[i]))//it was the above, but why do we care about this waypoint if it's already in the closed list? we never check 2 waypoints twice m8, the first iteration that we reach this waypoint is also the fastest way, so lets not EVER check it again.
+ {
+ //if (CheckIfWayInList (1, waypoints[current].target_id[i]))
+ //Con_DPrintf("Pathfind: waypoint %i in closed list\n", waypoints[current].target_id[i]);
+ continue;
+ }
+
+ if(tentative_f_score < waypoints[waypoints[current].target_id[i]].f_score)
+ {
+ //Con_DPrintf("Pathfind waypoint is better\n");
+ waypoints[waypoints[current].target_id[i]].g_score = tentative_g_score;
+ waypoints[waypoints[current].target_id[i]].f_score = tentative_f_score;
+ }
+
+ if (!CheckIfWayInList (2, waypoints[current].target_id[i]))
+ {
+ //Con_DPrintf("Pathfind waypoint not in list\n");
+ waypoints[waypoints[current].target_id[i]].g_score = tentative_g_score;
+ waypoints[waypoints[current].target_id[i]].f_score = tentative_f_score;
+
+ waypoints[waypoints[current].target_id[i]].came_from = current;
+ AddWayToList (2, waypoints[current].target_id[i]);
+ //Con_DPrintf("Pathfind: %i added to the openset with waypoints[current].came_from = %i, current = %i\n", waypoints[current].target_id[i], waypoints[current].came_from, current);
+ }
+ }
+ last_way = current;
+ }
+ return 0;
+}
+/*
+=================
+Get_Waypoint_Near
+
+vector Get_Waypoint_Near (entity)
+=================
+*/
+void Get_Waypoint_Near (void)
+{
+ float best_dist;
+ float dist;
+ int i, best;
+ trace_t trace;
+ edict_t *ent;
+
+ best = 0;
+ Con_DPrintf("Starting Get_Waypoint_Near\n");
+ ent = G_EDICT(OFS_PARM0);
+ best_dist = 1000000000;
+ dist = 0;
+
+ for (i = 0; i < MAX_WAYPOINTS; i++)
+ {
+ if (waypoints[i].open)
+ {
+ trace = SV_Move (ent->v.origin, vec3_origin, vec3_origin, waypoints[i].origin, 1, ent);
+ dist = VecLength2(waypoints[i].origin, ent->v.origin);
+
+ //Con_DPrintf("Waypoint: %i, distance: %f, fraction: %f\n", i, dist, trace.fraction);
+ if (trace.fraction >= 1)
+ {
+ if(dist < best_dist)
+ {
+ best_dist = dist;
+ best = i;
+ }
+ }
+ }
+ }
+ Con_DPrintf("'%5.1f %5.1f %5.1f', %f is %f, (%i, %i)\n", waypoints[best].origin[0],waypoints[best].origin[1], waypoints[best].origin[2], best_dist, dist, i, best);
+ VectorCopy (waypoints[best].origin, G_VECTOR(OFS_RETURN));
+}
+
+/*
+=================
+Open_Waypoint
+
+void Open_Waypoint (string, string, string, string, string, string, string, string)
+=================
+*/
+void Open_Waypoint (void)
+{
+ int i, t;
+ char *p = G_STRING(OFS_PARM0);
+
+ //Con_DPrintf("Open_Waypoint\n");
+ for (i = 1; i < MAX_WAYPOINTS; i++)
+ {
+ if (waypoints[i].special[0])//no need to open without tag
+ {
+ if (!strcmp(p, waypoints[i].special))
+ {
+ waypoints[i].open = 1;
+ //Con_DPrintf("Open_Waypoint: %i, opened\n", i);
+ t = 1;
+ }
+ else
+ continue;
+ }
+ }
+ //if (t == 0)
+ //{
+ //Con_DPrintf("Open_Waypoint: no waypoints opened\n");
+ //}
+}
+
+/*
+=================
+Close_Waypoint
+
+void Close_Waypoint (string, string, string, string, string, string, string, string)
+
+moto - basically a carbon copy of open_waypoint lol
+=================
+*/
+void Close_Waypoint (void)
+{
+ int i, t;
+ char *p = G_STRING(OFS_PARM0);
+
+ for (i = 1; i < MAX_WAYPOINTS; i++)
+ {
+ if (waypoints[i].special[0])//no need to open without tag
+ {
+ if (!strcmp(p, waypoints[i].special))
+ {
+ waypoints[i].open = 0;
+ t = 1;
+ }
+ else
+ continue;
+ }
+ }
+}
+
+/*
+=================
+Do_Pathfind
+
+float Do_Pathfind (entity zombie, entity target)
+=================
+*/
+void Do_Pathfind (void)
+{
+ float best_dist;
+ float dist;
+ int i, s, best, best_target;
+ trace_t trace;
+ edict_t *ent;
+ edict_t *zombie;
+ int entnum;
+
+ entnum = G_EDICTNUM(OFS_PARM0);
+
+ best = 0;
+ Con_DPrintf("Starting Do_Pathfind\n"); //we first need to look for closest point for both zombie and the player
+ zombie = G_EDICT(OFS_PARM0);
+ ent = G_EDICT(OFS_PARM1);
+
+ best_dist = 1000000000;
+ dist = 0;
+
+ for (i = 0; i < MAX_WAYPOINTS; i++)
+ {
+ if (waypoints[i].used && waypoints[i].open)
+ {
+ trace = SV_Move (zombie->v.origin, vec3_origin, vec3_origin, waypoints[i].origin, 1, zombie);
+ if (trace.fraction >= 1)
+ {
+ dist = VecLength2(waypoints[i].origin, zombie->v.origin);
+
+ if(dist < best_dist)
+ {
+ best_dist = dist;
+ best = i;
+ }
+ }
+ }
+ }
+
+ best_dist = 1000000000;
+ dist = 0;
+ best_target = 0;
+ for (i = 0; i < MAX_WAYPOINTS; i++)
+ {
+ if (waypoints[i].used && waypoints[i].open)
+ {
+ trace = SV_Move (ent->v.origin, vec3_origin, vec3_origin, waypoints[i].origin, 1, ent);
+ if (trace.fraction >= 1)
+ {
+ dist = VecLength2(waypoints[i].origin, ent->v.origin);
+
+ if(dist < best_dist)
+ {
+ best_dist = dist;
+ best_target = i;
+ }
+ }
+ }
+ }
+ Con_DPrintf("Starting waypoint: %i, Ending waypoint: %i\n", best, best_target);
+ if (Pathfind(best, best_target))
+ {
+ for (i = 0; i < MaxZombies; i++)
+ {
+ if (entnum == zombie_list[i].zombienum)
+ {
+ for (s = 0; s < MAX_WAYPOINTS; s++)
+ {
+ zombie_list[i].pathlist[s] = proces_list[s];
+ }
+ break;
+ }
+ if (i == MaxZombies - 1)//zombie was not in list
+ {
+ for (i = 0; i < MaxZombies; i++)
+ {
+ if (!zombie_list[i].zombienum)
+ {
+ zombie_list[i].zombienum = entnum;
+ for (s = 0; s < MAX_WAYPOINTS; s++)
+ {
+ zombie_list[i].pathlist[s] = proces_list[s];
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if(zombie_list[i].pathlist[2] == 0 && zombie_list[i].pathlist[1] != 0)//then we are at player's waypoint!
+ {
+ Con_DPrintf("We are at player's waypoint already!\n");
+ G_FLOAT(OFS_RETURN) = -1;
+ return;
+ }
+
+ Con_DPrintf("Path found!\n");
+ G_FLOAT(OFS_RETURN) = 1;
+ }
+ else
+ {
+ Con_DPrintf("Path not found!\n");
+ G_FLOAT(OFS_RETURN) = 0;
+ }
+}
+
+
+/*
+=================
+Get_Next_Waypoint This function will return the next waypoint in zombies path and then remove it from the list
+
+vector Get_Next_Waypoint (entity)
+=================
+*/
+void Get_Next_Waypoint (void)
+{
+ int i, s;
+ s = 0;//useless initialize, because compiler likes to yell at me
+ int entnum;
+ edict_t *ent;//blubs added
+ vec3_t move;
+ float *start,*mins, *maxs;
+ int currentWay = 0;
+ //int zomb = 0;
+ int skippedWays = 0;
+
+ move [0] = 0;
+ move [1] = 0;
+ move [2] = 0;
+
+ entnum = G_EDICTNUM(OFS_PARM0);
+ ent = G_EDICT(OFS_PARM0);//blubsadded
+ start = G_VECTOR(OFS_PARM1);
+ mins = G_VECTOR(OFS_PARM2);
+ maxs = G_VECTOR(OFS_PARM3);
+
+ mins[0] -= 2;
+ mins[1] -= 2;
+
+ maxs[0] += 2;
+ maxs[1] += 2;
+
+
+ for (i = 0; i < MaxZombies; i++)
+ {
+ if (entnum == zombie_list[i].zombienum)
+ {
+ for (s = MAX_WAYPOINTS - 1; s > -1; s--)
+ {
+ if (zombie_list[i].pathlist[s])
+ {
+ zombie_list[i].pathlist[s] = 0;//This is get_next, so remove our current waypoint from the list.
+
+ if(s == 1)
+ {
+ VectorCopy (move, G_VECTOR(OFS_RETURN));//we are at our last waypoint, so just return 0,0,0, this should never happen anyways, because we'll make pathfind return something else
+ Con_Printf("Warning, only one waypoint in path!\n");
+ return;
+ }
+ s-= 1;
+ currentWay = s;//We want the next waypoint
+ break;
+ }
+ }
+ break;
+ }
+ }
+ //s is the index in our path, so if s == 1
+
+ if(s == -1 || s == 0)
+ {
+ //-1?
+ //then that means only player was in path, this is just in case...
+ //we are at our last waypoint, so just return 0,0,0, this should never happen anyways, because we'll make pathfind return something else
+ //0?
+ //only 1 waypoint left in path, we can't possibly smooth the path in this scenario.
+ //next waypoint in any case is going to be player, so....
+ VectorCopy (move, G_VECTOR(OFS_RETURN));
+ return;
+ }
+
+ int iterations = 5;//that's how many segments per waypoint, pretty important number
+ float Scale = 0.5;
+ float curScale = 1;
+ float Scalar = Scale;
+ float TraceResult;
+ vec3_t toAdd;
+ vec3_t curStart;
+ vec3_t temp;
+ int q;
+ VectorCopy(waypoints[zombie_list[i].pathlist[currentWay]].origin,temp);
+ VectorCopy(temp,move);
+
+ while(1)
+ {
+ //Con_Printf("Main Vector Start: %f, %f, %f Vector End: %f, %f, %f\n",start[0],start[1],start[2],waypoints[zombie_list[i].pathlist[currentWay]].origin[0],waypoints[zombie_list[i].pathlist[currentWay]].origin[1],waypoints[zombie_list[i].pathlist[currentWay]].origin[2]);
+ TraceResult = TraceMove(start,mins,maxs,waypoints[zombie_list[i].pathlist[currentWay]].origin,MOVE_NOMONSTERS,ent);
+ if(TraceResult == 1)
+ {
+ VectorCopy(waypoints[zombie_list[i].pathlist[currentWay]].origin,move);
+ if(currentWay == 1)//we're at the end of the list, we better not go out of bounds, was 0, now 1, since 0 is for player index
+ {
+ break;
+ }
+ currentWay -= 1;
+ skippedWays += 1;
+ }
+ else
+ {
+ if(skippedWays > 0)
+ {
+ VectorCopy(waypoints[zombie_list[i].pathlist[currentWay + 1]].origin,temp);
+ VectorCopy(temp,curStart);
+ VectorSubtract(waypoints[zombie_list[i].pathlist[currentWay]].origin,curStart,toAdd);
+ for(q = 0;q < iterations; q++)
+ {
+ curScale *= Scalar;
+ VectorScale(toAdd,curScale,temp);
+ VectorAdd(temp,curStart,temp);
+ TraceResult = TraceMove(start,mins,maxs,temp,MOVE_NOMONSTERS,ent);
+ if(TraceResult ==1)
+ {
+ Scalar = Scale + 1;
+ VectorCopy(temp,move);
+ }
+ else
+ {
+ Scalar = Scale;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ Con_DPrintf("Get Next Way returns: list[%i], waypoint:%i\n",s,waypoints[zombie_list[i].pathlist[s]]);
+
+ //VectorCopy(waypoints[zombie_list[i].pathlist[s]].origin,move); //for old get_next_way
+ zombie_list[i].pathlist[s] = 0;
+
+ //Con_Printf("Skipped %i waypoints, we're moving to the %f percentage in between 2 waypoints\n",skippedWays,curScale);
+ //Con_DPrintf("'%5.1f %5.1f %5.1f'\n", move[0], move[1], move[2]);
+ VectorCopy (move, G_VECTOR(OFS_RETURN));
+}
+/*
+=================
+Get_First_Waypoint This function will return the waypoint waypoint in zombies path and then remove it from the list
+
+vector Get_First_Waypoint (entity)
+=================
+*/
+void Get_First_Waypoint (void)
+{
+ int i, s;
+ s = 0;//useless initialize, because compiler likes to yell at me
+ int entnum;
+ edict_t *ent;//blubs added
+ vec3_t move;
+ float *start,*mins, *maxs;
+ int currentWay = 0;
+ //int zomb = 0;
+ int skippedWays = 0;
+
+ move [0] = 0;
+ move [1] = 0;
+ move [2] = 0;
+
+ entnum = G_EDICTNUM(OFS_PARM0);
+ ent = G_EDICT(OFS_PARM0);//blubsadded
+ start = G_VECTOR(OFS_PARM1);
+ mins = G_VECTOR(OFS_PARM2);
+ maxs = G_VECTOR(OFS_PARM3);
+
+ mins[0] -= 2;
+ mins[1] -= 2;
+
+ maxs[0] += 2;
+ maxs[1] += 2;
+
+
+ for (i = 0; i < MaxZombies; i++)
+ {
+ if (entnum == zombie_list[i].zombienum)
+ {
+ for (s = MAX_WAYPOINTS - 1; s > -1; s--)
+ {
+ if (zombie_list[i].pathlist[s])
+ {
+ currentWay = s;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if(s == 0)
+ {
+ //0?
+ //currentway is player, just return world
+ VectorCopy (move, G_VECTOR(OFS_RETURN));
+ return;
+ }
+ //1? only one way in list, we can't possibly smooth list when we only have one...
+
+ int iterations = 5;//that's how many segments per waypoint, pretty important number
+ float Scale = 0.5;
+ float curScale = 1;
+ float Scalar = Scale;
+ float TraceResult;
+ vec3_t toAdd;
+ vec3_t curStart;
+ vec3_t temp;
+ int q;
+ VectorCopy(waypoints[zombie_list[i].pathlist[currentWay]].origin,temp);
+ VectorCopy(temp,move);
+
+ while(1)
+ {
+ //Con_Printf("Main Vector Start: %f, %f, %f Vector End: %f, %f, %f\n",start[0],start[1],start[2],waypoints[zombie_list[i].pathlist[currentWay]].origin[0],waypoints[zombie_list[i].pathlist[currentWay]].origin[1],waypoints[zombie_list[i].pathlist[currentWay]].origin[2]);
+ TraceResult = TraceMove(start,mins,maxs,waypoints[zombie_list[i].pathlist[currentWay]].origin,MOVE_NOMONSTERS,ent);
+ if(TraceResult == 1)
+ {
+ VectorCopy(waypoints[zombie_list[i].pathlist[currentWay]].origin,move);
+ if(currentWay == 1)//we're at the end of the list, we better not go out of bounds//was 0, now 1 since 0 is for enemy
+ {
+ break;
+ }
+ currentWay -= 1;
+ skippedWays += 1;
+ }
+ else
+ {
+ if(skippedWays > 0)
+ {
+ VectorCopy(waypoints[zombie_list[i].pathlist[currentWay + 1]].origin,temp);
+ VectorCopy(temp,curStart);
+ VectorSubtract(waypoints[zombie_list[i].pathlist[currentWay]].origin,curStart,toAdd);
+ for(q = 0;q < iterations; q++)
+ {
+ curScale *= Scalar;
+ VectorScale(toAdd,curScale,temp);
+ VectorAdd(temp,curStart,temp);
+ //Con_Printf("subVector Start: %f, %f, %f Vector End: %f, %f, %f\n",start[0],start[1],start[2],temp[0],temp[1],temp[2]);
+ TraceResult = TraceMove(start,mins,maxs,temp,MOVE_NOMONSTERS,ent);
+ if(TraceResult ==1)
+ {
+ Scalar = Scale + 1;
+ VectorCopy(temp,move);
+ }
+ else
+ {
+ Scalar = Scale;
+ }//we need a way to go back to the other value if it doesn't work!, so lets work with temp, but RETURN a different value other than temp!
+ }
+ }
+ break;
+ }
+ }
+
+ Con_DPrintf("Get First Way returns: %i\n",s);
+ //VectorCopy(waypoints[zombie_list[i].pathlist[s]].origin,move);//for old get_first_way
+ zombie_list[i].pathlist[s] = 0;
+ //Con_Printf("Skipped %i waypoints, we're moving to the %f percentage in between 2 waypoints\n",skippedWays,curScale);
+ //Con_DPrintf("'%5.1f %5.1f %5.1f'\n", move[0], move[1], move[2]);
+ VectorCopy (move, G_VECTOR(OFS_RETURN));
+}
+
+
+// 2001-09-20 QuakeC file access by FrikaC/Maddes start
+/*
+=================
+PF_fopen
+
+float fopen (string,float)
+=================
+*/
+void PF_fopen (void)
+{
+ char *p = G_STRING(OFS_PARM0);
+ char *ftemp;
+ int fmode = G_FLOAT(OFS_PARM1);
+ int h = 0, fsize = 0;
+
+ switch (fmode)
+ {
+ case 0: // read
+ Sys_FileOpenRead (va("%s/%s",com_gamedir, p), &h);
+ G_FLOAT(OFS_RETURN) = (float) h;
+ return;
+ case 1: // append -- this is nasty
+ // copy whole file into the zone
+ fsize = Sys_FileOpenRead(va("%s/%s",com_gamedir, p), &h);
+ if (h == -1)
+ {
+ h = Sys_FileOpenWrite(va("%s/%s",com_gamedir, p));
+ G_FLOAT(OFS_RETURN) = (float) h;
+ return;
+ }
+ ftemp = Z_Malloc(fsize + 1);
+ Sys_FileRead(h, ftemp, fsize);
+ Sys_FileClose(h);
+ // spit it back out
+ h = Sys_FileOpenWrite(va("%s/%s",com_gamedir, p));
+ Sys_FileWrite(h, ftemp, fsize);
+ Z_Free(ftemp); // free it from memory
+ G_FLOAT(OFS_RETURN) = (float) h; // return still open handle
+ return;
+ default: // write
+ h = Sys_FileOpenWrite (va("%s/%s", com_gamedir, p));
+ G_FLOAT(OFS_RETURN) = (float) h;
+ return;
+ }
+}
+
+/*
+=================
+PF_fclose
+
+void fclose (float)
+=================
+*/
+void PF_fclose (void)
+{
+ int h = (int)G_FLOAT(OFS_PARM0);
+ Sys_FileClose(h);
+}
+
+/*
+=================
+PF_fgets
+
+string fgets (float)
+=================
+*/
+void PF_fgets (void)
+{
+ // reads one line (up to a \n) into a string
+ int h;
+ int i;
+ int count;
+ char buffer;
+
+ h = (int)G_FLOAT(OFS_PARM0);
+
+ count = Sys_FileRead(h, &buffer, 1);
+ if (count && buffer == '\r') // carriage return
+ {
+ count = Sys_FileRead(h, &buffer, 1); // skip
+ }
+ if (!count) // EndOfFile
+ {
+ G_INT(OFS_RETURN) = OFS_NULL; // void string
+ return;
+ }
+
+ i = 0;
+ while (count && buffer != '\n')
+ {
+ if (i < PR_MAX_TEMPSTRING-1) // no place for character in temp string
+ {
+ pr_string_temp[i++] = buffer;
+ }
+
+ // read next character
+ count = Sys_FileRead(h, &buffer, 1);
+ if (count && buffer == '\r') // carriage return
+ {
+ count = Sys_FileRead(h, &buffer, 1); // skip
+ }
+ };
+ pr_string_temp[i] = 0;
+
+ G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+
+/*
+=================
+PF_fputs
+
+void fputs (float,string)
+=================
+*/
+void PF_fputs (void)
+{
+ // writes to file, like bprint
+ float handle = G_FLOAT(OFS_PARM0);
+ char *str = PF_VarString(1);
+ Sys_FileWrite (handle, str, strlen(str));
+}
+// 2001-09-20 QuakeC file access by FrikaC/Maddes end
+
+// entity (entity start, .string field, string match) find = #5;
+void PF_Find (void)
+{
+ int e;
+ int f;
+ char *s, *t;
+ edict_t *ed;
+
+ e = G_EDICTNUM(OFS_PARM0);
+ f = G_INT(OFS_PARM1);
+ s = G_STRING(OFS_PARM2);
+ if (!s)
+ PR_RunError ("PF_Find: bad search string");
+
+ for (e++ ; e < sv.num_edicts ; e++)
+ {
+ ed = EDICT_NUM(e);
+ if (ed->free)
+ continue;
+ t = E_STRING(ed,f);
+ if (!t)
+ continue;
+ if (!strcmp(t,s))
+ {
+ RETURN_EDICT(ed);
+ return;
+ }
+ }
+
+ RETURN_EDICT(sv.edicts);
+}
+
+// entity (entity start, .float field, float match) findfloat = #98;
+void PF_FindFloat (void)
+{
+ int e;
+ int f;
+ float s, t;
+ edict_t *ed;
+
+ e = G_EDICTNUM(OFS_PARM0);
+ f = G_INT(OFS_PARM1);
+ s = G_FLOAT(OFS_PARM2);
+ if (!s)
+ PR_RunError ("PF_FindFloat: bad search float");
+
+ for (e++ ; e < sv.num_edicts ; e++)
+ {
+ ed = EDICT_NUM(e);
+ if (ed->free)
+ continue;
+ t = E_FLOAT(ed,f);
+ if (!t)
+ continue;
+ if (t == s)
+ {
+ RETURN_EDICT(ed);
+ return;
+ }
+ }
+
+ RETURN_EDICT(sv.edicts);
+}
+
+void PR_CheckEmptyString (char *s)
+{
+ if (s[0] <= ' ')
+ PR_RunError ("Bad string");
+}
+
+void PF_precache_file (void)
+{ // precache_file is only used to copy files with qcc, it does nothing
+ G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
+}
+
+void PF_precache_sound (void)
+{
+ char *s;
+ int i;
+
+ if (sv.state != ss_loading)
+ PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions");
+
+ s = G_STRING(OFS_PARM0);
+ G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
+ PR_CheckEmptyString (s);
+
+ for (i=0 ; iself);
+ yaw = G_FLOAT(OFS_PARM0);
+ dist = G_FLOAT(OFS_PARM1);
+
+ if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
+ {
+ G_FLOAT(OFS_RETURN) = 0;
+ return;
+ }
+
+ yaw = yaw*M_PI*2 / 360;
+
+ move[0] = cosf(yaw)*dist;
+ move[1] = sinf(yaw)*dist;
+ move[2] = 0;
+
+// save program state, because SV_movestep may call other progs
+ oldf = pr_xfunction;
+ oldself = pr_global_struct->self;
+
+ G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true);
+
+ //if(!strcmp(pr_strings + ent->v.classname, "ai_zombie"))
+ //{
+ // VectorCopy(ent->v.origin,PROG_TO_EDICT(ent->v.head)->v.origin);
+ // VectorCopy(ent->v.origin,PROG_TO_EDICT(ent->v.rarm)->v.origin);
+ // VectorCopy(ent->v.origin,PROG_TO_EDICT(ent->v.larm)->v.origin);
+ //}
+
+// restore program state
+ pr_xfunction = oldf;
+ pr_global_struct->self = oldself;
+}
+
+/*
+===============
+PF_droptofloor
+
+void() droptofloor
+===============
+*/
+void PF_droptofloor (void)
+{
+ edict_t *ent;
+ vec3_t end;
+ trace_t trace;
+
+ ent = PROG_TO_EDICT(pr_global_struct->self);
+
+ VectorCopy (ent->v.origin, end);
+ end[2] -= 256;
+
+ trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent);
+
+ if (trace.fraction == 1 || trace.allsolid)
+ G_FLOAT(OFS_RETURN) = 0;
+ else
+ {
+ VectorCopy (trace.endpos, ent->v.origin);
+ SV_LinkEdict (ent, false);
+ ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
+ ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+ G_FLOAT(OFS_RETURN) = 1;
+ }
+}
+
+/*
+===============
+PF_lightstyle
+
+void(float style, string value) lightstyle
+===============
+*/
+void PF_lightstyle (void)
+{
+ int style;
+ char *val;
+ client_t *client;
+ int j;
+
+ style = G_FLOAT(OFS_PARM0);
+ val = G_STRING(OFS_PARM1);
+
+// change the string in sv
+ sv.lightstyles[style] = val;
+
+// send message to all clients on this server
+ if (sv.state != ss_active)
+ return;
+
+ for (j=0, client = svs.clients ; jactive || client->spawned)
+ {
+ MSG_WriteChar (&client->message, svc_lightstyle);
+ MSG_WriteChar (&client->message,style);
+ MSG_WriteString (&client->message, val);
+ }
+}
+
+void PF_rint (void)
+{
+ float f;
+ f = G_FLOAT(OFS_PARM0);
+ if (f > 0)
+ G_FLOAT(OFS_RETURN) = (int)(f + 0.5);
+ else
+ G_FLOAT(OFS_RETURN) = (int)(f - 0.5);
+}
+void PF_floor (void)
+{
+ G_FLOAT(OFS_RETURN) = floorf(G_FLOAT(OFS_PARM0));
+}
+void PF_ceil (void)
+{
+ G_FLOAT(OFS_RETURN) = ceilf(G_FLOAT(OFS_PARM0));
+}
+
+
+/*
+=============
+PF_checkbottom
+=============
+*/
+void PF_checkbottom (void)
+{
+ edict_t *ent;
+
+ ent = G_EDICT(OFS_PARM0);
+
+ G_FLOAT(OFS_RETURN) = SV_CheckBottom (ent);
+}
+
+/*
+=============
+PF_pointcontents
+=============
+*/
+void PF_pointcontents (void)
+{
+ float *v;
+
+ v = G_VECTOR(OFS_PARM0);
+
+ G_FLOAT(OFS_RETURN) = SV_PointContents (v);
+}
+
+/*
+=============
+PF_nextent
+
+entity nextent(entity)
+=============
+*/
+void PF_nextent (void)
+{
+ int i;
+ edict_t *ent;
+
+ i = G_EDICTNUM(OFS_PARM0);
+ while (1)
+ {
+ i++;
+ if (i == sv.num_edicts)
+ {
+ RETURN_EDICT(sv.edicts);
+ return;
+ }
+ ent = EDICT_NUM(i);
+ if (!ent->free)
+ {
+ RETURN_EDICT(ent);
+ return;
+ }
+ }
+}
+
+/*
+=============
+PF_aim
+
+Pick a vector for the player to shoot along
+vector aim(entity, missilespeed)
+=============
+*/
+cvar_t sv_aim = {"sv_aim", "0.93"};
+void PF_aim (void)
+{
+ edict_t *ent, *check, *bestent;
+ vec3_t start, dir, end, bestdir, tempv;
+ int i, j;
+ trace_t tr;
+ float dist, bestdist;
+ float speed;
+
+ ent = G_EDICT(OFS_PARM0);
+ speed = G_FLOAT(OFS_PARM1);
+ VectorAdd(ent->v.origin, ent->v.view_ofs, tempv);
+ VectorCopy (tempv, start);
+ start[2] += 20;
+
+// try sending a trace straight
+ VectorCopy (pr_global_struct->v_forward, dir);
+ VectorMA (start, 2048, dir, end);
+ tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent);
+ if (tr.ent && tr.ent->v.takedamage == DAMAGE_AIM
+ && (!teamplay.value || ent->v.team <=0 || ent->v.team != tr.ent->v.team) )
+ {
+ VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN));
+ return;
+ }
+
+
+// try all possible entities
+ VectorCopy (dir, bestdir);
+ bestdist = sv_aim.value;
+ // dr_mabuse1981: PSP controls sucks ass
+ bestent = NULL;
+
+ check = NEXT_EDICT(sv.edicts);
+ for (i=1 ; iv.takedamage != DAMAGE_AIM)
+ continue;
+ if (check == ent)
+ continue;
+ if (teamplay.value && ent->v.team > 0 && ent->v.team == check->v.team)
+ continue; // don't aim at teammate
+ for (j=0 ; j<3 ; j++)
+ end[j] = check->v.origin[j]
+ + 0.5*(check->v.mins[j] + check->v.maxs[j]);
+ VectorSubtract (end, start, dir);
+ VectorNormalize (dir);
+ dist = DotProduct (dir, pr_global_struct->v_forward);
+ if (dist < bestdist)
+ continue; // to far to turn
+ tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent);
+ if (tr.ent == check)
+ { // can shoot at this one
+ bestdist = dist;
+ bestent = check;
+ }
+ }
+
+ if (bestent)
+ {
+ VectorSubtract (bestent->v.origin, ent->v.origin, dir);
+// dr_mabuse1981: bullets shouldnt go offset anymore (fix from Quartal engine)
+// dist = DotProduct (dir, pr_global_struct->v_forward);
+// VectorScale (pr_global_struct->v_forward, dist, end);
+// end[2] = dir[2];
+ end[0] = dir[0];
+ end[1] = dir[1];
+ end[2] = dir[2];
+ VectorNormalize (end);
+ VectorCopy (end, G_VECTOR(OFS_RETURN));
+ }
+ else
+ {
+ VectorCopy (bestdir, G_VECTOR(OFS_RETURN));
+ }
+}
+
+/*
+==============
+PF_changeyaw
+
+This was a major timewaster in progs, so it was converted to C
+==============
+*/
+void PF_changeyaw (void)
+{
+ edict_t *ent;
+ float ideal, current, move, speed;
+
+ ent = PROG_TO_EDICT(pr_global_struct->self);
+ current = anglemod( ent->v.angles[1] );
+ ideal = ent->v.ideal_yaw;
+ speed = ent->v.yaw_speed;
+
+ if (current == ideal)
+ return;
+ move = ideal - current;
+ if (ideal > current)
+ {
+ if (move >= 180)
+ move = move - 360;
+ }
+ else
+ {
+ if (move <= -180)
+ move = move + 360;
+ }
+ if (move > 0)
+ {
+ if (move > speed)
+ move = speed;
+ }
+ else
+ {
+ if (move < -speed)
+ move = -speed;
+ }
+
+ ent->v.angles[1] = anglemod (current + move);
+}
+
+/*
+==============
+PF_GetSoundLen
+
+Get the lenght of the sound (useful for things like radio)
+==============
+*/
+void PF_GetSoundLen (void)
+{
+
+ char *name;
+
+ name = G_STRING(OFS_PARM0);
+
+ char namebuffer[256];
+ byte *data;
+ wavinfo_t info;
+ byte stackbuf[1*1024]; // avoid dirtying the cache heap
+
+//Con_Printf ("S_LoadSound: %x\n", (int)stackbuf);
+// load it in
+ Q_strcpy(namebuffer, "");
+ Q_strcat(namebuffer, name);
+
+ data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf));
+
+ if (!data)
+ {
+ Con_Printf ("Couldn't load %s\n", namebuffer);
+ G_FLOAT(OFS_RETURN) = -1;
+ return;
+ }
+
+ info = GetWavinfo (name, data, com_filesize);
+ if (info.channels != 1)
+ {
+ Con_Printf ("%s is a stereo sample\n",name);
+ G_FLOAT(OFS_RETURN) = -1;
+ return;
+ }
+
+ G_FLOAT(OFS_RETURN) = (float)info.samples/(float)info.rate;
+}
+
+/*
+==============
+PF_changepitch
+==============
+*/
+void PF_changepitch (void)
+{
+ float ideal, current, move, speed;
+ edict_t *ent;
+ eval_t *val;
+
+ ent = G_EDICT(OFS_PARM0);
+ current = anglemod(ent->v.angles[0]);
+ if ((val = GETEDICTFIELDVALUE(ent, eval_idealpitch)))
+ {
+ ideal = val->_float;
+ }
+ else
+ {
+ PR_RunError ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch");
+ return;
+ }
+
+ if ((val = GETEDICTFIELDVALUE(ent, eval_pitch_speed)))
+ {
+ speed = val->_float;
+ }
+ else
+ {
+ PR_RunError ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch");
+ return;
+ }
+
+ if (current == ideal)
+ return;
+
+ move = ideal - current;
+ if (ideal > current)
+ {
+ if (move >= 180)
+ move = move - 360;
+ }
+ else
+ {
+ if (move <= -180)
+ move = move + 360;
+ }
+
+ if (move > 0)
+ {
+ if (move > speed)
+ move = speed;
+ }
+ else
+ {
+ if (move < -speed)
+ move = -speed;
+ }
+
+ ent->v.angles[0] = anglemod (current + move);
+}
+
+/*
+===============================================================================
+
+MESSAGE WRITING
+
+===============================================================================
+*/
+
+#define MSG_BROADCAST 0 // unreliable to all
+#define MSG_ONE 1 // reliable to one (msg_entity)
+#define MSG_ALL 2 // reliable to all
+#define MSG_INIT 3 // write to the init string
+
+sizebuf_t *WriteDest (void)
+{
+ int entnum;
+ int dest;
+ edict_t *ent;
+
+ dest = G_FLOAT(OFS_PARM0);
+ switch (dest)
+ {
+ case MSG_BROADCAST:
+ return &sv.datagram;
+
+ case MSG_ONE:
+ ent = PROG_TO_EDICT(pr_global_struct->msg_entity);
+ entnum = NUM_FOR_EDICT(ent);
+ if (entnum < 1 || entnum > svs.maxclients)
+ PR_RunError ("WriteDest: not a client");
+ return &svs.clients[entnum-1].message;
+
+ case MSG_ALL:
+ return &sv.reliable_datagram;
+
+ case MSG_INIT:
+ return &sv.signon;
+
+ default:
+ PR_RunError ("WriteDest: bad destination");
+ break;
+ }
+
+ return NULL;
+}
+
+void PF_WriteByte (void)
+{
+ MSG_WriteByte (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteChar (void)
+{
+ MSG_WriteChar (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteShort (void)
+{
+ MSG_WriteShort (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteLong (void)
+{
+ MSG_WriteLong (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteAngle (void)
+{
+ MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteCoord (void)
+{
+ MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteString (void)
+{
+ MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1));
+}
+
+
+void PF_WriteEntity (void)
+{
+ MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1));
+}
+
+//=============================================================================
+
+int SV_ModelIndex (char *name);
+
+void PF_makestatic (void)
+{
+ edict_t *ent;
+ int i;
+
+ ent = G_EDICT(OFS_PARM0);
+
+ MSG_WriteByte (&sv.signon,svc_spawnstatic);
+
+ MSG_WriteByte (&sv.signon, SV_ModelIndex(pr_strings + ent->v.model));
+
+ MSG_WriteByte (&sv.signon, ent->v.frame);
+ MSG_WriteByte (&sv.signon, ent->v.colormap);
+ MSG_WriteByte (&sv.signon, ent->v.skin);
+ for (i=0 ; i<3 ; i++)
+ {
+ MSG_WriteCoord(&sv.signon, ent->v.origin[i]);
+ MSG_WriteAngle(&sv.signon, ent->v.angles[i]);
+ }
+
+// throw the entity away now
+ ED_Free (ent);
+}
+
+//=============================================================================
+
+/*
+==============
+PF_setspawnparms
+==============
+*/
+void PF_setspawnparms (void)
+{
+ edict_t *ent;
+ int i;
+ client_t *client;
+
+ ent = G_EDICT(OFS_PARM0);
+ i = NUM_FOR_EDICT(ent);
+ if (i < 1 || i > svs.maxclients)
+ PR_RunError ("Entity is not a client");
+
+ // copy spawn parms out of the client_t
+ client = svs.clients + (i-1);
+
+ for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
+ (&pr_global_struct->parm1)[i] = client->spawn_parms[i];
+}
+
+/*
+==============
+PF_changelevel
+==============
+*/
+void PF_changelevel (void)
+{
+ char *s;
+
+// make sure we don't issue two changelevels
+ if (svs.changelevel_issued)
+ return;
+ svs.changelevel_issued = true;
+
+ s = G_STRING(OFS_PARM0);
+ Cbuf_AddText (va("changelevel %s\n",s));
+}
+
+
+
+void PF_sin (void)
+{
+ G_FLOAT(OFS_RETURN) = sinf(G_FLOAT(OFS_PARM0));
+}
+
+void PF_cos (void)
+{
+ G_FLOAT(OFS_RETURN) = cosf(G_FLOAT(OFS_PARM0));
+}
+
+void PF_sqrt (void)
+{
+ G_FLOAT(OFS_RETURN) = sqrtf(G_FLOAT(OFS_PARM0));
+}
+
+void PF_Fixme (void)
+{
+ //PR_RunError ("unimplemented bulitin");
+ Con_DPrintf("unimplemented bulitin");
+}
+
+/*
+=================
+PF_SongEgg
+
+plays designated easter egg track
+
+songegg(trackname)
+=================
+*/
+void PF_SongEgg (void)
+{
+ char trackname;
+
+ trackname = G_STRING(OFS_PARM0);
+
+ MSG_WriteByte (&sv.reliable_datagram, svc_songegg);
+ MSG_WriteString (&sv.reliable_datagram, trackname);
+}
+
+/*
+=================
+PF_MaxAmmo
+
+activates max ammo text in HUD
+
+nzp_maxammo()
+=================
+*/
+void PF_MaxAmmo(void)
+{
+ MSG_WriteByte(&sv.reliable_datagram, svc_maxammo);
+}
+
+
+/*
+=================
+PF_achievement
+
+unlocks the achievement number for entity
+
+achievement(clientent, value)
+=================
+*/
+void PF_achievement (void)
+{
+ int ach;
+ client_t *client;
+ int entnum;
+
+ entnum = G_EDICTNUM(OFS_PARM0);
+ ach = G_FLOAT(OFS_PARM1);
+
+ if (entnum < 1 || entnum > svs.maxclients)
+ {
+ Con_DPrintf ("tried to unlock ach to a non-client\n");
+ return;
+ }
+
+ //Con_Printf (va("Achievement? %i\n", ach)); // JPG
+ client = &svs.clients[entnum-1];
+
+ MSG_WriteByte (&client->message,svc_achievement);
+ MSG_WriteByte (&client->message, ach);
+}
+
+
+/*
+=================
+PF_updateLimb
+
+updates zombies limb
+
+PF_updateLimb(zombieent, value. limbent)
+=================
+*/
+void PF_updateLimb (void)
+{
+ int limb;
+ int zombieent, limbent;
+
+ zombieent = G_EDICTNUM(OFS_PARM0);
+ limb = G_FLOAT(OFS_PARM1);
+ limbent = G_EDICTNUM(OFS_PARM2);
+ MSG_WriteByte (&sv.reliable_datagram, svc_limbupdate);
+ MSG_WriteByte (&sv.reliable_datagram, limb);
+ MSG_WriteShort (&sv.reliable_datagram, zombieent);
+ MSG_WriteShort (&sv.reliable_datagram, limbent);
+}
+
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start
+/*
+=================
+PF_builtin_find
+
+float builtin_find (string)
+=================
+*/
+void PF_builtin_find (void)
+{
+ int j;
+ float funcno;
+ char *funcname;
+
+ funcno = 0;
+ funcname = G_STRING(OFS_PARM0);
+
+ // search function name
+ for ( j=1 ; j < pr_ebfs_numbuiltins ; j++)
+ {
+ if ((pr_ebfs_builtins[j].funcname) && (!(Q_strcasecmp(funcname,pr_ebfs_builtins[j].funcname))))
+ {
+ break; // found
+ }
+ }
+
+ if (j < pr_ebfs_numbuiltins)
+ {
+ funcno = pr_ebfs_builtins[j].funcno;
+ }
+
+ G_FLOAT(OFS_RETURN) = funcno;
+}
+
+/*
+=================
+PF_tokenize
+
+float tokenize (string) = #441
+=================
+*/
+//KRIMZON_SV_PARSECLIENTCOMMAND added both of these
+// refined to work on psp on 2017-DEC-09
+void PF_tokenize (void)
+{
+ char *m = G_STRING(OFS_PARM0);
+ Cmd_TokenizeString(m);
+ G_FLOAT(OFS_RETURN) = Cmd_Argc();
+};
+
+/*
+=================
+PF_argv
+
+string argv (float num) = #442
+=================
+*/
+void PF_ArgV (void)
+{
+ char tempc[256];
+ strcpy(tempc, Cmd_Argv(G_FLOAT(OFS_PARM0)));
+ G_INT(OFS_RETURN) = tempc - pr_strings;
+}
+
+
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end
+builtin_t *pr_builtins;
+int pr_numbuiltins;
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start
+// for builtin function definitions see Quake Standards Group at http://www.quakesrc.org/
+ebfs_builtin_t pr_ebfs_builtins[] =
+{
+ { 0, NULL, PF_Fixme }, // has to be first entry as it is needed for initialization in PR_LoadProgs()
+ { 1, "makevectors", PF_makevectors }, // void(entity e) makevectors = #1;
+ { 2, "setorigin", PF_setorigin }, // void(entity e, vector o) setorigin = #2;
+ { 3, "setmodel", PF_setmodel }, // void(entity e, string m) setmodel = #3;
+ { 4, "setsize", PF_setsize }, // void(entity e, vector min, vector max) setsize = #4;
+// { 5, "fixme", PF_Fixme }, // void(entity e, vector min, vector max) setabssize = #5;
+ { 6, "break", PF_break }, // void() break = #6;
+ { 7, "random", PF_random }, // float() random = #7;
+ { 8, "sound", PF_sound }, // void(entity e, float chan, string samp) sound = #8;
+ { 9, "normalize", PF_normalize }, // vector(vector v) normalize = #9;
+ { 10, "error", PF_error }, // void(string e) error = #10;
+ { 11, "objerror", PF_objerror }, // void(string e) objerror = #11;
+ { 12, "vlen", PF_vlen }, // float(vector v) vlen = #12;
+ { 13, "vectoyaw", PF_vectoyaw }, // float(vector v) vectoyaw = #13;
+ { 14, "spawn", PF_Spawn }, // entity() spawn = #14;
+ { 15, "remove", PF_Remove }, // void(entity e) remove = #15;
+ { 16, "traceline", PF_traceline }, // float(vector v1, vector v2, float tryents) traceline = #16;
+ { 17, "checkclient", PF_checkclient }, // entity() clientlist = #17;
+ { 18, "find", PF_Find }, // entity(entity start, .string fld, string match) find = #18;
+ { 19, "precache_sound", PF_precache_sound }, // void(string s) precache_sound = #19;
+ { 20, "precache_model", PF_precache_model }, // void(string s) precache_model = #20;
+ { 21, "stuffcmd", PF_stuffcmd }, // void(entity client, string s)stuffcmd = #21;
+ { 22, "findradius", PF_findradius }, // entity(vector org, float rad) findradius = #22;
+ { 23, "bprint", PF_bprint }, // void(string s) bprint = #23;
+ { 24, "sprint", PF_sprint }, // void(entity client, string s) sprint = #24;
+ { 25, "dprint", PF_dprint }, // void(string s) dprint = #25;
+ { 26, "ftos", PF_ftos }, // void(string s) ftos = #26;
+ { 27, "vtos", PF_vtos }, // void(string s) vtos = #27;
+ { 28, "coredump", PF_coredump },
+ { 29, "traceon", PF_traceon },
+ { 30, "traceoff", PF_traceoff },
+ { 31, "eprint", PF_eprint }, // void(entity e) debug print an entire entity
+ { 32, "walkmove", PF_walkmove }, // float(float yaw, float dist) walkmove
+ { 33, "updateLimb", PF_updateLimb },
+ { 34, "droptofloor", PF_droptofloor },
+ { 35, "lightstyle", PF_lightstyle },
+ { 36, "rint", PF_rint },
+ { 37, "floor", PF_floor },
+ { 38, "ceil", PF_ceil },
+// { 39, "fixme", PF_Fixme },
+ { 40, "checkbottom", PF_checkbottom },
+ { 41, "pointcontents", PF_pointcontents },
+// { 42, "fixme", PF_Fixme },
+ { 43, "fabs", PF_fabs },
+ { 44, "aim", PF_aim },
+ { 45, "cvar", PF_cvar },
+ { 46, "localcmd", PF_localcmd },
+ { 47, "nextent", PF_nextent },
+ { 48, "particle", PF_particle },
+ { 49, "ChangeYaw", PF_changeyaw },
+ { 50, "getSoundLen", PF_GetSoundLen },
+ { 51, "vectoangles", PF_vectoangles },
+
+ { 52, "WriteByte", PF_WriteByte },
+ { 53, "WriteChar", PF_WriteChar },
+ { 54, "WriteShort", PF_WriteShort },
+ { 55, "WriteLong", PF_WriteLong },
+ { 56, "WriteCoord", PF_WriteCoord },
+ { 57, "WriteAngle", PF_WriteAngle },
+ { 58, "WriteString", PF_WriteString },
+ { 59, "WriteEntity", PF_WriteEntity },
+
+ { 60, "sin", PF_sin },
+ { 61, "cos", PF_cos },
+ { 62, "sqrt", PF_sqrt },
+ { 65, "etos", PF_etos },
+
+ { 67, "movetogoal", SV_MoveToGoal },
+ { 68, "precache_file", PF_precache_file },
+ { 69, "makestatic", PF_makestatic },
+
+ { 70, "changelevel", PF_changelevel },
+ { 71, "movetoorigin", SV_MoveToOrigin },
+
+ { 72, "cvar_set", PF_cvar_set },
+ { 73, "centerprint", PF_centerprint },
+
+ { 74, "ambientsound", PF_ambientsound },
+
+ { 75, "precache_model2", PF_precache_model },
+ { 76, "precache_sound2", PF_precache_sound }, // precache_sound2 is different only for qcc
+ { 77, "precache_file2", PF_precache_file },
+
+ { 78, "setspawnparms", PF_setspawnparms },
+ { 79, "achievement", PF_achievement },
+
+ { 81, "stof", PF_stof }, // 2001-09-20 QuakeC string manipulation by FrikaC/Maddes
+
+ { 82, "Set_Zombie", PF_Set_Zombie }, //set zombies stats
+ { 83, "Get_Waypoint_Near", Get_Waypoint_Near }, //returns the cords for closest waypoint
+ { 84, "Do_Pathfind", Do_Pathfind }, //starts the magic
+ { 85, "Open_Waypoint", Open_Waypoint }, //Opens a waypoint
+ { 86, "Get_Next_Waypoint", Get_Next_Waypoint }, //Get next waypoint
+ { 87, "useprint", PF_useprint }, //Print with button
+ { 88, "Get_First_Waypoint", Get_First_Waypoint },//Get the first waypoint in the list
+ { 89, "Close_Waypoint", Close_Waypoint }, //Closes a waypoint
+
+// 2001-11-15 DarkPlaces general builtin functions by Lord Havoc start
+// not implemented yet
+
+
+ { 90, "tracebox", PF_tracebox },
+ { 99, "tracemove", PF_tracemove },//blubs improved tracebox
+/* { 91, "randomvec", PF_randomvec },
+ { 92, "getlight", PF_GetLight }, // not implemented yet
+ { 93, "cvar_create", PF_cvar_create }, // 2001-09-18 New BuiltIn Function: cvar_create() by Maddes
+ { 94, "fmin", PF_fmin },
+ { 95, "fmax", PF_fmax },
+ { 96, "fbound", PF_fbound },
+ { 97, "fpow", PF_fpow },
+ */{ 98, "findfloat", PF_FindFloat },/*
+ { PR_DEFAULT_FUNCNO_EXTENSION_FIND, "extension_find", PF_extension_find }, // 2001-10-20 Extension System by Lord Havoc/Maddes
+ { 0, "registercvar", PF_cvar_create }, // 0 indicates that this entry is just for remapping (because of name change)
+ { 0, "checkextension", PF_extension_find },
+*/
+// 2001-11-15 DarkPlaces general builtin functions by Lord Havoc end
+
+ { PR_DEFAULT_FUNCNO_BUILTIN_FIND, "builtin_find", PF_builtin_find }, // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes
+
+// not implemented yet
+/*
+ { 101, "cmd_find", PF_cmd_find }, // 2001-09-16 New BuiltIn Function: cmd_find() by Maddes
+
+ { 102, "cvar_find", PF_cvar_find }, // 2001-09-16 New BuiltIn Function: cvar_find() by Maddes
+
+ { 103, "cvar_string", PF_cvar_string }, // 2001-09-16 New BuiltIn Function: cvar_string() by Maddes
+
+ { 105, "cvar_free", PF_cvar_free }, // 2001-09-18 New BuiltIn Function: cvar_free() by Maddes
+
+ { 106, "NVS_InitSVCMsg", PF_NVS_InitSVCMsg }, // 2000-05-02 NVS SVC by Maddes
+
+ { 107, "WriteFloat", PF_WriteFloat }, // 2001-09-16 New BuiltIn Function: WriteFloat() by Maddes
+
+ { 108, "etof", PF_etof }, // 2001-09-25 New BuiltIn Function: etof() by Maddes
+
+ { 109, "ftoe", PF_ftoe }, // 2001-09-25 New BuiltIn Function: ftoe() by Maddes
+*/
+
+// 2001-09-20 QuakeC file access by FrikaC/Maddes start
+// not implemented yet
+
+ { 110, "fopen", PF_fopen },
+ { 111, "fclose", PF_fclose },
+ { 112, "fgets", PF_fgets },
+ { 113, "fputs", PF_fputs },
+ { 0, "open", PF_fopen }, // 0 indicates that this entry is just for remapping (because of name and number change)
+ { 0, "close", PF_fclose },
+ { 0, "read", PF_fgets },
+ { 0, "write", PF_fputs },
+
+// 2001-09-20 QuakeC file access by FrikaC/Maddes end
+
+// 2001-09-20 QuakeC string manipulation by FrikaC/Maddes start
+// not implemented yet
+
+ { 114, "strlen", PF_strlen },
+ { 115, "strcat", PF_strcat },
+ { 116, "substring", PF_substring },
+ { 117, "stov", PF_stov },
+ { 118, "strzone", PF_strzone },
+ { 119, "strunzone", PF_strunzone },
+ { 120, "strtrim", PF_strtrim },
+ { 0, "zone", PF_strzone }, // 0 indicates that this entry is just for remapping (because of name and number change)
+ { 0, "unzone", PF_strunzone },
+
+// 2001-09-20 QuakeC string manipulation by FrikaC/Maddes end
+
+// 2001-11-15 DarkPlaces general builtin functions by Lord Havoc start
+// not implemented yet
+/*
+ { 400, "copyentity", PF_... },
+ { 401, "setcolor", PF_... },
+ { 402, "findchain", PF_... },
+ { 403, "findchainfloat", PF_... },
+ { 404, "effect", PF_... },
+ { 405, "te_blood", PF_... },
+ { 406, "te_bloodshower", PF_... },
+ { 407, "te_explosionrgb", PF_... },
+ { 408, "te_particlecube", PF_... },
+ { 409, "te_particlerain", PF_... },
+ { 410, "te_particlesnow", PF_... },
+ { 411, "te_spark", PF_... },
+ { 412, "te_gunshotquad", PF_... },
+ { 413, "te_spikequad", PF_... },
+ { 414, "te_superspikequad", PF_... },
+ { 415, "te_explosionquad", PF_... },
+ { 416, "te_smallflash", PF_... }
+ { 417, "te_customflash", PF_... },
+ { 418, "te_gunshot", PF_... },
+ { 419, "te_spike", PF_... },
+ { 420, "te_superspike", PF_... },
+ { 421, "te_explosion", PF_... },
+ { 422, "te_tarexplosion", PF_... },
+ { 423, "te_wizspike", PF_... },
+ { 424, "te_knightspike", PF_... },
+ { 425, "te_lavasplash", PF_... }
+ { 426, "te_teleport", PF_... },
+ { 427, "te_explosion2", PF_... },
+ { 428, "te_lightning1", PF_... },
+ { 429, "te_lightning2", PF_... },
+ { 430, "te_lightning3", PF_... },
+ { 431, "te_beam", PF_... },
+ { 432, "vectorvectors", PF_... },*/
+ { 441, "tokenize", PF_tokenize },
+ { 442, "argv", PF_ArgV },
+
+ { 500, "songegg", PF_SongEgg },
+ { 501, "nzp_maxammo", PF_MaxAmmo }
+
+// 2001-11-15 DarkPlaces general builtin functions by Lord Havoc end
+
+};
+
+int pr_ebfs_numbuiltins = sizeof(pr_ebfs_builtins)/sizeof(pr_ebfs_builtins[0]);
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end
+
diff --git a/source/pr_comp.h b/source/pr_comp.h
new file mode 100644
index 0000000..b237ed5
--- /dev/null
+++ b/source/pr_comp.h
@@ -0,0 +1,180 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+// this file is shared by quake and qcc
+
+typedef int func_t;
+typedef int string_t;
+
+typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer} etype_t;
+
+
+#define OFS_NULL 0
+#define OFS_RETURN 1
+#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors
+#define OFS_PARM1 7
+#define OFS_PARM2 10
+#define OFS_PARM3 13
+#define OFS_PARM4 16
+#define OFS_PARM5 19
+#define OFS_PARM6 22
+#define OFS_PARM7 25
+#define RESERVED_OFS 28
+
+
+enum {
+ OP_DONE,
+ OP_MUL_F,
+ OP_MUL_V,
+ OP_MUL_FV,
+ OP_MUL_VF,
+ OP_DIV_F,
+ OP_ADD_F,
+ OP_ADD_V,
+ OP_SUB_F,
+ OP_SUB_V,
+
+ OP_EQ_F,
+ OP_EQ_V,
+ OP_EQ_S,
+ OP_EQ_E,
+ OP_EQ_FNC,
+
+ OP_NE_F,
+ OP_NE_V,
+ OP_NE_S,
+ OP_NE_E,
+ OP_NE_FNC,
+
+ OP_LE,
+ OP_GE,
+ OP_LT,
+ OP_GT,
+
+ OP_LOAD_F,
+ OP_LOAD_V,
+ OP_LOAD_S,
+ OP_LOAD_ENT,
+ OP_LOAD_FLD,
+ OP_LOAD_FNC,
+
+ OP_ADDRESS,
+
+ OP_STORE_F,
+ OP_STORE_V,
+ OP_STORE_S,
+ OP_STORE_ENT,
+ OP_STORE_FLD,
+ OP_STORE_FNC,
+
+ OP_STOREP_F,
+ OP_STOREP_V,
+ OP_STOREP_S,
+ OP_STOREP_ENT,
+ OP_STOREP_FLD,
+ OP_STOREP_FNC,
+
+ OP_RETURN,
+ OP_NOT_F,
+ OP_NOT_V,
+ OP_NOT_S,
+ OP_NOT_ENT,
+ OP_NOT_FNC,
+ OP_IF,
+ OP_IFNOT,
+ OP_CALL0,
+ OP_CALL1,
+ OP_CALL2,
+ OP_CALL3,
+ OP_CALL4,
+ OP_CALL5,
+ OP_CALL6,
+ OP_CALL7,
+ OP_CALL8,
+ OP_STATE,
+ OP_GOTO,
+ OP_AND,
+ OP_OR,
+
+ OP_BITAND,
+ OP_BITOR
+};
+
+
+typedef struct statement_s
+{
+ unsigned short op;
+ short a,b,c;
+} dstatement_t;
+
+typedef struct
+{
+ unsigned short type; // if DEF_SAVEGLOBGAL bit is set
+ // the variable needs to be saved in savegames
+ unsigned short ofs;
+ int s_name;
+} ddef_t;
+#define DEF_SAVEGLOBAL (1<<15)
+
+#define MAX_PARMS 8
+
+typedef struct
+{
+ int first_statement; // negative numbers are builtins
+ int parm_start;
+ int locals; // total ints of parms + locals
+
+ int profile; // runtime
+
+ int s_name;
+ int s_file; // source file defined in
+
+ int numparms;
+ byte parm_size[MAX_PARMS];
+} dfunction_t;
+
+
+#define PROG_VERSION 6
+typedef struct
+{
+ int version;
+ int crc; // check of header file
+
+ int ofs_statements;
+ int numstatements; // statement 0 is an error
+
+ int ofs_globaldefs;
+ int numglobaldefs;
+
+ int ofs_fielddefs;
+ int numfielddefs;
+
+ int ofs_functions;
+ int numfunctions; // function 0 is an empty
+
+ int ofs_strings;
+ int numstrings; // first string is a null string
+
+ int ofs_globals;
+ int numglobals;
+
+ int entityfields;
+} dprograms_t;
+
diff --git a/source/pr_edict.c b/source/pr_edict.c
new file mode 100644
index 0000000..167a7d1
--- /dev/null
+++ b/source/pr_edict.c
@@ -0,0 +1,1339 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// sv_edict.c -- entity dictionary
+
+#include "quakedef.h"
+
+dprograms_t *progs;
+dfunction_t *pr_functions;
+char *pr_strings;
+ddef_t *pr_fielddefs;
+ddef_t *pr_globaldefs;
+dstatement_t *pr_statements;
+globalvars_t *pr_global_struct;
+float *pr_globals; // same as pr_global_struct
+int pr_edict_size; // in bytes
+
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start
+cvar_t pr_builtin_find = {"pr_builtin_find", "0", false, false};
+cvar_t pr_builtin_remap = {"pr_builtin_remap", "0", false, false};
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end
+
+unsigned short pr_crc;
+
+int type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
+
+ddef_t *ED_FieldAtOfs (int ofs);
+qboolean ED_ParseEpair (void *base, ddef_t *key, char *s);
+
+cvar_t nomonsters = {"nomonsters", "0"};
+cvar_t gamecfg = {"gamecfg", "0"};
+cvar_t scratch1 = {"scratch1", "0"};
+cvar_t scratch2 = {"scratch2", "0"};
+cvar_t scratch3 = {"scratch3", "0"};
+cvar_t scratch4 = {"scratch4", "0"};
+cvar_t savedgamecfg = {"savedgamecfg", "0", true};
+cvar_t saved1 = {"saved1", "0", true};
+cvar_t saved2 = {"saved2", "0", true};
+cvar_t saved3 = {"saved3", "0", true};
+cvar_t saved4 = {"saved4", "0", true};
+
+#define MAX_FIELD_LEN 64
+#define GEFV_CACHESIZE 2
+
+typedef struct {
+ ddef_t *pcache;
+ char field[MAX_FIELD_LEN];
+} gefv_cache;
+
+static gefv_cache gefvCache[GEFV_CACHESIZE] = {{NULL, ""}, {NULL, ""}};
+
+// evaluation shortcuts
+int eval_gravity;
+int eval_idealpitch, eval_pitch_speed;
+
+// Half_life modes. Crow_bar
+int eval_renderamt, eval_rendermode, eval_rendercolor;
+
+ddef_t *ED_FindField (char *name);
+
+int FindFieldOffset (char *field)
+{
+ ddef_t *d;
+
+ if (!(d = ED_FindField(field)))
+ return 0;
+
+ return d->ofs*4;
+}
+
+void FindEdictFieldOffsets (void)
+{
+ eval_gravity = FindFieldOffset ("gravity");
+
+ eval_idealpitch = FindFieldOffset ("idealpitch");
+ eval_pitch_speed = FindFieldOffset ("pitch_speed");
+ eval_renderamt = FindFieldOffset ("renderamt");
+ eval_rendermode = FindFieldOffset ("rendermode");
+ eval_rendercolor = FindFieldOffset ("rendercolor");
+}
+
+/*
+=================
+ED_ClearEdict
+
+Sets everything to NULL
+=================
+*/
+void ED_ClearEdict (edict_t *e)
+{
+ memset (&e->v, 0, progs->entityfields * 4);
+ e->free = false;
+}
+
+/*
+=================
+ED_Alloc
+
+Either finds a free edict, or allocates a new one.
+Try to avoid reusing an entity that was recently freed, because it
+can cause the client to think the entity morphed into something else
+instead of being removed and recreated, which can cause interpolated
+angles and bad trails.
+=================
+*/
+edict_t *ED_Alloc (void)
+{
+ int i;
+ edict_t *e;
+
+ for ( i=svs.maxclients+1 ; ifree && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) )
+ {
+ ED_ClearEdict (e);
+ return e;
+ }
+ }
+
+ if (i == MAX_EDICTS)
+ Sys_Error ("ED_Alloc: no free edicts");
+
+ sv.num_edicts++;
+ e = EDICT_NUM(i);
+ ED_ClearEdict (e);
+
+ return e;
+}
+
+/*
+=================
+ED_Free
+
+Marks the edict as free
+FIXME: walk all entities and NULL out references to this entity
+=================
+*/
+void ED_Free (edict_t *ed)
+{
+ SV_UnlinkEdict (ed); // unlink from world bsp
+
+ ed->free = true;
+ ed->v.model = 0;
+ ed->v.takedamage = 0;
+ ed->v.modelindex = 0;
+ ed->v.colormap = 0;
+ ed->v.skin = 0;
+ ed->v.frame = 0;
+ VectorCopy (vec3_origin, ed->v.origin);
+ VectorCopy (vec3_origin, ed->v.angles);
+ ed->v.nextthink = -1;
+ ed->v.solid = 0;
+
+ ed->freetime = sv.time;
+}
+
+//===========================================================================
+
+/*
+============
+ED_GlobalAtOfs
+============
+*/
+ddef_t *ED_GlobalAtOfs (int ofs)
+{
+ ddef_t *def;
+ int i;
+
+ for (i=0 ; inumglobaldefs ; i++)
+ {
+ def = &pr_globaldefs[i];
+ if (def->ofs == ofs)
+ return def;
+ }
+ return NULL;
+}
+
+/*
+============
+ED_FieldAtOfs
+============
+*/
+ddef_t *ED_FieldAtOfs (int ofs)
+{
+ ddef_t *def;
+ int i;
+
+ for (i=0 ; inumfielddefs ; i++)
+ {
+ def = &pr_fielddefs[i];
+ if (def->ofs == ofs)
+ return def;
+ }
+ return NULL;
+}
+
+/*
+============
+ED_FindField
+============
+*/
+ddef_t *ED_FindField (char *name)
+{
+ ddef_t *def;
+ int i;
+
+ for (i=0 ; inumfielddefs ; i++)
+ {
+ def = &pr_fielddefs[i];
+ if (!strcmp(pr_strings + def->s_name,name) )
+ return def;
+ }
+ return NULL;
+}
+
+
+/*
+============
+ED_FindGlobal
+============
+*/
+ddef_t *ED_FindGlobal (char *name)
+{
+ ddef_t *def;
+ int i;
+
+ for (i=0 ; inumglobaldefs ; i++)
+ {
+ def = &pr_globaldefs[i];
+ if (!strcmp(pr_strings + def->s_name,name) )
+ return def;
+ }
+ return NULL;
+}
+
+
+/*
+============
+ED_FindFunction
+============
+*/
+dfunction_t *ED_FindFunction (char *name)
+{
+ dfunction_t *func;
+ int i;
+
+ for (i=0 ; inumfunctions ; i++)
+ {
+ func = &pr_functions[i];
+ if (!strcmp(pr_strings + func->s_name,name) )
+ return func;
+ }
+ return NULL;
+}
+
+/*
+eval_t *GetEdictFieldValue(edict_t *ed, char *field)
+{
+ ddef_t *def = NULL;
+ int i;
+ static int rep = 0;
+
+ for (i=0 ; iv + def->ofs*4);
+}
+*/
+
+/*
+============
+PR_ValueString
+
+Returns a string describing *data in a type specific manner
+=============
+*/
+char *PR_ValueString (etype_t type, eval_t *val)
+{
+ static char line[256];
+ ddef_t *def;
+ dfunction_t *f;
+
+ type &= ~DEF_SAVEGLOBAL;
+
+ switch (type)
+ {
+ case ev_string:
+ sprintf (line, "%s", pr_strings + val->string);
+ break;
+ case ev_entity:
+ sprintf (line, "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) );
+ break;
+ case ev_function:
+ f = pr_functions + val->function;
+ sprintf (line, "%s()", pr_strings + f->s_name);
+ break;
+ case ev_field:
+ def = ED_FieldAtOfs ( val->_int );
+ sprintf (line, ".%s", pr_strings + def->s_name);
+ break;
+ case ev_void:
+ sprintf (line, "void");
+ break;
+ case ev_float:
+ sprintf (line, "%5.1f", val->_float);
+ break;
+ case ev_vector:
+ sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]);
+ break;
+ case ev_pointer:
+ sprintf (line, "pointer");
+ break;
+ default:
+ sprintf (line, "bad type %i", type);
+ break;
+ }
+
+ return line;
+}
+
+/*
+============
+PR_UglyValueString
+
+Returns a string describing *data in a type specific manner
+Easier to parse than PR_ValueString
+=============
+*/
+char *PR_UglyValueString (etype_t type, eval_t *val)
+{
+ static char line[256];
+ ddef_t *def;
+ dfunction_t *f;
+
+ type &= ~DEF_SAVEGLOBAL;
+
+ switch (type)
+ {
+ case ev_string:
+ sprintf (line, "%s", pr_strings + val->string);
+ break;
+ case ev_entity:
+ sprintf (line, "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)));
+ break;
+ case ev_function:
+ f = pr_functions + val->function;
+ sprintf (line, "%s", pr_strings + f->s_name);
+ break;
+ case ev_field:
+ def = ED_FieldAtOfs ( val->_int );
+ sprintf (line, "%s", pr_strings + def->s_name);
+ break;
+ case ev_void:
+ sprintf (line, "void");
+ break;
+ case ev_float:
+ sprintf (line, "%f", val->_float);
+ break;
+ case ev_vector:
+ sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
+ break;
+ default:
+ sprintf (line, "bad type %i", type);
+ break;
+ }
+
+ return line;
+}
+
+/*
+============
+PR_GlobalString
+
+Returns a string with a description and the contents of a global,
+padded to 20 field width
+============
+*/
+char *PR_GlobalString (int ofs)
+{
+ char *s;
+ int i;
+ ddef_t *def;
+ void *val;
+ static char line[128];
+
+ val = (void *)&pr_globals[ofs];
+ def = ED_GlobalAtOfs(ofs);
+ if (!def)
+ sprintf (line,"%i(?\?\?)", ofs);
+ else
+ {
+ s = PR_ValueString (def->type, val);
+ sprintf (line,"%i(%s)%s", ofs, pr_strings + def->s_name, s);
+ }
+
+ i = strlen(line);
+ for ( ; i<20 ; i++)
+ strcat (line," ");
+ strcat (line," ");
+
+ return line;
+}
+
+char *PR_GlobalStringNoContents (int ofs)
+{
+ int i;
+ ddef_t *def;
+ static char line[128];
+
+ def = ED_GlobalAtOfs(ofs);
+ if (!def)
+ sprintf (line,"%i(?\?\?)", ofs);
+ else
+ sprintf (line,"%i(%s)", ofs, pr_strings + def->s_name);
+
+ i = strlen(line);
+ for ( ; i<20 ; i++)
+ strcat (line," ");
+ strcat (line," ");
+
+ return line;
+}
+
+
+/*
+=============
+ED_Print
+
+For debugging
+=============
+*/
+void ED_Print (edict_t *ed)
+{
+ int l;
+ ddef_t *d;
+ int *v;
+ int i, j;
+ char *name;
+ int type;
+
+ if (ed->free)
+ {
+ Con_Printf ("FREE\n");
+ return;
+ }
+
+ Con_Printf("\nEDICT %i:\n", NUM_FOR_EDICT(ed));
+ for (i=1 ; inumfielddefs ; i++)
+ {
+ d = &pr_fielddefs[i];
+ name = pr_strings + d->s_name;
+ if (name[strlen(name)-2] == '_')
+ continue; // skip _x, _y, _z vars
+
+ v = (int *)((char *)&ed->v + d->ofs*4);
+
+ // if the value is still all 0, skip the field
+ type = d->type & ~DEF_SAVEGLOBAL;
+
+ for (j=0 ; jtype, (eval_t *)v));
+ }
+}
+
+/*
+=============
+ED_Write
+
+For savegames
+=============
+*/
+void ED_Write (FILE *f, edict_t *ed)
+{
+ ddef_t *d;
+ int *v;
+ int i, j;
+ char *name;
+ int type;
+
+ fprintf (f, "{\n");
+
+ if (ed->free)
+ {
+ fprintf (f, "}\n");
+ return;
+ }
+
+ for (i=1 ; inumfielddefs ; i++)
+ {
+ d = &pr_fielddefs[i];
+ name = pr_strings + d->s_name;
+ if (name[strlen(name)-2] == '_')
+ continue; // skip _x, _y, _z vars
+
+ v = (int *)((char *)&ed->v + d->ofs*4);
+
+ // if the value is still all 0, skip the field
+ type = d->type & ~DEF_SAVEGLOBAL;
+ for (j=0 ; jtype, (eval_t *)v));
+ }
+
+ fprintf (f, "}\n");
+}
+
+void ED_PrintNum (int ent)
+{
+ ED_Print (EDICT_NUM(ent));
+}
+
+/*
+=============
+ED_PrintEdicts
+
+For debugging, prints all the entities in the current server
+=============
+*/
+void ED_PrintEdicts (void)
+{
+ int i;
+
+ Con_Printf ("%i entities\n", sv.num_edicts);
+ for (i=0 ; i= sv.num_edicts)
+ {
+ Con_Printf("Bad edict number\n");
+ return;
+ }
+ ED_PrintNum (i);
+}
+
+/*
+=============
+ED_Count
+
+For debugging
+=============
+*/
+void ED_Count (void)
+{
+ int i;
+ edict_t *ent;
+ int active, models, solid, step;
+
+ active = models = solid = step = 0;
+ for (i=0 ; ifree)
+ continue;
+ active++;
+ if (ent->v.solid)
+ solid++;
+ if (ent->v.model)
+ models++;
+ if (ent->v.movetype == MOVETYPE_STEP)
+ step++;
+ }
+
+ Con_Printf ("num_edicts:%3i\n", sv.num_edicts);
+ Con_Printf ("active :%3i\n", active);
+ Con_Printf ("view :%3i\n", models);
+ Con_Printf ("touch :%3i\n", solid);
+ Con_Printf ("step :%3i\n", step);
+
+}
+
+/*
+==============================================================================
+
+ ARCHIVING GLOBALS
+
+FIXME: need to tag constants, doesn't really work
+==============================================================================
+*/
+
+/*
+=============
+ED_WriteGlobals
+=============
+*/
+void ED_WriteGlobals (FILE *f)
+{
+ ddef_t *def;
+ int i;
+ char *name;
+ int type;
+
+ fprintf (f,"{\n");
+ for (i=0 ; inumglobaldefs ; i++)
+ {
+ def = &pr_globaldefs[i];
+ type = def->type;
+ if ( !(def->type & DEF_SAVEGLOBAL) )
+ continue;
+ type &= ~DEF_SAVEGLOBAL;
+
+ if (type != ev_string
+ && type != ev_float
+ && type != ev_entity)
+ continue;
+
+ name = pr_strings + def->s_name;
+ fprintf (f,"\"%s\" ", name);
+ fprintf (f,"\"%s\"\n", PR_UglyValueString(type, (eval_t *)&pr_globals[def->ofs]));
+ }
+ fprintf (f,"}\n");
+}
+
+/*
+=============
+ED_ParseGlobals
+=============
+*/
+void ED_ParseGlobals (char *data)
+{
+ char keyname[64];
+ ddef_t *key;
+
+ while (1)
+ {
+ // parse key
+ data = COM_Parse (data);
+ if (com_token[0] == '}')
+ break;
+ if (!data)
+ Sys_Error ("ED_ParseEntity: EOF without closing brace");
+
+ strcpy (keyname, com_token);
+
+ // parse value
+ data = COM_Parse (data);
+ if (!data)
+ Sys_Error ("ED_ParseEntity: EOF without closing brace");
+
+ if (com_token[0] == '}')
+ Sys_Error ("ED_ParseEntity: closing brace without data");
+
+ key = ED_FindGlobal (keyname);
+ if (!key)
+ {
+ Con_Printf ("'%s' is not a global\n", keyname);
+ continue;
+ }
+
+ if (!ED_ParseEpair ((void *)pr_globals, key, com_token))
+ Host_Error ("ED_ParseGlobals: parse error");
+ }
+}
+
+//============================================================================
+
+
+/*
+=============
+ED_NewString
+=============
+*/
+char *ED_NewString (char *string)
+{
+ char *new, *new_p;
+ int i,l;
+
+ l = strlen(string) + 1;
+ new = Hunk_Alloc (l);
+ new_p = new;
+
+ for (i=0 ; i< l ; i++)
+ {
+ if (string[i] == '\\' && i < l-1)
+ {
+ i++;
+ if (string[i] == 'n')
+ *new_p++ = '\n';
+ else
+ *new_p++ = '\\';
+ }
+ else
+ *new_p++ = string[i];
+ }
+
+ return new;
+}
+
+
+/*
+=============
+ED_ParseEval
+
+Can parse either fields or globals
+returns false if error
+=============
+*/
+qboolean ED_ParseEpair (void *base, ddef_t *key, char *s)
+{
+ int i;
+ char string[128];
+ ddef_t *def;
+ char *v, *w;
+ void *d;
+ dfunction_t *func;
+
+ d = (void *)((int *)base + key->ofs);
+
+ switch (key->type & ~DEF_SAVEGLOBAL)
+ {
+ case ev_string:
+ *(string_t *)d = ED_NewString (s) - pr_strings;
+ break;
+
+ case ev_float:
+ *(float *)d = atof (s);
+ break;
+
+ case ev_vector:
+ strcpy (string, s);
+ v = string;
+ w = string;
+ for (i=0 ; i<3 ; i++)
+ {
+ while (*v && *v != ' ')
+ v++;
+ *v = 0;
+ ((float *)d)[i] = atof (w);
+ w = v = v+1;
+ }
+ break;
+
+ case ev_entity:
+ *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s)));
+ break;
+
+ case ev_field:
+ def = ED_FindField (s);
+ if (!def)
+ {
+ Con_Printf ("Can't find field %s\n", s);
+ return false;
+ }
+ *(int *)d = G_INT(def->ofs);
+ break;
+
+ case ev_function:
+ func = ED_FindFunction (s);
+ if (!func)
+ {
+ Con_Printf ("Can't find function %s\n", s);
+ return false;
+ }
+ *(func_t *)d = func - pr_functions;
+ break;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+/*
+====================
+ED_ParseEdict
+
+Parses an edict out of the given string, returning the new position
+ed should be a properly initialized empty edict.
+Used for initial level load and for savegames.
+====================
+*/
+char *ED_ParseEdict (char *data, edict_t *ent)
+{
+ ddef_t *key;
+ qboolean anglehack;
+ qboolean init;
+ char keyname[256];
+ int n;
+
+ init = false;
+
+// clear it
+ if (ent != sv.edicts) // hack
+ memset (&ent->v, 0, progs->entityfields * 4);
+
+// go through all the dictionary pairs
+ while (1)
+ {
+ // parse key
+ data = COM_Parse (data);
+ if (com_token[0] == '}')
+ break;
+ if (!data)
+ Sys_Error ("ED_ParseEntity: EOF without closing brace");
+
+// anglehack is to allow QuakeEd to write single scalar angles
+// and allow them to be turned into vectors. (FIXME...)
+if (!strcmp(com_token, "angle"))
+{
+ strcpy (com_token, "angles");
+ anglehack = true;
+}
+else
+ anglehack = false;
+
+// FIXME: change light to _light to get rid of this hack
+if (!strcmp(com_token, "light"))
+ strcpy (com_token, "light_lev"); // hack for single light def
+
+ strcpy (keyname, com_token);
+
+ // another hack to fix heynames with trailing spaces
+ n = strlen(keyname);
+ while (n && keyname[n-1] == ' ')
+ {
+ keyname[n-1] = 0;
+ n--;
+ }
+
+ // parse value
+ data = COM_Parse (data);
+ if (!data)
+ Sys_Error ("ED_ParseEntity: EOF without closing brace");
+
+ if (com_token[0] == '}')
+ Sys_Error ("ED_ParseEntity: closing brace without data");
+
+ init = true;
+
+// keynames with a leading underscore are used for utility comments,
+// and are immediately discarded by quake
+ if (keyname[0] == '_')
+ continue;
+
+ key = ED_FindField (keyname);
+ if (!key)
+ {
+ if (strcmp (keyname, "compiler") &&
+ strcmp (keyname, "r_skycolor") &&
+ strcmp (keyname, "sequence") &&
+ strcmp (keyname, "message2") && //dirty hack to keep the console clean. Dem lazy mappers
+ strcmp (keyname, "mangle") &&
+ strcmp (keyname, "Maxrange") &&
+ strcmp (keyname, "light_lev") &&
+ strcmp (keyname, "fog"))
+ Con_Printf ("'%s' is not a field\n", keyname);
+ continue;
+ }
+
+if (anglehack)
+{
+char temp[32];
+strcpy (temp, com_token);
+sprintf (com_token, "0 %s 0", temp);
+}
+
+ if (!ED_ParseEpair ((void *)&ent->v, key, com_token))
+ Host_Error ("ED_ParseEdict: parse error");
+ }
+
+ if (!init)
+ ent->free = true;
+
+ return data;
+}
+
+
+/*
+================
+ED_LoadFromFile
+
+The entities are directly placed in the array, rather than allocated with
+ED_Alloc, because otherwise an error loading the map would have entity
+number references out of order.
+
+Creates a server's entity / program execution context by
+parsing textual entity definitions out of an ent file.
+
+Used for both fresh maps and savegame loads. A fresh map would also need
+to call ED_CallSpawnFunctions () to let the objects initialize themselves.
+================
+*/
+void ED_LoadFromFile (char *data)
+{
+ edict_t *ent;
+ int inhibit;
+ dfunction_t *func;
+
+ ent = NULL;
+ inhibit = 0;
+ pr_global_struct->time = sv.time;
+
+// parse ents
+ while (1)
+ {
+// parse the opening brace
+ data = COM_Parse (data);
+ if (!data)
+ break;
+ if (com_token[0] != '{')
+ Sys_Error ("ED_LoadFromFile: found %s when expecting {",com_token);
+
+ if (!ent)
+ ent = EDICT_NUM(0);
+ else
+ ent = ED_Alloc ();
+ data = ED_ParseEdict (data, ent);
+
+// remove things from different skill levels or deathmatch
+ if (deathmatch.value)
+ {
+ if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH))
+ {
+ ED_Free (ent);
+ inhibit++;
+ continue;
+ }
+ }
+ else if ((current_skill == 0 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_EASY))
+ || (current_skill == 1 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM))
+ || (current_skill >= 2 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_HARD)) )
+ {
+ ED_Free (ent);
+ inhibit++;
+ continue;
+ }
+
+//
+// immediately call spawn function
+//
+ if (!ent->v.classname)
+ {
+ Con_Printf ("No classname for:\n");
+ ED_Print (ent);
+ ED_Free (ent);
+ continue;
+ }
+
+ // look for the spawn function
+ func = ED_FindFunction ( pr_strings + ent->v.classname );
+
+ if (!func)
+ {
+ Con_Printf ("No spawn function for:\n");
+ ED_Print (ent);
+ ED_Free (ent);
+ continue;
+ }
+
+ pr_global_struct->self = EDICT_TO_PROG(ent);
+ PR_ExecuteProgram (func - pr_functions);
+ }
+
+ Con_DPrintf ("%i entities inhibited\n", inhibit);
+}
+
+func_t EndFrame;
+/*
+===============
+PR_LoadProgs
+===============
+*/
+void PR_LoadProgs (void)
+{
+ dfunction_t *f;
+ int i;
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm start
+ int j;
+ int funcno;
+ char *funcname;
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm end
+
+// flush the non-C variable lookup cache
+ for (i=0 ; iversion != PROG_VERSION)
+ Sys_Error ("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION);
+ if (progs->crc != PROGHEADER_CRC)
+ Con_Printf ("WARNING!!! progs.dat system vars have been modified, progdefs.h is out of date. Current one is %i\n", progs->crc);
+
+ pr_functions = (dfunction_t *)((byte *)progs + progs->ofs_functions);
+ pr_strings = (char *)progs + progs->ofs_strings;
+ pr_globaldefs = (ddef_t *)((byte *)progs + progs->ofs_globaldefs);
+ pr_fielddefs = (ddef_t *)((byte *)progs + progs->ofs_fielddefs);
+ pr_statements = (dstatement_t *)((byte *)progs + progs->ofs_statements);
+
+ pr_global_struct = (globalvars_t *)((byte *)progs + progs->ofs_globals);
+ pr_globals = (float *)pr_global_struct;
+
+ pr_edict_size = progs->entityfields * 4 + sizeof (edict_t) - sizeof(entvars_t);
+
+// byte swap the lumps
+ for (i=0 ; inumstatements ; i++)
+ {
+ pr_statements[i].op = LittleShort(pr_statements[i].op);
+ pr_statements[i].a = LittleShort(pr_statements[i].a);
+ pr_statements[i].b = LittleShort(pr_statements[i].b);
+ pr_statements[i].c = LittleShort(pr_statements[i].c);
+ }
+
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm start
+ // initialize function numbers for PROGS.DAT
+ pr_numbuiltins = 0;
+ pr_builtins = NULL;
+ if (pr_builtin_remap.value)
+ {
+ // remove all previous assigned function numbers
+ for ( j=1 ; j < pr_ebfs_numbuiltins; j++)
+ {
+ pr_ebfs_builtins[j].funcno = 0;
+ }
+ }
+ else
+ {
+ // use default function numbers
+ for ( j=1 ; j < pr_ebfs_numbuiltins; j++)
+ {
+ pr_ebfs_builtins[j].funcno = pr_ebfs_builtins[j].default_funcno;
+ // determine highest builtin number (when NOT remapped)
+ if (pr_ebfs_builtins[j].funcno > pr_numbuiltins)
+ {
+ pr_numbuiltins = pr_ebfs_builtins[j].funcno;
+ }
+ }
+ }
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm end
+
+ for (i=0 ; inumfunctions; i++)
+ {
+ pr_functions[i].first_statement = LittleLong (pr_functions[i].first_statement);
+ pr_functions[i].parm_start = LittleLong (pr_functions[i].parm_start);
+ pr_functions[i].s_name = LittleLong (pr_functions[i].s_name);
+ pr_functions[i].s_file = LittleLong (pr_functions[i].s_file);
+ pr_functions[i].numparms = LittleLong (pr_functions[i].numparms);
+ pr_functions[i].locals = LittleLong (pr_functions[i].locals);
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm start
+ if (pr_builtin_remap.value)
+ {
+ if (pr_functions[i].first_statement < 0) // builtin function
+ {
+ funcno = -pr_functions[i].first_statement;
+ funcname = pr_strings + pr_functions[i].s_name;
+
+ // search function name
+ for ( j=1 ; j < pr_ebfs_numbuiltins ; j++)
+ {
+ if (!(Q_strcasecmp(funcname, pr_ebfs_builtins[j].funcname)))
+ {
+ break; // found
+ }
+ }
+
+ if (j < pr_ebfs_numbuiltins) // found
+ {
+ pr_ebfs_builtins[j].funcno = funcno;
+ }
+ else
+ {
+ Con_DPrintf("Can not assign builtin number #%i to %s - function unknown\n", funcno, funcname);
+ }
+ }
+ }
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm end
+ }
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm start
+ if (pr_builtin_remap.value)
+ {
+ // check for unassigned functions and try to assign their default function number
+ for ( i=1 ; i < pr_ebfs_numbuiltins; i++)
+ {
+ if ((!pr_ebfs_builtins[i].funcno) && (pr_ebfs_builtins[i].default_funcno)) // unassigned and has a default number
+ {
+ // check if default number is already assigned to another function
+ for ( j=1 ; j < pr_ebfs_numbuiltins; j++)
+ {
+ if (pr_ebfs_builtins[j].funcno == pr_ebfs_builtins[i].default_funcno)
+ {
+ break; // number already assigned to another builtin function
+ }
+ }
+
+ if (j < pr_ebfs_numbuiltins) // already assigned
+ {
+ Con_DPrintf("Can not assign default builtin number #%i to %s - number is already assigned to %s\n",
+ pr_ebfs_builtins[i].default_funcno, pr_ebfs_builtins[i].funcname, pr_ebfs_builtins[j].funcname);
+ }
+ else
+ {
+ pr_ebfs_builtins[i].funcno = pr_ebfs_builtins[i].default_funcno;
+ }
+ }
+ // determine highest builtin number (when remapped)
+ if (pr_ebfs_builtins[i].funcno > pr_numbuiltins)
+ {
+ pr_numbuiltins = pr_ebfs_builtins[i].funcno;
+ }
+ }
+ }
+ pr_numbuiltins++;
+
+ // allocate and initialize builtin list for execution time
+ pr_builtins = Hunk_AllocName (pr_numbuiltins*sizeof(builtin_t), "builtins");
+ for ( i=0 ; i < pr_numbuiltins ; i++)
+ {
+ pr_builtins[i] = pr_ebfs_builtins[0].function;
+ }
+
+ // create builtin list for execution time and set cvars accordingly
+ Cvar_Set("pr_builtin_find", "0");
+// Cvar_Set("pr_checkextension", "0"); // 2001-10-20 Extension System by Lord Havoc/Maddes (DP compatibility)
+ for ( j=1 ; j < pr_ebfs_numbuiltins ; j++)
+ {
+ if (pr_ebfs_builtins[j].funcno) // only put assigned functions into builtin list
+ {
+ pr_builtins[pr_ebfs_builtins[j].funcno] = pr_ebfs_builtins[j].function;
+ }
+
+ if (pr_ebfs_builtins[j].default_funcno == PR_DEFAULT_FUNCNO_BUILTIN_FIND)
+ {
+ Cvar_SetValue("pr_builtin_find", pr_ebfs_builtins[j].funcno);
+ }
+
+// 2001-10-20 Extension System by Lord Havoc/Maddes (DP compatibility) start
+// not implemented yet
+/*
+ if (pr_ebfs_builtins[j].default_funcno == PR_DEFAULT_FUNCNO_EXTENSION_FIND)
+ {
+ Cvar_SetValue("pr_checkextension", pr_ebfs_builtins[j].funcno);
+ }
+*/
+// 2001-10-20 Extension System by Lord Havoc/Maddes (DP compatibility) end
+ }
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm end
+
+ for (i=0 ; inumglobaldefs ; i++)
+ {
+ pr_globaldefs[i].type = LittleShort (pr_globaldefs[i].type);
+ pr_globaldefs[i].ofs = LittleShort (pr_globaldefs[i].ofs);
+ pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name);
+ }
+
+ for (i=0 ; inumfielddefs ; i++)
+ {
+ pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type);
+ if (pr_fielddefs[i].type & DEF_SAVEGLOBAL)
+ Sys_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL");
+ pr_fielddefs[i].ofs = LittleShort (pr_fielddefs[i].ofs);
+ pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name);
+ }
+
+ for (i=0 ; inumglobals ; i++)
+ ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);
+
+ FindEdictFieldOffsets ();
+ EndFrame = 0;
+
+ if ((f = ED_FindFunction ("EndFrame")) != NULL)
+ EndFrame = (func_t)(f - pr_functions);
+}
+
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start
+/*
+=============
+PR_BuiltInList_f
+
+For debugging, prints all builtin functions with assigned and default number
+=============
+*/
+void PR_BuiltInList_f (void)
+{
+ int i;
+ char *partial;
+ int len;
+ int count;
+
+ if (Cmd_Argc() > 1)
+ {
+ partial = Cmd_Argv (1);
+ len = strlen(partial);
+ }
+ else
+ {
+ partial = NULL;
+ len = 0;
+ }
+
+ count=0;
+ for (i=1; i < pr_ebfs_numbuiltins; i++)
+ {
+ if (partial && Q_strncasecmp (partial, pr_ebfs_builtins[i].funcname, len))
+ {
+ continue;
+ }
+ count++;
+ Con_Printf ("%i(%i): %s\n", pr_ebfs_builtins[i].funcno, pr_ebfs_builtins[i].default_funcno, pr_ebfs_builtins[i].funcname);
+ }
+
+ Con_Printf ("------------\n");
+ if (partial)
+ {
+ Con_Printf ("%i beginning with \"%s\" out of ", count, partial);
+ }
+ Con_Printf ("%i builtin functions\n", i);
+}
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end
+
+/*
+===============
+PR_Init
+===============
+*/
+void PR_Init (void)
+{
+ Cmd_AddCommand ("edict", ED_PrintEdict_f);
+ Cmd_AddCommand ("edicts", ED_PrintEdicts);
+ Cmd_AddCommand ("edictcount", ED_Count);
+ Cmd_AddCommand ("profile", PR_Profile_f);
+ Cvar_RegisterVariable (&nomonsters);
+ Cvar_RegisterVariable (&gamecfg);
+ Cvar_RegisterVariable (&scratch1);
+ Cvar_RegisterVariable (&scratch2);
+ Cvar_RegisterVariable (&scratch3);
+ Cvar_RegisterVariable (&scratch4);
+ Cvar_RegisterVariable (&savedgamecfg);
+ Cvar_RegisterVariable (&saved1);
+ Cvar_RegisterVariable (&saved2);
+ Cvar_RegisterVariable (&saved3);
+ Cvar_RegisterVariable (&saved4);
+ // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start
+ Cvar_RegisterVariable (&pr_builtin_find);
+ Cvar_RegisterVariable (&pr_builtin_remap);
+ Cmd_AddCommand ("builtinlist", PR_BuiltInList_f); // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes
+ // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end
+}
+
+
+
+edict_t *EDICT_NUM(int n)
+{
+ if (n < 0 || n >= sv.max_edicts)
+ Sys_Error ("EDICT_NUM: bad number %i", n);
+ return (edict_t *)((byte *)sv.edicts+ (n)*pr_edict_size);
+}
+
+int NUM_FOR_EDICT(edict_t *e)
+{
+ int b;
+
+ b = (byte *)e - (byte *)sv.edicts;
+ b = b / pr_edict_size;
+
+ if (b < 0 || b >= sv.num_edicts)
+ Sys_Error ("NUM_FOR_EDICT: bad pointer");
+ return b;
+}
diff --git a/source/pr_exec.c b/source/pr_exec.c
new file mode 100644
index 0000000..41d9f5f
--- /dev/null
+++ b/source/pr_exec.c
@@ -0,0 +1,673 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+#include "thread.h"
+
+
+/*
+
+*/
+
+typedef struct
+{
+ int s;
+ dfunction_t *f;
+} prstack_t;
+
+#define MAX_STACK_DEPTH 32
+prstack_t pr_stack[MAX_STACK_DEPTH];
+int pr_depth;
+
+#define LOCALSTACK_SIZE 2048
+int localstack[LOCALSTACK_SIZE];
+int localstack_used;
+
+
+qboolean pr_trace;
+dfunction_t *pr_xfunction;
+int pr_xstatement;
+
+
+int pr_argc;
+
+char *pr_opnames[] =
+{
+"DONE",
+
+"MUL_F",
+"MUL_V",
+"MUL_FV",
+"MUL_VF",
+
+"DIV",
+
+"ADD_F",
+"ADD_V",
+
+"SUB_F",
+"SUB_V",
+
+"EQ_F",
+"EQ_V",
+"EQ_S",
+"EQ_E",
+"EQ_FNC",
+
+"NE_F",
+"NE_V",
+"NE_S",
+"NE_E",
+"NE_FNC",
+
+"LE",
+"GE",
+"LT",
+"GT",
+
+"INDIRECT",
+"INDIRECT",
+"INDIRECT",
+"INDIRECT",
+"INDIRECT",
+"INDIRECT",
+
+"ADDRESS",
+
+"STORE_F",
+"STORE_V",
+"STORE_S",
+"STORE_ENT",
+"STORE_FLD",
+"STORE_FNC",
+
+"STOREP_F",
+"STOREP_V",
+"STOREP_S",
+"STOREP_ENT",
+"STOREP_FLD",
+"STOREP_FNC",
+
+"RETURN",
+
+"NOT_F",
+"NOT_V",
+"NOT_S",
+"NOT_ENT",
+"NOT_FNC",
+
+"IF",
+"IFNOT",
+
+"CALL0",
+"CALL1",
+"CALL2",
+"CALL3",
+"CALL4",
+"CALL5",
+"CALL6",
+"CALL7",
+"CALL8",
+
+"STATE",
+
+"GOTO",
+
+"AND",
+"OR",
+
+"BITAND",
+"BITOR"
+};
+
+char *PR_GlobalString (int ofs);
+char *PR_GlobalStringNoContents (int ofs);
+
+
+//=============================================================================
+
+/*
+=================
+PR_PrintStatement
+=================
+*/
+void PR_PrintStatement (dstatement_t *s)
+{
+ int i;
+
+ if ( (unsigned)s->op < sizeof(pr_opnames)/sizeof(pr_opnames[0]))
+ {
+ Con_Printf ("%s ", pr_opnames[s->op]);
+ i = strlen(pr_opnames[s->op]);
+ for ( ; i<10 ; i++)
+ Con_Printf (" ");
+ }
+
+ if (s->op == OP_IF || s->op == OP_IFNOT)
+ Con_Printf ("%sbranch %i",PR_GlobalString(s->a),s->b);
+ else if (s->op == OP_GOTO)
+ {
+ Con_Printf ("branch %i",s->a);
+ }
+ else if ( (unsigned)(s->op - OP_STORE_F) < 6)
+ {
+ Con_Printf ("%s",PR_GlobalString(s->a));
+ Con_Printf ("%s", PR_GlobalStringNoContents(s->b));
+ }
+ else
+ {
+ if (s->a)
+ Con_Printf ("%s",PR_GlobalString(s->a));
+ if (s->b)
+ Con_Printf ("%s",PR_GlobalString(s->b));
+ if (s->c)
+ Con_Printf ("%s", PR_GlobalStringNoContents(s->c));
+ }
+ Con_Printf ("\n");
+}
+
+/*
+============
+PR_StackTrace
+============
+*/
+void PR_StackTrace (void)
+{
+ dfunction_t *f;
+ int i;
+
+ if (pr_depth == 0)
+ {
+ Con_Printf ("\n");
+ return;
+ }
+
+ pr_stack[pr_depth].f = pr_xfunction;
+ for (i=pr_depth ; i>=0 ; i--)
+ {
+ f = pr_stack[i].f;
+
+ if (!f)
+ {
+ Con_Printf ("\n");
+ }
+ else
+ Con_Printf ("%12s : %s\n", pr_strings + f->s_file, pr_strings + f->s_name);
+ }
+}
+
+
+/*
+============
+PR_Profile_f
+
+============
+*/
+void PR_Profile_f (void)
+{
+ dfunction_t *f, *best;
+ int max;
+ int num;
+ int i;
+
+ num = 0;
+ do
+ {
+ max = 0;
+ best = NULL;
+ for (i=0 ; inumfunctions ; i++)
+ {
+ f = &pr_functions[i];
+ if (f->profile > max)
+ {
+ max = f->profile;
+ best = f;
+ }
+ }
+ if (best)
+ {
+ if (num < 10)
+ Con_Printf ("%7i %s\n", best->profile, pr_strings+best->s_name);
+ num++;
+ best->profile = 0;
+ }
+ } while (best);
+}
+
+
+/*
+============
+PR_RunError
+
+Aborts the currently executing function
+============
+*/
+void PR_RunError (char *error, ...)
+{
+ va_list argptr;
+ char string[1024];
+
+ va_start (argptr,error);
+ vsprintf (string,error,argptr);
+ va_end (argptr);
+
+ PR_PrintStatement (pr_statements + pr_xstatement);
+ PR_StackTrace ();
+ Con_Printf ("%s\n", string);
+
+ pr_depth = 0; // dump the stack so host_error can shutdown functions
+
+ Host_Error ("Program error");
+}
+
+/*
+============================================================================
+PR_ExecuteProgram
+
+The interpretation main loop
+============================================================================
+*/
+
+/*
+====================
+PR_EnterFunction
+
+Returns the new program statement counter
+====================
+*/
+int PR_EnterFunction (dfunction_t *f)
+{
+ int i, j, c, o;
+
+ pr_stack[pr_depth].s = pr_xstatement;
+ pr_stack[pr_depth].f = pr_xfunction;
+ pr_depth++;
+ if (pr_depth >= MAX_STACK_DEPTH)
+ PR_RunError ("stack overflow");
+
+// save off any locals that the new function steps on
+ c = f->locals;
+ if (localstack_used + c > LOCALSTACK_SIZE)
+ PR_RunError ("PR_ExecuteProgram: locals stack overflow\n");
+
+ for (i=0 ; i < c ; i++)
+ localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i];
+ localstack_used += c;
+
+// copy parameters
+ o = f->parm_start;
+ for (i=0 ; inumparms ; i++)
+ {
+ for (j=0 ; jparm_size[i] ; j++)
+ {
+ ((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j];
+ o++;
+ }
+ }
+
+ pr_xfunction = f;
+ return f->first_statement - 1; // offset the s++
+}
+
+/*
+====================
+PR_LeaveFunction
+====================
+*/
+int PR_LeaveFunction (void)
+{
+ int i, c;
+
+ if (pr_depth <= 0)
+ Sys_Error ("prog stack underflow");
+
+// restore locals from the stack
+ c = pr_xfunction->locals;
+ localstack_used -= c;
+ if (localstack_used < 0)
+ PR_RunError ("PR_ExecuteProgram: locals stack underflow\n");
+
+ for (i=0 ; i < c ; i++)
+ ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i];
+
+// up stack
+ pr_depth--;
+ pr_xfunction = pr_stack[pr_depth].f;
+ return pr_stack[pr_depth].s;
+}
+
+
+/*
+====================
+PR_ExecuteProgram
+====================
+*/
+void PR_ExecuteProgram (func_t fnum)
+{
+ eval_t *a, *b, *c;
+ int s;
+ dstatement_t *st;
+ dfunction_t *f, *newf;
+ int runaway;
+ int i;
+ edict_t *ed;
+ int exitdepth;
+ eval_t *ptr;
+ // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start
+ char *funcname;
+ char *remaphint;
+ // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end
+
+ if (!fnum || fnum >= progs->numfunctions)
+ {
+ if (pr_global_struct->self)
+ ED_Print (PROG_TO_EDICT(pr_global_struct->self));
+ Host_Error ("PR_ExecuteProgram: NULL function");
+ }
+
+ f = &pr_functions[fnum];
+
+ runaway = 100000;
+ pr_trace = false;
+
+// make a stack frame
+ exitdepth = pr_depth;
+
+ s = PR_EnterFunction (f);
+
+while (1)
+{
+ s++; // next statement
+
+ st = &pr_statements[s];
+ a = (eval_t *)&pr_globals[st->a];
+ b = (eval_t *)&pr_globals[st->b];
+ c = (eval_t *)&pr_globals[st->c];
+
+ if (!--runaway)
+ PR_RunError ("runaway loop error");
+
+ pr_xfunction->profile++;
+ pr_xstatement = s;
+
+ if (pr_trace)
+ PR_PrintStatement (st);
+
+ switch (st->op)
+ {
+ case OP_ADD_F:
+ c->_float = a->_float + b->_float;
+ break;
+ case OP_ADD_V:
+ c->vector[0] = a->vector[0] + b->vector[0];
+ c->vector[1] = a->vector[1] + b->vector[1];
+ c->vector[2] = a->vector[2] + b->vector[2];
+ break;
+
+ case OP_SUB_F:
+ c->_float = a->_float - b->_float;
+ break;
+ case OP_SUB_V:
+ c->vector[0] = a->vector[0] - b->vector[0];
+ c->vector[1] = a->vector[1] - b->vector[1];
+ c->vector[2] = a->vector[2] - b->vector[2];
+ break;
+
+ case OP_MUL_F:
+ c->_float = a->_float * b->_float;
+ break;
+ case OP_MUL_V:
+ c->_float = a->vector[0]*b->vector[0]
+ + a->vector[1]*b->vector[1]
+ + a->vector[2]*b->vector[2];
+ break;
+ case OP_MUL_FV:
+ c->vector[0] = a->_float * b->vector[0];
+ c->vector[1] = a->_float * b->vector[1];
+ c->vector[2] = a->_float * b->vector[2];
+ break;
+ case OP_MUL_VF:
+ c->vector[0] = b->_float * a->vector[0];
+ c->vector[1] = b->_float * a->vector[1];
+ c->vector[2] = b->_float * a->vector[2];
+ break;
+
+ case OP_DIV_F:
+ c->_float = a->_float / b->_float;
+ break;
+
+ case OP_BITAND:
+ c->_float = (int)a->_float & (int)b->_float;
+ break;
+
+ case OP_BITOR:
+ c->_float = (int)a->_float | (int)b->_float;
+ break;
+
+
+ case OP_GE:
+ c->_float = a->_float >= b->_float;
+ break;
+ case OP_LE:
+ c->_float = a->_float <= b->_float;
+ break;
+ case OP_GT:
+ c->_float = a->_float > b->_float;
+ break;
+ case OP_LT:
+ c->_float = a->_float < b->_float;
+ break;
+ case OP_AND:
+ c->_float = a->_float && b->_float;
+ break;
+ case OP_OR:
+ c->_float = a->_float || b->_float;
+ break;
+
+ case OP_NOT_F:
+ c->_float = !a->_float;
+ break;
+ case OP_NOT_V:
+ c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2];
+ break;
+ case OP_NOT_S:
+ c->_float = !a->string || !pr_strings[a->string];
+ break;
+ case OP_NOT_FNC:
+ c->_float = !a->function;
+ break;
+ case OP_NOT_ENT:
+ c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts);
+ break;
+
+ case OP_EQ_F:
+ c->_float = a->_float == b->_float;
+ break;
+ case OP_EQ_V:
+ c->_float = (a->vector[0] == b->vector[0]) &&
+ (a->vector[1] == b->vector[1]) &&
+ (a->vector[2] == b->vector[2]);
+ break;
+ case OP_EQ_S:
+ c->_float = !strcmp(pr_strings+a->string,pr_strings+b->string);
+ break;
+ case OP_EQ_E:
+ c->_float = a->_int == b->_int;
+ break;
+ case OP_EQ_FNC:
+ c->_float = a->function == b->function;
+ break;
+
+
+ case OP_NE_F:
+ c->_float = a->_float != b->_float;
+ break;
+ case OP_NE_V:
+ c->_float = (a->vector[0] != b->vector[0]) ||
+ (a->vector[1] != b->vector[1]) ||
+ (a->vector[2] != b->vector[2]);
+ break;
+ case OP_NE_S:
+ c->_float = strcmp(pr_strings+a->string,pr_strings+b->string);
+ break;
+ case OP_NE_E:
+ c->_float = a->_int != b->_int;
+ break;
+ case OP_NE_FNC:
+ c->_float = a->function != b->function;
+ break;
+
+//==================
+ case OP_STORE_F:
+ case OP_STORE_ENT:
+ case OP_STORE_FLD: // integers
+ case OP_STORE_S:
+ case OP_STORE_FNC: // pointers
+ b->_int = a->_int;
+ break;
+ case OP_STORE_V:
+ b->vector[0] = a->vector[0];
+ b->vector[1] = a->vector[1];
+ b->vector[2] = a->vector[2];
+ break;
+
+ case OP_STOREP_F:
+ case OP_STOREP_ENT:
+ case OP_STOREP_FLD: // integers
+ case OP_STOREP_S:
+ case OP_STOREP_FNC: // pointers
+ ptr = (eval_t *)((byte *)sv.edicts + b->_int);
+ ptr->_int = a->_int;
+ break;
+ case OP_STOREP_V:
+ ptr = (eval_t *)((byte *)sv.edicts + b->_int);
+ ptr->vector[0] = a->vector[0];
+ ptr->vector[1] = a->vector[1];
+ ptr->vector[2] = a->vector[2];
+ break;
+
+ case OP_ADDRESS:
+ ed = PROG_TO_EDICT(a->edict);
+
+ if (ed == (edict_t *)sv.edicts && sv.state == ss_active)
+ PR_RunError ("assignment to world entity");
+ c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)sv.edicts;
+ break;
+
+ case OP_LOAD_F:
+ case OP_LOAD_FLD:
+ case OP_LOAD_ENT:
+ case OP_LOAD_S:
+ case OP_LOAD_FNC:
+ ed = PROG_TO_EDICT(a->edict);
+ a = (eval_t *)((int *)&ed->v + b->_int);
+ c->_int = a->_int;
+ break;
+
+ case OP_LOAD_V:
+ ed = PROG_TO_EDICT(a->edict);
+ a = (eval_t *)((int *)&ed->v + b->_int);
+ c->vector[0] = a->vector[0];
+ c->vector[1] = a->vector[1];
+ c->vector[2] = a->vector[2];
+ break;
+
+//==================
+
+ case OP_IFNOT:
+ if (!a->_int)
+ s += st->b - 1; // offset the s++
+ break;
+
+ case OP_IF:
+ if (a->_int)
+ s += st->b - 1; // offset the s++
+ break;
+
+ case OP_GOTO:
+ s += st->a - 1; // offset the s++
+ break;
+
+ case OP_CALL0:
+ case OP_CALL1:
+ case OP_CALL2:
+ case OP_CALL3:
+ case OP_CALL4:
+ case OP_CALL5:
+ case OP_CALL6:
+ case OP_CALL7:
+ case OP_CALL8:
+ pr_argc = st->op - OP_CALL0;
+ if (!a->function)
+ PR_RunError ("NULL function");
+ newf = &pr_functions[a->function];
+ if (newf->first_statement < 0)
+ { // negative statements are built in functions
+ i = -newf->first_statement;
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start
+ if ( (i >= pr_numbuiltins)
+ || (pr_builtins[i] == pr_ebfs_builtins[0].function) )
+ {
+ funcname = pr_strings + newf->s_name;
+ if (pr_builtin_remap.value)
+ {
+ remaphint = NULL;
+ }
+ else
+ {
+ remaphint = "Try \"builtin remapping\" by setting PR_BUILTIN_REMAP to 1\n";
+ }
+ PR_RunError ("Bad builtin call number %i for %s\nPlease contact the PROGS.DAT author\nUse BUILTINLIST to see all assigned builtin functions\n%s", i, funcname, remaphint);
+ }
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end
+ pr_builtins[i] ();
+ break;
+ }
+
+ s = PR_EnterFunction (newf);
+ break;
+
+ case OP_DONE:
+ case OP_RETURN:
+ pr_globals[OFS_RETURN] = pr_globals[st->a];
+ pr_globals[OFS_RETURN+1] = pr_globals[st->a+1];
+ pr_globals[OFS_RETURN+2] = pr_globals[st->a+2];
+
+ s = PR_LeaveFunction ();
+ if (pr_depth == exitdepth)
+ return; // all done
+ break;
+
+ case OP_STATE:
+ ed = PROG_TO_EDICT(pr_global_struct->self);
+ ed->v.nextthink = pr_global_struct->time + 0.1;
+ if (a->_float != ed->v.frame)
+ {
+ ed->v.frame = a->_float;
+ }
+ ed->v.think = b->function;
+ break;
+
+ default:
+ PR_RunError ("Bad opcode %i", st->op);
+ }
+}
+
+}
diff --git a/source/progdefs.h b/source/progdefs.h
new file mode 100644
index 0000000..1061081
--- /dev/null
+++ b/source/progdefs.h
@@ -0,0 +1,169 @@
+
+/* file generated by qcc, do not modify */
+
+typedef struct
+{ int pad[28];
+ int self;
+ int other;
+ int world;
+ float time;
+ float frametime;
+ float force_retouch;
+ string_t mapname;
+ float deathmatch;
+ float coop;
+ float teamplay;
+ float serverflags;
+ float rounds;
+ float rounds_change;
+ float parm1;
+ float parm2;
+ float parm3;
+ float parm4;
+ float parm5;
+ float parm6;
+ float parm7;
+ float parm8;
+ float parm9;
+ float parm10;
+ float parm11;
+ float parm12;
+ float parm13;
+ float parm14;
+ float parm15;
+ float parm16;
+ vec3_t v_forward;
+ vec3_t v_up;
+ vec3_t v_right;
+ float trace_allsolid;
+ float trace_startsolid;
+ float trace_fraction;
+ vec3_t trace_endpos;
+ vec3_t trace_plane_normal;
+ float trace_plane_dist;
+ int trace_ent;
+ float trace_inopen;
+ float trace_inwater;
+ int msg_entity;
+ func_t main;
+ func_t StartFrame;
+ func_t EndFrame;
+ func_t PlayerPreThink;
+ func_t PlayerPostThink;
+ func_t ClientKill;
+ func_t ClientConnect;
+ func_t PutClientInServer;
+ func_t ClientDisconnect;
+ func_t SetNewParms;
+ func_t SetChangeParms;
+ func_t ParseClientCommand;
+ string_t CMD_STRING;
+ func_t Soft_Restart;
+} globalvars_t;
+
+typedef struct
+{
+ float modelindex;
+ vec3_t absmin;
+ vec3_t absmax;
+ float ltime;
+ float movetype;
+ float solid;
+ vec3_t origin;
+ vec3_t oldorigin;
+ vec3_t velocity;
+ vec3_t angles;
+ vec3_t avelocity;
+ vec3_t punchangle;
+ string_t classname;
+ string_t model;
+ float frame;
+ float skin;
+ float iframetime;
+ float effects;
+ vec3_t mins;
+ vec3_t maxs;
+ vec3_t size;
+ func_t touch;
+ func_t use;
+ func_t think;
+ func_t blocked;
+ float nextthink;
+ int groundentity;
+ float health;
+ float points;
+ float kills;
+ float weapon;
+ string_t weaponmodel;
+ string_t weapon2model;
+ float weaponframe;
+ float weapon2frame;
+ float currentammo;
+ float currentmag;
+ float zoom;
+ float weaponskin;
+ float weapon2skin;
+ float primary_grenades;
+ float secondary_grenades;
+ float grenades;
+ float perks;
+ float takedamage;
+ int chain;
+ float deadflag;
+ vec3_t view_ofs;
+ float button0;
+ float button1;
+ float button2;
+ float button3;
+ float button4;
+ float button5;
+ float button6;
+ float button7;
+ float button8;
+ float impulse;
+ float fixangle;
+ vec3_t v_angle;
+ float idealpitch;
+ string_t netname;
+ int enemy;
+ float flags;
+ float colormap;
+ float team;
+ float max_health;
+ float teleport_time;
+ float waterlevel;
+ float watertype;
+ float ideal_yaw;
+ float yaw_speed;
+ int aiment;
+ int head;
+ int larm;
+ int rarm;
+ int goalentity;
+ float spawnflags;
+ string_t target;
+ string_t targetname;
+ float bleed_out;
+ float progress_bar;
+ int dmg_inflictor;
+ int owner;
+ vec3_t movedir;
+ string_t message;
+ float sounds;
+ string_t noise;
+ string_t noise1;
+ string_t noise2;
+ string_t noise3;
+ float x2_icon;
+ float insta_icon;
+ vec3_t ADS_Offset;
+ vec3_t Flash_Offset;
+ float Flash_Size;
+ string_t Weapon_Name;
+ string_t Weapon_Name_Touch;
+ float currentmag2;
+ float maxspeed;
+ float facingenemy;
+} entvars_t;
+
+#define PROGHEADER_CRC 14116
diff --git a/source/progs.h b/source/progs.h
new file mode 100644
index 0000000..6985511
--- /dev/null
+++ b/source/progs.h
@@ -0,0 +1,158 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "pr_comp.h" // defs shared with qcc
+#include "progdefs.h" // generated by program cdefs
+
+typedef union eval_s
+{
+ string_t string;
+ float _float;
+ float vector[3];
+ func_t function;
+ int _int;
+ int edict;
+} eval_t;
+
+#define MAX_ENT_LEAFS 16
+typedef struct edict_s
+{
+ qboolean free;
+ link_t area; // linked to a division node or leaf
+
+ int num_leafs;
+ short leafnums[MAX_ENT_LEAFS];
+
+ entity_state_t baseline;
+
+ float freetime; // sv.time when the object was freed
+ entvars_t v; // C exported fields from progs
+// other fields from progs come immediately after
+} edict_t;
+#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area)
+extern int eval_gravity;
+extern int eval_idealpitch, eval_pitch_speed;
+// Half_life modes. Crow_bar
+extern int eval_renderamt, eval_rendermode, eval_rendercolor;
+
+//============================================================================
+
+extern dprograms_t *progs;
+extern dfunction_t *pr_functions;
+extern char *pr_strings;
+extern ddef_t *pr_globaldefs;
+extern ddef_t *pr_fielddefs;
+extern dstatement_t *pr_statements;
+extern globalvars_t *pr_global_struct;
+extern float *pr_globals; // same as pr_global_struct
+
+extern int pr_edict_size; // in bytes
+
+//============================================================================
+
+void PR_Init (void);
+
+void PR_ExecuteProgram (func_t fnum);
+void PR_LoadProgs (void);
+
+void PR_Profile_f (void);
+
+edict_t *ED_Alloc (void);
+void ED_Free (edict_t *ed);
+
+char *ED_NewString (char *string);
+// returns a copy of the string allocated from the server's string heap
+
+void ED_Print (edict_t *ed);
+void ED_Write (FILE *f, edict_t *ed);
+char *ED_ParseEdict (char *data, edict_t *ent);
+
+void ED_WriteGlobals (FILE *f);
+void ED_ParseGlobals (char *data);
+
+void ED_LoadFromFile (char *data);
+
+//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size))
+//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size)
+
+edict_t *EDICT_NUM(int n);
+int NUM_FOR_EDICT(edict_t *e);
+
+#define NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size))
+
+#define EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts)
+#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e))
+
+//============================================================================
+
+#define G_FLOAT(o) (pr_globals[o])
+#define G_INT(o) (*(int *)&pr_globals[o])
+#define G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o]))
+#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o))
+#define G_VECTOR(o) (&pr_globals[o])
+#define G_STRING(o) (pr_strings + *(string_t *)&pr_globals[o])
+#define G_FUNCTION(o) (*(func_t *)&pr_globals[o])
+
+#define E_FLOAT(e,o) (((float*)&e->v)[o])
+#define E_INT(e,o) (*(int *)&((float*)&e->v)[o])
+#define E_VECTOR(e,o) (&((float*)&e->v)[o])
+#define E_STRING(e,o) (pr_strings + *(string_t *)&((float*)&e->v)[o])
+
+extern int type_size[8];
+
+typedef void (*builtin_t) (void);
+extern builtin_t *pr_builtins;
+extern int pr_numbuiltins;
+
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start
+typedef struct ebfs_builtin_s
+{
+ int default_funcno;
+ char *funcname;
+ builtin_t function;
+ int funcno;
+} ebfs_builtin_t;
+
+extern ebfs_builtin_t pr_ebfs_builtins[];
+extern int pr_ebfs_numbuiltins;
+
+#define PR_DEFAULT_FUNCNO_BUILTIN_FIND 100
+
+extern cvar_t pr_builtin_find;
+extern cvar_t pr_builtin_remap;
+
+#define PR_DEFAULT_FUNCNO_EXTENSION_FIND 99 // 2001-10-20 Extension System by Lord Havoc/Maddes
+// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end
+
+extern int pr_argc;
+
+extern qboolean pr_trace;
+extern dfunction_t *pr_xfunction;
+extern int pr_xstatement;
+
+extern unsigned short pr_crc;
+
+void PR_RunError (char *error, ...);
+
+void ED_PrintEdicts (void);
+void ED_PrintNum (int ent);
+
+//eval_t *GetEdictFieldValue(edict_t *ed, char *field);
+#define GETEDICTFIELDVALUE(ed, fieldoffset) (fieldoffset ? (eval_t *)((byte *)&ed->v + fieldoffset) : NULL)
diff --git a/source/protocol.h b/source/protocol.h
new file mode 100644
index 0000000..d5d79e9
--- /dev/null
+++ b/source/protocol.h
@@ -0,0 +1,185 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// protocol.h -- communications protocols
+
+#define PROTOCOL_VERSION 15
+
+// if the high bit of the servercmd is set, the low bits are fast update flags:
+#define U_MOREBITS (1<<0)
+#define U_ORIGIN1 (1<<1)
+#define U_ORIGIN2 (1<<2)
+#define U_ORIGIN3 (1<<3)
+#define U_ANGLE2 (1<<4)
+#define U_NOLERP (1<<5) // don't interpolate movement
+#define U_FRAME (1<<6)
+#define U_SIGNAL (1<<7) // just differentiates from other updates
+
+// svc_update can pass all of the fast update bits, plus more
+#define U_EXTEND1 (1<<8)
+#define U_ANGLE1 (1<<9)
+#define U_ANGLE3 (1<<10)
+#define U_MODEL (1<<11)
+#define U_COLORMAP (1<<12)
+#define U_SKIN (1<<13)
+#define U_EFFECTS (1<<14)
+
+
+// Tomaz - QC Alpha Scale Glow Control Begin
+#define U_LONGENTITY (1<<15)//blubs here, U_EXTEND1 used to be here, but it needs to be in the byte above, so moved it to the 1<<8 position, and moved the rest down
+#define U_RENDERMODE (1<<16)
+#define U_RENDERAMT (1<<17)
+#define U_RENDERCOLOR1 (1<<18)
+#define U_RENDERCOLOR2 (1<<19)
+#define U_RENDERCOLOR3 (1<<20)
+#define U_EXTEND2 (1<<21) // another byte to follow
+#define U_FRAMETIME (1<<22) // another byte to follow
+// Tomaz - QC Alpha Scale Glow Control End
+
+
+#define SU_VIEWHEIGHT (1<<0)
+#define SU_IDEALPITCH (1<<1)
+#define SU_PUNCH1 (1<<2)
+#define SU_PUNCH2 (1<<3)
+#define SU_PUNCH3 (1<<4)
+#define SU_VELOCITY1 (1<<5)
+#define SU_VELOCITY2 (1<<6)
+#define SU_VELOCITY3 (1<<7)
+#define SU_WEAPONSKIN (1<<7)
+#define SU_PERKS (1<<9)
+#define SU_ONGROUND (1<<10) // no data follows, the bit is it
+#define SU_INWATER (1<<11) // no data follows, the bit is it
+#define SU_WEAPONFRAME (1<<12)
+#define SU_WEAPON (1<<14)
+#define SU_PRIGRENADES (1<<15)
+#define SU_SECGRENADES (1<<16)
+#define SU_GRENADES (1<<13)
+
+// a sound with no channel is a local only sound
+#define SND_VOLUME (1<<0) // a byte
+#define SND_ATTENUATION (1<<1) // a byte
+#define SND_LOOPING (1<<2) // a long
+
+
+// defaults for clientinfo messages
+#define DEFAULT_VIEWHEIGHT 22
+
+
+// game types sent by serverinfo
+// these determine which intermission screen plays
+#define GAME_COOP 0
+#define GAME_DEATHMATCH 1
+
+//==================
+// note that there are some defs.qc that mirror to these numbers
+// also related to svc_strings[] in cl_parse
+//==================
+
+//
+// server to client
+//
+#define svc_bad 0
+#define svc_nop 1
+#define svc_disconnect 2
+#define svc_updatestat 3 // [byte] [long]
+#define svc_version 4 // [long] server version
+#define svc_setview 5 // [short] entity number
+#define svc_sound 6 //
+#define svc_time 7 // [float] server time
+#define svc_print 8 // [string] null terminated string
+#define svc_stufftext 9 // [string] stuffed into client's console buffer
+ // the string should be \n terminated
+#define svc_setangle 10 // [angle3] set the view angle to this absolute value
+
+#define svc_serverinfo 11 // [long] version
+ // [string] signon string
+ // [string]..[0]model cache
+ // [string]...[0]sounds cache
+#define svc_lightstyle 12 // [byte] [string]
+#define svc_updatename 13 // [byte] [string]
+#define svc_updatepoints 14 // [byte] [short]
+#define svc_clientdata 15 //
+#define svc_stopsound 16 //
+#define svc_particle 18 // [vec3]
+#define svc_damage 19
+
+#define svc_spawnstatic 20
+// svc_spawnbinary 21
+#define svc_spawnbaseline 22
+
+#define svc_temp_entity 23
+
+#define svc_setpause 24 // [byte] on / off
+#define svc_signonnum 25 // [byte] used for the signon sequence
+
+#define svc_centerprint 26 // [string] to put in center of the screen
+
+#define svc_spawnstaticsound 29 // [coord3] [byte] samp [byte] vol [byte] aten
+
+#define svc_intermission 30 // [string] music
+#define svc_finale 31 // [string] music [string] text
+
+#define svc_cdtrack 32 // [byte] track [byte] looptrack
+#define svc_sellscreen 33
+
+#define svc_cutscene 34
+#define svc_weaponfire 35
+#define svc_hitmark 36
+#define svc_skybox 37 // [string] skyname
+#define svc_useprint 38 // [string] to put in center of the screen
+#define svc_updatekills 39 // [string] to put in center of the screen
+#define svc_limbupdate 40
+#define svc_fog 41 // [byte] start [byte] end [byte] red [byte] green [byte] blue [float] time
+#define svc_bspdecal 42 // [string] name [byte] decal_size [coords] pos
+#define svc_achievement 43 // [string] name [byte] decal_size [coords] pos
+#define svc_songegg 44 // [string] track name
+#define svc_maxammo 45
+
+
+//
+// client to server
+//
+#define clc_bad 0
+#define clc_nop 1
+#define clc_disconnect 2
+#define clc_move 3 // [usercmd_t]
+#define clc_stringcmd 4 // [string] message
+
+
+//
+// temp entity events
+//
+#define TE_SPIKE 0
+#define TE_SUPERSPIKE 1
+#define TE_GUNSHOT 2
+#define TE_EXPLOSION 3
+#define TE_TAREXPLOSION 4
+#define TE_LIGHTNING1 5
+#define TE_LIGHTNING2 6
+#define TE_WIZSPIKE 7
+#define TE_KNIGHTSPIKE 8
+#define TE_LIGHTNING3 9
+#define TE_LAVASPLASH 10
+#define TE_TELEPORT 11
+#define TE_EXPLOSION2 12
+// PGM 01/21/97
+#define TE_BEAM 13
+// PGM 01/21/97
+#define TE_RAYSPLASHGREEN 14
+#define TE_RAYSPLASHRED 15
diff --git a/source/psp/.bash_history b/source/psp/.bash_history
new file mode 100644
index 0000000..8530933
--- /dev/null
+++ b/source/psp/.bash_history
@@ -0,0 +1,2 @@
+make -f makephat clean install
+make -f makephat install
\ No newline at end of file
diff --git a/source/psp/MakePHAT b/source/psp/MakePHAT
new file mode 100644
index 0000000..cac4d65
--- /dev/null
+++ b/source/psp/MakePHAT
@@ -0,0 +1,130 @@
+PSPSDK = $(shell psp-config --pspsdk-path)
+PSPLIBSDIR = $(PSPSDK)/..
+
+TARGET = nzportable
+
+PSP_EBOOT_TITLE = Nazi Zombies Portable Reboot
+
+PSP_EBOOT_ICON = pics/icon.png
+PSP_EBOOT_SND0 = pics/snd0.at3
+PSP_EBOOT_PIC1 = pics/pic.png
+
+PSP_FW_VERSION=660
+
+MODE=-DKERNEL_MODE
+
+
+BUILD_PRX = 1
+
+COMMON_OBJS = \
+ battery.o \
+ ../thread.o \
+ VramExt.o \
+ input.o \
+ main.o \
+ math.o \
+ sound.o \
+ system.o \
+ module.o \
+ network.o \
+ network_psp.o \
+ gethost.o \
+ fnmatch.o \
+ cd.o \
+ mp3.o \
+ Random.o \
+ \
+ ../chase.o \
+ ../cl_demo.o \
+ ../cl_input.o \
+ ../cl_main.o \
+ ../cl_parse.o \
+ ../cl_tent.o \
+ ../cl_slist.o \
+ ../cmd.o \
+ ../common.o \
+ ../console.o \
+ ../crc.o \
+ ../cvar.o \
+ ../host.o \
+ ../host_cmd.o \
+ ../keys.o \
+ ../mathlib.o \
+ ../menu.o \
+ ../net_dgrm.o \
+ ../net_loop.o \
+ ../net_main.o \
+ ../net_vcr.o \
+ ../pr_cmds.o \
+ ../pr_edict.o \
+ ../pr_exec.o \
+ ../snd_dma.o \
+ ../snd_mem.o \
+ ../snd_mix.o \
+ ../cl_hud.o \
+ ../sv_main.o \
+ ../sv_move.o \
+ ../sv_phys.o \
+ ../sv_user.o \
+ ../view.o \
+ ../wad.o \
+ ../world.o \
+ ../zone.o \
+ ../crypter.o
+
+HARDWARE_VIDEO_ONLY_OBJS = \
+ wad3.o \
+ clipping.o \
+ vram.o \
+ video_hardware.o \
+ video_hardware_resample.o \
+ video_hardware_images.o \
+ video_hardware_fullbright.o \
+ video_hardware_hlmdl.o \
+ video_hardware_draw.o \
+ video_hardware_entity_fragment.o \
+ video_hardware_QMB.o \
+ video_hardware_decals_QMB.o \
+ video_hardware_part.o \
+ video_hardware_light.o \
+ video_hardware_main.o \
+ video_hardware_mesh.o \
+ video_hardware_mhex2.o \
+ video_hardware_misc.o \
+ video_hardware_model.o \
+ video_hardware_screen.o \
+ video_hardware_surface.o \
+ video_hardware_warp.o \
+ video_hardware_fog.o \
+ video_hardware_dxtn.o
+HARDWARE_VIDEO_ONLY_FLAGS = -DPSP_HARDWARE_VIDEO
+
+
+
+OBJS = $(COMMON_OBJS) $(HARDWARE_VIDEO_ONLY_OBJS)
+
+
+#LIBS = -lpspaudiolib -lpspaudio -lpspgum -lpspgu -lpsprtc -lpsppower -lpspwlan -lstdc++ -lm
+
+GU_LIBS = -lpspgum_vfpu -lpspvfpu -lpspgu -lpspvram
+AUDIO_LIBS = -lpspaudiolib -lpspaudio -lpspmp3 m33libs/libpspaudiocodec.a m33libs/libpspkubridge.a
+
+MISC_LIBS = -lpsprtc -lpspmath -lpsppower -lpsphprm -ljpeg -lpng m33libs/libz.a iridlibs/libPerf.a
+NET_LIBS = -lpspwlan -lpspnet_adhoc -lpspnet_adhocctl
+STD_LIBS = -lstdc++ -lm -lc
+LIBS = $(GU_LIBS) $(AUDIO_LIBS) $(MISC_LIBS) $(STD_LIBS) $(NET_LIBS)
+
+CFLAGS = -ffast-math -O3 -G0 -Wall -Did386="0" -DPSP $(MODE) $(HARDWARE_VIDEO_ONLY_FLAGS) -DSWIZZLE32 -DPSP_MP3_HWDECODE -DFULLBRIGHT -DHL_RENDER -Wno-strict-aliasing -DPSP_VFPU
+CXXFLAGS = -fno-rtti -Wcast-qual -Wno-write-strings -Wno-sign-compare -Wno-strict-aliasing
+ASFLAGS = $(CFLAGS) -c
+
+include $(PSPSDK)/lib/build.mak
+
+ifneq ($(VS_PATH),)
+CC = vs-psp-gcc
+CXX = vs-psp-g++
+endif
+
+install: EBOOT.PBP
+ mv EBOOT.PBP ../../../../psp/nzportable/
+ @echo DONE
diff --git a/source/psp/MakeSLIM b/source/psp/MakeSLIM
new file mode 100644
index 0000000..7dda2e3
--- /dev/null
+++ b/source/psp/MakeSLIM
@@ -0,0 +1,132 @@
+PSPSDK = $(shell psp-config --pspsdk-path)
+PSPLIBSDIR = $(PSPSDK)/..
+
+TARGET = nzportable
+
+PSP_EBOOT_TITLE = Nazi Zombies Portable Reboot
+
+PSP_EBOOT_ICON = pics/icon.png
+PSP_EBOOT_SND0 = pics/snd0.at3
+PSP_EBOOT_PIC1 = pics/pic.png
+
+PSP_FW_VERSION=660
+
+PSP_LARGE_MEMORY = 1
+
+MODE=-DKERNEL_MODE
+
+
+BUILD_PRX = 1
+
+COMMON_OBJS = \
+ battery.o \
+ ../thread.o \
+ VramExt.o \
+ input.o \
+ main.o \
+ math.o \
+ sound.o \
+ system.o \
+ module.o \
+ network.o \
+ network_psp.o \
+ gethost.o \
+ fnmatch.o \
+ cd.o \
+ mp3.o \
+ Random.o \
+ \
+ ../chase.o \
+ ../cl_demo.o \
+ ../cl_input.o \
+ ../cl_main.o \
+ ../cl_parse.o \
+ ../cl_tent.o \
+ ../cl_slist.o \
+ ../cmd.o \
+ ../common.o \
+ ../console.o \
+ ../crc.o \
+ ../cvar.o \
+ ../host.o \
+ ../host_cmd.o \
+ ../keys.o \
+ ../mathlib.o \
+ ../menu.o \
+ ../net_dgrm.o \
+ ../net_loop.o \
+ ../net_main.o \
+ ../net_vcr.o \
+ ../pr_cmds.o \
+ ../pr_edict.o \
+ ../pr_exec.o \
+ ../snd_dma.o \
+ ../snd_mem.o \
+ ../snd_mix.o \
+ ../cl_hud.o \
+ ../sv_main.o \
+ ../sv_move.o \
+ ../sv_phys.o \
+ ../sv_user.o \
+ ../view.o \
+ ../wad.o \
+ ../world.o \
+ ../zone.o \
+ ../crypter.o
+
+HARDWARE_VIDEO_ONLY_OBJS = \
+ wad3.o \
+ clipping.o \
+ vram.o \
+ video_hardware.o \
+ video_hardware_resample.o \
+ video_hardware_images.o \
+ video_hardware_fullbright.o \
+ video_hardware_hlmdl.o \
+ video_hardware_draw.o \
+ video_hardware_entity_fragment.o \
+ video_hardware_QMB.o \
+ video_hardware_decals_QMB.o \
+ video_hardware_part.o \
+ video_hardware_light.o \
+ video_hardware_main.o \
+ video_hardware_mesh.o \
+ video_hardware_mhex2.o \
+ video_hardware_misc.o \
+ video_hardware_model.o \
+ video_hardware_screen.o \
+ video_hardware_surface.o \
+ video_hardware_warp.o \
+ video_hardware_fog.o \
+ video_hardware_dxtn.o
+HARDWARE_VIDEO_ONLY_FLAGS = -DPSP_HARDWARE_VIDEO
+
+
+
+OBJS = $(COMMON_OBJS) $(HARDWARE_VIDEO_ONLY_OBJS)
+
+
+#LIBS = -lpspaudiolib -lpspaudio -lpspgum -lpspgu -lpsprtc -lpsppower -lpspwlan -lstdc++ -lm
+
+GU_LIBS = -lpspgum_vfpu -lpspvfpu -lpspgu -lpspvram
+AUDIO_LIBS = -lpspaudiolib -lpspaudio -lpspmp3 m33libs/libpspaudiocodec.a m33libs/libpspkubridge.a
+
+MISC_LIBS = -lpsprtc -lpsppower -lpspmath -lpsphprm -ljpeg -lpng m33libs/libz.a iridlibs/libPerf.a
+NET_LIBS = -lpspwlan -lpspnet_adhoc -lpspnet_adhocctl
+STD_LIBS = -lstdc++ -lm -lc
+LIBS = $(GU_LIBS) $(AUDIO_LIBS) $(MISC_LIBS) $(STD_LIBS) $(NET_LIBS)
+
+CFLAGS = -ffast-math -O3 -G0 -Wall -Did386="0" -DPSP $(MODE) $(HARDWARE_VIDEO_ONLY_FLAGS) -DSWIZZLE32 -DSLIM -DPSP_MP3_HWDECODE -DFULLBRIGHT -DHL_RENDER -Wno-strict-aliasing -DPSP_VFPU
+CXXFLAGS = -fno-rtti -Wcast-qual -Wno-write-strings -Wno-sign-compare -Wno-strict-aliasing
+ASFLAGS = $(CFLAGS) -c
+
+include $(PSPSDK)/lib/build.mak
+
+ifneq ($(VS_PATH),)
+CC = vs-psp-gcc
+CXX = vs-psp-g++
+endif
+
+install: EBOOT.PBP
+ mv EBOOT.PBP ../../../../psp/nzportable/EBOOT2000.PBP
+ @echo DONE
diff --git a/source/psp/Nazi-Zombies-Portable.cbp b/source/psp/Nazi-Zombies-Portable.cbp
new file mode 100644
index 0000000..8115965
--- /dev/null
+++ b/source/psp/Nazi-Zombies-Portable.cbp
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/psp/Nazi-Zombies-Portable.layout b/source/psp/Nazi-Zombies-Portable.layout
new file mode 100644
index 0000000..3cc9da4
--- /dev/null
+++ b/source/psp/Nazi-Zombies-Portable.layout
@@ -0,0 +1,444 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/psp/Random.cpp b/source/psp/Random.cpp
new file mode 100644
index 0000000..53cce65
--- /dev/null
+++ b/source/psp/Random.cpp
@@ -0,0 +1,118 @@
+#include
+#include "Random.h"
+
+#define IA 16807
+#define IM 2147483647
+#define IQ 127773
+#define IR 2836
+#define NTAB 32
+#define NDIV (1+(IM-1)/NTAB)
+
+static long idum = 0;
+
+void SeedRandomNumberGenerator(long lSeed)
+{
+ if (lSeed)
+ {
+ idum = lSeed;
+ }
+ else
+ {
+ idum = -time(NULL);
+ }
+ if (1000 < idum)
+ {
+ idum = -idum;
+ }
+ else if (-1000 < idum)
+ {
+ idum -= 22261048;
+ }
+}
+
+long ran1(void)
+{
+ int j;
+ long k;
+ static long iy=0;
+ static long iv[NTAB];
+
+ if (idum <= 0 || !iy)
+ {
+ if (-(idum) < 1) idum=1;
+ else idum = -(idum);
+ for (j=NTAB+7;j>=0;j--)
+ {
+ k=(idum)/IQ;
+ idum=IA*(idum-k*IQ)-IR*k;
+ if (idum < 0) idum += IM;
+ if (j < NTAB) iv[j] = idum;
+ }
+ iy=iv[0];
+ }
+ k=(idum)/IQ;
+ idum=IA*(idum-k*IQ)-IR*k;
+ if (idum < 0) idum += IM;
+ j=iy/NDIV;
+ iy=iv[j];
+ iv[j] = idum;
+
+ return iy;
+}
+
+// fran1 -- return a random floating-point number on the interval [0,1)
+//
+#define AM (1.0/IM)
+#define EPS 1.2e-7
+#define RNMX (1.0-EPS)
+float fran1(void)
+{
+ float temp = (float)AM*ran1();
+ if (temp > RNMX) return (float)RNMX;
+ else return temp;
+}
+
+float RandomFloat( float flLow, float flHigh )
+{
+ if (idum == 0)
+ {
+ SeedRandomNumberGenerator(0);
+ }
+
+ float fl = fran1(); // float in [0,1)
+ return (fl * (flHigh-flLow)) + flLow; // float in [low,high)
+}
+
+long RandomLong( long lLow, long lHigh )
+{
+ if (idum == 0)
+ {
+ SeedRandomNumberGenerator(0);
+ }
+
+ unsigned long maxAcceptable;
+ unsigned long x = lHigh-lLow+1;
+ unsigned long n;
+ if (x <= 0 || MAX_RANDOM_RANGE < x-1)
+ {
+ return lLow;
+ }
+
+ // The following maps a uniform distribution on the interval [0,MAX_RANDOM_RANGE]
+ // to a smaller, client-specified range of [0,x-1] in a way that doesn't bias
+ // the uniform distribution unfavorably. Even for a worst case x, the loop is
+ // guaranteed to be taken no more than half the time, so for that worst case x,
+ // the average number of times through the loop is 2. For cases where x is
+ // much smaller than MAX_RANDOM_RANGE, the average number of times through the
+ // loop is very close to 1.
+ //
+ maxAcceptable = MAX_RANDOM_RANGE - ((MAX_RANDOM_RANGE+1) % x );
+ do
+ {
+ n = ran1();
+ } while (n > maxAcceptable);
+
+ return lLow + (n % x);
+}
+
+
diff --git a/source/psp/Random.h b/source/psp/Random.h
new file mode 100644
index 0000000..d290f70
--- /dev/null
+++ b/source/psp/Random.h
@@ -0,0 +1,18 @@
+#ifndef RANDOM_H
+#define RANDOM_H
+
+// the random number seeding is automatic
+#define MAX_RANDOM_RANGE 0x7FFFFFFFUL
+
+// restarts random generator
+// setting lSeed to 0 causes the current time to be used as the seed
+// random number generator will automatically seed itself on first use with current time if this is not called
+void SeedRandomNumberGenerator(long lSeed = 0);
+
+// returns a random integer of range [low, high]
+long RandomLong( long lLow, long lHigh );
+
+// returns a random float of range [low, high)
+float RandomFloat( float flLow, float flHigh );
+
+#endif // RANDOM_H
diff --git a/source/psp/VramExt.S b/source/psp/VramExt.S
new file mode 100644
index 0000000..0b0c1d5
--- /dev/null
+++ b/source/psp/VramExt.S
@@ -0,0 +1,7 @@
+ .set noreorder
+
+#include "pspstub.s"
+
+ STUB_START "VramExt",0x40090000,0x00010005
+ STUB_FUNC 0xA5853E1E,VramSetSize
+ STUB_END
\ No newline at end of file
diff --git a/source/psp/battery.cpp b/source/psp/battery.cpp
new file mode 100644
index 0000000..cac813b
--- /dev/null
+++ b/source/psp/battery.cpp
@@ -0,0 +1,107 @@
+/*
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+
+extern cvar_t show_bat;
+
+#include "battery.hpp"
+
+namespace quake
+{
+ namespace battery
+ {
+ // The previous battery level.
+ static int lastLevel = scePowerGetBatteryLifePercent();
+ static bool lastCharging = scePowerGetBatteryChargingStatus();
+
+ void check()
+ {
+ // No battery?
+ if (!scePowerIsBatteryExist())
+ {
+ // Don't report anything.
+ return;
+ }
+
+ if (show_bat.value)
+ return;
+
+ // Get the new battery status.
+ const int level = scePowerGetBatteryLifePercent();
+ const bool charging = scePowerGetBatteryChargingStatus();
+
+ // Is the level not sensible?
+ if ((level < 0) || (level > 100))
+ {
+ // Hopefully it will be sensible soon.
+ return;
+ }
+
+ // Has the battery status changed?
+ if ((level != lastLevel) || (charging != lastCharging))
+ {
+ // Charging?
+ if (charging)
+ {
+ // Inform the player.
+ Con_Printf("Battery %d%% (charging)\n",
+ level);
+ }
+ else
+ {
+ // How much time is left?
+ const int timeLeft = scePowerGetBatteryLifeTime();
+
+ // Is the time sensible?
+ if (timeLeft > 0)
+ {
+ // Convert the time to something readable.
+ const int hoursLeft = timeLeft / 60;
+ const int minutesLeft = timeLeft % 60;
+
+ // Inform the player.
+ Con_Printf("Battery %d%% (%d hour%s %d minute%s)\n",
+ level,
+ hoursLeft,
+ (hoursLeft == 1) ? "" : "s", // Handle pluralisation of hour(s).
+ minutesLeft,
+ (minutesLeft == 1) ? "" : "s"); // Handle pluralisation of minute(s).
+ }
+ else
+ {
+ // It's a silly time, just report the battery level.
+ Con_Printf("Battery %d%%\n",
+ level);
+ }
+ }
+
+ // Remember the status for next frame.
+ lastLevel = level;
+ lastCharging = charging;
+ }
+ }
+ }
+}
diff --git a/source/psp/battery.hpp b/source/psp/battery.hpp
new file mode 100644
index 0000000..40d26c8
--- /dev/null
+++ b/source/psp/battery.hpp
@@ -0,0 +1,33 @@
+/*
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef QUAKE_BATTERY_HPP
+#define QUAKE_BATTERY_HPP
+
+namespace quake
+{
+ namespace battery
+ {
+ // Checks the battery level and prints it to the console if it's changed.
+ void check();
+ }
+}
+
+#endif
diff --git a/source/psp/cd.cpp b/source/psp/cd.cpp
new file mode 100644
index 0000000..159d5e8
--- /dev/null
+++ b/source/psp/cd.cpp
@@ -0,0 +1,289 @@
+/*
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include
+#include
+
+#include
+
+//#include
+
+#include "mp3.h"
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+
+extern cvar_t bgmtype;
+extern cvar_t bgmvolume;
+
+
+namespace quake
+{
+ namespace cd
+ {
+ struct Sample
+ {
+ short left;
+ short right;
+ };
+
+// static int file = -1;
+ static int last_track = 1;
+
+ static bool playing = false;
+ static bool paused = false;
+ static bool enabled = false;
+ static float cdvolume = 0;
+ }
+}
+
+using namespace quake;
+using namespace quake::cd;
+
+static void CD_f (void)
+{
+ char *command;
+
+ if (Cmd_Argc() < 2)
+ {
+ Con_Printf("commands:");
+ Con_Printf("on, off, reset, remap, \n");
+ Con_Printf("play, stop, loop, pause, resume\n");
+ Con_Printf("eject, close, info\n");
+ return;
+ }
+
+ command = Cmd_Argv (1);
+
+ if (Q_strcasecmp(command, "on") == 0)
+ {
+ enabled = true;
+ return;
+ }
+
+ if (Q_strcasecmp(command, "off") == 0)
+ {
+ if (playing)
+ CDAudio_Stop();
+ enabled = false;
+ return;
+ }
+
+ if (Q_strcasecmp(command, "reset") == 0)
+ {
+ enabled = true;
+ if (playing)
+ CDAudio_Stop();
+ return;
+ }
+
+ if (Q_strcasecmp(command, "remap") == 0)
+ {
+ return;
+ }
+
+ if (Q_strcasecmp(command, "close") == 0)
+ {
+ return;
+ }
+
+ if (Q_strcasecmp(command, "play") == 0)
+ {
+ CDAudio_Play((byte)atoi(Cmd_Argv (2)), (qboolean) false);
+ return;
+ }
+
+ if (Q_strcasecmp(command, "loop") == 0)
+ {
+ CDAudio_Play((byte)atoi(Cmd_Argv (2)), (qboolean) true);
+ return;
+ }
+
+ if (Q_strcasecmp(command, "stop") == 0)
+ {
+ CDAudio_Stop();
+ return;
+ }
+
+ if (Q_strcasecmp(command, "pause") == 0)
+ {
+ CDAudio_Pause();
+ return;
+ }
+
+ if (Q_strcasecmp(command, "resume") == 0)
+ {
+ CDAudio_Resume();
+ return;
+ }
+
+ if (Q_strcasecmp(command, "eject") == 0)
+ {
+ if (playing)
+ CDAudio_Stop();
+ return;
+ }
+
+ if (Q_strcasecmp(command, "info") == 0)
+ {
+ Con_Printf("MP3 Player By Crow_bar\n");
+ Con_Printf("Based On sceMp3 Lib\n");
+ Con_Printf("Additional fixed by\n");
+ Con_Printf("dr_mabuse1981 and Baker.\n");
+ Con_Printf("\n");
+ return;
+ }
+}
+
+void CDAudio_VolumeChange(float bgmvolume)
+{
+ int volume = (int) (bgmvolume * (float) PSP_VOLUME_MAX);
+ //pspAudioSetVolume(1, volume, volume);
+ mp3_volume = volume;
+ cdvolume = bgmvolume;
+
+// Con_Printf("Volume changed to : %i\n", mp3_volume);
+
+}
+
+extern "C" int sceKernelDelayThread(int delay);
+
+void CDAudio_Track(char* trackname)
+{
+ //CDAudio_Stop();
+
+ char path[256];
+
+ sprintf(path, "%s\\sounds\\stream\\%s", host_parms.basedir, trackname);
+
+ Sys_Error(path);
+}
+
+void CDAudio_Play(byte track, qboolean looping)
+{
+ last_track = track;
+ CDAudio_Stop();
+
+ if (track < 1)
+ track = 1;
+
+ char path[256];
+ //snprintf(path, sizeof(path), "%s/%s/music/track%02u.mp3", host_parms.basedir, kurok ? "kurok" : "id1", track);
+ sprintf(path, "%s\\stream\\stream%02u.mp3", host_parms.basedir, track);
+
+ int ret;
+ ret = mp3_start_play(path, 0);
+
+ if(ret != 2)
+ {
+// Con_Printf("Playing %s\n", path);
+ playing = true;
+ }
+ else
+ {
+ Con_Printf("Couldn't find %s\n", path);
+ playing = false;
+ Cvar_Set("bgmtype","none");
+ CDAudio_VolumeChange(0);
+ }
+
+
+ CDAudio_VolumeChange(bgmvolume.value);
+}
+
+void CDAudio_Stop(void)
+{
+ mp3_job_started = 0;
+
+// file = -1;
+ playing = false;
+ CDAudio_VolumeChange(0);
+}
+
+void CDAudio_Pause(void)
+{
+ CDAudio_VolumeChange(0);
+ paused = true;
+}
+
+void CDAudio_Resume(void)
+{
+ CDAudio_VolumeChange(bgmvolume.value);
+ paused = false;
+}
+
+void CDAudio_Update(void)
+{
+
+ //if (bgmvolume.value != mp3_volume)
+ // CDAudio_VolumeChange(bgmvolume.value);
+ //if(changeMp3Volume) CDAudio_VolumeChange(bgmvolume.value);
+
+ if (strcmpi(bgmtype.string,"cd") == 0) {
+ if (playing == false) {
+ CDAudio_Play(last_track, (qboolean) false);
+ }
+ if (paused == true) {
+ CDAudio_Resume();
+ }
+
+ } else {
+ if (paused == false) {
+ CDAudio_Pause();
+ }
+ if (playing == true) {
+ CDAudio_Stop();
+ }
+ }
+}
+
+int CDAudio_Init(void)
+{
+ if (cls.state == ca_dedicated)
+ return -1;
+
+ if (COM_CheckParm("-nocdaudio"))
+ return -1;
+
+ mp3_init();
+ sceKernelDelayThread(5*10000);
+
+ enabled = true;
+
+ Cmd_AddCommand ("cd", CD_f);
+
+ Con_Printf("MP3 library Initialized\n");
+
+ return 0;
+}
+
+void CDAudio_Shutdown(void)
+{
+ Con_Printf("CDAudio_Shutdown\n");
+
+ CDAudio_Stop();
+
+ sceKernelDelayThread(5*10000);
+// mp3_deinit();
+
+}
+
diff --git a/source/psp/clipping.cpp b/source/psp/clipping.cpp
new file mode 100644
index 0000000..55fd7ed
--- /dev/null
+++ b/source/psp/clipping.cpp
@@ -0,0 +1,474 @@
+/*
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+Copyright (C) 2021 Sergey Galushko
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#define CLIPPING_DEBUGGING 0
+#define CLIP_LEFT 1
+#define CLIP_RIGHT 1
+#define CLIP_BOTTOM 1
+#define CLIP_TOP 1
+#define CLIP_NEAR 0
+#define CLIP_FAR 0
+
+#include "clipping.hpp"
+
+#include
+#include
+#include
+
+#include "math.hpp"
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+
+namespace quake
+{
+ namespace clipping
+ {
+ // Plane types are sorted, most likely to clip first.
+ enum plane_index
+ {
+#if CLIP_BOTTOM
+ plane_index_bottom,
+#endif
+#if CLIP_LEFT
+ plane_index_left,
+#endif
+#if CLIP_RIGHT
+ plane_index_right,
+#endif
+#if CLIP_TOP
+ plane_index_top,
+#endif
+#if CLIP_NEAR
+ plane_index_near,
+#endif
+#if CLIP_FAR
+ plane_index_far,
+#endif
+ plane_count
+ };
+
+ // Types.
+ typedef ScePspFVector4 plane_type;
+ typedef plane_type frustum_t[plane_count];
+
+ // Transformed frustum.
+ static ScePspFMatrix4 projection_view_matrix __attribute__((aligned(16)));
+ static frustum_t projection_view_frustum __attribute__((aligned(16)));
+ static frustum_t clipping_frustum __attribute__((aligned(16)));
+
+ // The temporary working buffers.
+ static const std::size_t max_clipped_vertices = 32;
+ static glvert_t work_buffer[2][max_clipped_vertices] __attribute__((aligned(16)));
+
+ static inline void calculate_frustum(const ScePspFMatrix4& clip, frustum_t* frustum)
+ {
+ __asm__ (
+ ".set push\n" // save assembler option
+ ".set noreorder\n" // suppress reordering
+ #if CLIP_NEAR_FAR
+ "lv.q C000, 0(%6)\n" // C000 = clip->x
+ "lv.q C010, 16(%6)\n" // C010 = clip->y
+ "lv.q C020, 32(%6)\n" // C020 = clip->z
+ "lv.q C030, 48(%6)\n" // C030 = clip->w
+ #else
+ "lv.q C000, 0(%4)\n" // C000 = clip->x
+ "lv.q C010, 16(%4)\n" // C010 = clip->y
+ "lv.q C020, 32(%4)\n" // C020 = clip->z
+ "lv.q C030, 48(%4)\n" // C030 = clip->w
+ #endif
+ /* Extract the BOTTOM plane */
+ "vadd.s S100, S003, S001\n" // S100 = clip->x.w + clip->x.y
+ "vadd.s S101, S013, S011\n" // S101 = clip->y.w + clip->y.y
+ "vadd.s S102, S023, S021\n" // S102 = clip->z.w + clip->z.y
+ "vadd.s S103, S033, S031\n" // S103 = clip->w.w + clip->w.y
+ "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103
+ "vzero.s S111\n" // S111 = 0
+ "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f )
+ "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 )
+ "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111
+ "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110
+ "sv.q C100, %0\n" // Store plane from register
+ /* Extract the LEFT plane */
+ "vadd.s S100, S003, S000\n" // S100 = clip->x.w + clip->x.x
+ "vadd.s S101, S013, S010\n" // S101 = clip->y.w + clip->y.x
+ "vadd.s S102, S023, S020\n" // S102 = clip->z.w + clip->z.x
+ "vadd.s S103, S033, S030\n" // S103 = clip->w.w + clip->w.x
+ "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103
+ "vzero.s S111\n" // S111 = 0
+ "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f )
+ "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 )
+ "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111
+ "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110
+ "sv.q C100, %1\n" // Store plane from register
+ /* Extract the RIGHT plane */
+ "vsub.s S100, S003, S000\n" // S100 = clip->x.w - clip->x.x
+ "vsub.s S101, S013, S010\n" // S101 = clip->y.w - clip->y.x
+ "vsub.s S102, S023, S020\n" // S102 = clip->z.w - clip->z.x
+ "vsub.s S103, S033, S030\n" // S103 = clip->w.w - clip->w.x
+ "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103
+ "vzero.s S111\n" // S111 = 0
+ "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f )
+ "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 )
+ "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111
+ "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110
+ "sv.q C100, %2\n" // Store plane from register
+ /* Extract the TOP plane */
+ "vsub.s S100, S003, S001\n" // S100 = clip->x.w - clip->x.y
+ "vsub.s S101, S013, S011\n" // S101 = clip->y.w - clip->y.y
+ "vsub.s S102, S023, S021\n" // S102 = clip->z.w - clip->z.y
+ "vsub.s S103, S033, S031\n" // S103 = clip->w.w - clip->w.y
+ "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103
+ "vzero.s S111\n" // S111 = 0
+ "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f )
+ "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 )
+ "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111
+ "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110
+ "sv.q C100, %3\n" // Store plane from register
+ #if CLIP_NEAR_FAR
+ /* Extract the NEAR plane */
+ "vadd.s S100, S003, S002\n" // S100 = clip->x.w + clip->x.z
+ "vadd.s S101, S013, S012\n" // S101 = clip->y.w + clip->y.z
+ "vadd.s S102, S023, S022\n" // S102 = clip->z.w + clip->z.z
+ "vadd.s S103, S033, S032\n" // S103 = clip->w.w + clip->w.z
+ "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103
+ "vzero.s S111\n" // S111 = 0
+ "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f )
+ "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 )
+ "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111
+ "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110
+ "sv.q C100, %4\n" // Store plane from register
+ /* Extract the FAR plane */
+ "vsub.s S100, S003, S002\n" // S100 = clip->x.w - clip->x.z
+ "vsub.s S101, S013, S012\n" // S101 = clip->y.w - clip->y.z
+ "vsub.s S102, S023, S022\n" // S102 = clip->z.w - clip->z.z
+ "vsub.s S103, S033, S032\n" // S103 = clip->w.w - clip->w.z
+ "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103
+ "vzero.s S111\n" // S111 = 0
+ "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f )
+ "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 )
+ "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111
+ "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110
+ "sv.q C100, %5\n" // Store plane from register
+ #endif
+ ".set pop\n" // Restore assembler option
+ : "=m"( ( *frustum )[plane_index_bottom] ),
+ "=m"( ( *frustum )[plane_index_left] ),
+ "=m"( ( *frustum )[plane_index_right] ),
+ #if CLIP_NEAR_FAR
+ "=m"( ( *frustum )[plane_index_top] ),
+ "=m"( ( *frustum )[plane_index_near] ),
+ "=m"( ( *frustum )[plane_index_far] )
+ #else
+ "=m"( ( *frustum )[plane_index_top] )
+ #endif
+ : "r"( &clip )
+ );
+ }
+
+ void begin_frame()
+ {
+ // Get the projection matrix.
+ sceGumMatrixMode(GU_PROJECTION);
+ ScePspFMatrix4 proj;
+ sceGumStoreMatrix(&proj);
+
+ // Get the view matrix.
+ sceGumMatrixMode(GU_VIEW);
+ ScePspFMatrix4 view;
+ sceGumStoreMatrix(&view);
+
+ // Restore matrix mode.
+ sceGumMatrixMode(GU_MODEL);
+
+ // Combine the two matrices (multiply projection by view).
+ gumMultMatrix(&projection_view_matrix, &proj, &view);
+
+ // Calculate and cache the clipping frustum.
+ calculate_frustum(projection_view_matrix, &projection_view_frustum);
+
+ __asm__ volatile (
+ "ulv.q C700, %4\n" // Load plane into register
+ "ulv.q C710, %5\n" // Load plane into register
+ "ulv.q C720, %6\n" // Load plane into register
+ "ulv.q C730, %7\n" // Load plane into register
+ "sv.q C700, %0\n" // Store plane from register
+ "sv.q C710, %1\n" // Store plane from register
+ "sv.q C720, %2\n" // Store plane from register
+ "sv.q C730, %3\n" // Store plane from register
+ : "=m"( clipping_frustum[plane_index_bottom] ),
+ "=m"( clipping_frustum[plane_index_left] ),
+ "=m"( clipping_frustum[plane_index_right] ),
+ "=m"( clipping_frustum[plane_index_top] )
+ : "m"( projection_view_frustum[plane_index_bottom] ),
+ "m"( projection_view_frustum[plane_index_left] ),
+ "m"( projection_view_frustum[plane_index_right] ),
+ "m"( projection_view_frustum[plane_index_top] )
+ );
+ }
+
+ void begin_brush_model()
+ {
+ // Get the model matrix.
+ ScePspFMatrix4 model_matrix;
+ sceGumStoreMatrix(&model_matrix);
+
+ // Combine the matrices (multiply projection-view by model).
+ ScePspFMatrix4 projection_view_model_matrix;
+ gumMultMatrix(&projection_view_model_matrix, &projection_view_matrix, &model_matrix);
+
+ // Calculate the clipping frustum.
+ calculate_frustum(projection_view_model_matrix, &clipping_frustum);
+
+ __asm__ volatile (
+ "ulv.q C700, %0\n" // Load plane into register
+ "ulv.q C710, %1\n" // Load plane into register
+ "ulv.q C720, %2\n" // Load plane into register
+ "ulv.q C730, %3\n" // Load plane into register
+ :: "m"(clipping_frustum[plane_index_bottom]),
+ "m"(clipping_frustum[plane_index_left]),
+ "m"(clipping_frustum[plane_index_right]),
+ "m"(clipping_frustum[plane_index_top])
+ );
+ }
+
+ void end_brush_model()
+ {
+ // Restore the clipping frustum.
+ __asm__ volatile (
+ "ulv.q C700, %4\n" // Load plane into register
+ "ulv.q C710, %5\n" // Load plane into register
+ "ulv.q C720, %6\n" // Load plane into register
+ "ulv.q C730, %7\n" // Load plane into register
+ "sv.q C700, %0\n" // Store plane from register
+ "sv.q C710, %1\n" // Store plane from register
+ "sv.q C720, %2\n" // Store plane from register
+ "sv.q C730, %3\n" // Store plane from register
+ : "=m"( clipping_frustum[plane_index_bottom] ),
+ "=m"( clipping_frustum[plane_index_left] ),
+ "=m"( clipping_frustum[plane_index_right] ),
+ "=m"( clipping_frustum[plane_index_top] )
+ : "m"( projection_view_frustum[plane_index_bottom] ),
+ "m"( projection_view_frustum[plane_index_left] ),
+ "m"( projection_view_frustum[plane_index_right] ),
+ "m"( projection_view_frustum[plane_index_top] )
+ );
+ }
+
+ // Is clipping required?
+ bool is_clipping_required(const struct glvert_s* vertices, std::size_t vertex_count)
+ {
+ int res;
+ __asm__ (
+ ".set push\n" // save assembler option
+ ".set noreorder\n" // suppress reordering
+ "move $8, %1\n" // $8 = &vertices[0]
+ "move $9, %2\n" // $9 = vertex_count
+ "li $10, 20\n" // $10 = 20( sizeof(glvert_t) )
+ "mul $10, $10, $9\n" // $10 = $10 * $9
+ "addu $10, $10, $8\n" // $10 = $10 + $8
+ "0:\n" // loop
+ "ulv.q C610, 8($8)\n" // Load vertex into register skip tex cord 8 byte
+ "vone.s S613\n" // Now set the 4th entry to be 1 as that is just random
+ "vdot.q S620, C700, C610\n" // S620 = v[i] * frustrum[0]
+ "vdot.q S621, C710, C610\n" // S621 = v[i] * frustrum[1]
+ "vdot.q S622, C720, C610\n" // S622 = v[i] * frustrum[2]
+ "vdot.q S623, C730, C610\n" // S623 = v[i] * frustrum[3]
+ "vzero.q C610\n" // C610 = [0.0f, 0.0f, 0.0f. 0.0f]
+ "addiu %0, $0, 1\n" // res = 1
+ "vcmp.q LT, C620, C610\n" // S620 < 0.0f || S621 < 0.0f || S622 < 0.0f || S623 < 0.0f
+ "bvt 4, 1f\n" // if ( CC[4] == 1 ) jump to exit
+ "addiu $8, $8, 20\n" // $8 = $8 + 20( sizeof(glvert_t) )
+ "bne $10, $8, 0b\n" // if ( $9 != 0 ) jump to loop
+ "move %0, $0\n" // res = 0
+ "1:\n" // exit
+ ".set pop\n" // Restore assembler option
+ : "=r"(res)
+ : "r"(vertices), "r"(vertex_count)
+ : "$8", "$9", "$10"
+ );
+ return (res == 1) ? true : false;
+ }
+
+ // Clips a polygon against a plane.
+ // http://hpcc.engin.umich.edu/CFD/users/charlton/Thesis/html/node90.html
+ static void clip_to_plane(
+ const plane_type& plane,
+ const glvert_t* const unclipped_vertices,
+ std::size_t const unclipped_vertex_count,
+ glvert_t* const clipped_vertices,
+ std::size_t* const clipped_vertex_count)
+ {
+ __asm__ (
+ ".set push\n" // save assembler option
+ ".set noreorder\n" // suppress reordering
+ "move $8, %1\n" // $8 = uv[0] is S
+ "addiu $9, $8, 20\n" // $9 = uv[1] is P
+ "move $10, %2\n" // $10 = uvc
+ "li $11, 20\n" // $11 = 20( sizeof( gu_vert_t ) )
+ "mul $11, $11, $10\n" // $11 = $11 * $10
+ "addu $11, $11, $8\n" // $11 = $11 + $8
+ "move $12, %3\n" // $12 = &cv[0]
+ "move %0, $0\n" // cvc = 0
+ "ulv.q C100, %4\n" // Load plane into register
+ "vzero.s S110\n" // Set zero for cmp
+ "0:\n" // loop
+ "ulv.q C000, 0($8)\n" // Load vertex S TEX(8b) into register
+ "vzero.p C002\n" // Set the 3th and 4th entry C000 to zero
+ "ulv.q C010, 8($8)\n" // Load vertex S XYZ(12b) into register
+ "vzero.s S013\n" // Set the 4th entry C010 to zero
+ "ulv.q C020, 0($9)\n" // Load vertex P TEX(8b) into register
+ "vzero.p C022\n" // Set the 3th and 4th entry C020 to zero
+ "ulv.q C030, 8($9)\n" // Load vertex P XYZ(12b) into register
+ "vzero.s S033\n" // Set the 4th entry C030 to zero
+ "vdot.q S111, C100, C010\n" // S111 = plane * S
+ "vdot.q S112, C100, C030\n" // S112 = plane * P
+ "vadd.s S111, S111, S103\n" // S111 = S111 + plane->w is S dist
+ "vadd.s S112, S112, S103\n" // S112 = S112 + plane->w is P dist
+ "vcmp.s LE, S111, S110\n" // (S dist <= 0.0f)
+ "bvt 0, 2f\n" // if ( CC[0] == 1 ) jump to 2f
+ "nop\n" // ( delay slot )
+ "vcmp.s LE, S112, S110\n" // (P dist <= 0.0f)
+ "bvt 0, 1f\n" // if ( CC[0] == 1 ) jump to 1f
+ "nop\n" // ( delay slot )
+ "sv.s S020, 0($12)\n" // cv->uv[0] = C020 TEX U
+ "sv.s S021, 4($12)\n" // cv->uv[1] = C021 TEX V
+ "sv.s S030, 8($12)\n" // cv->xyz[0] = C030 XYZ X
+ "sv.s S031, 12($12)\n" // cv->xyz[1] = C031 XYZ Y
+ "sv.s S032, 16($12)\n" // cv->xyz[2] = C032 XYZ Z
+ "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) )
+ "j 3f\n" // jump to next
+ "addiu %0, %0, 1\n" // cvc += 1 ( delay slot )
+ "1:\n"
+ "vsub.s S112, S111, S112\n" // S112 = ( S dist - P dist )
+ "vdiv.s S112, S111, S112\n" // S112 = S111 / S112
+ "vsub.q C120, C020, C000\n" // C120 = C020(P TEX) - C000(S TEX)
+ "vsub.q C130, C030, C010\n" // C130 = C030(P XYZ) - C010(S XYZ)
+ "vscl.q C120, C120, S112\n" // C120 = C020 * S112
+ "vscl.q C130, C130, S112\n" // C130 = C030 * S112
+ "vadd.q C120, C120, C000\n" // C120 = C120 + C000(S TEX)
+ "vadd.q C130, C130, C010\n" // C130 = C130 + C010(S XYZ)
+ "sv.s S120, 0($12)\n" // cv->uv[0] = S120 TEX U
+ "sv.s S121, 4($12)\n" // cv->uv[1] = S121 TEX V
+ "sv.s S130, 8($12)\n" // cv->xyz[0] = S130 XYZ X
+ "sv.s S131, 12($12)\n" // cv->xyz[1] = S131 XYZ Y
+ "sv.s S132, 16($12)\n" // cv->xyz[2] = S132 XYZ Z
+ "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) )
+ "j 3f\n" // jump to next
+ "addiu %0, %0, 1\n" // cvc += 1 ( delay slot )
+ "2:\n"
+ "vcmp.s LE, S112, S110\n" // (P dist <= 0.0f)
+ "bvt 0, 3f\n" // if ( CC[0] == 1 ) jump to next
+ "nop\n" // ( delay slot )
+ "vsub.s S112, S111, S112\n" // S112 = ( S dist - P dist )
+ "vdiv.s S112, S111, S112\n" // S112 = S111 / S112
+ "vsub.q C120, C020, C000\n" // C120 = C020(P TEX) - C000(S TEX)
+ "vsub.q C130, C030, C010\n" // C130 = C030(P XYZ) - C010(S XYZ)
+ "vscl.q C120, C120, S112\n" // C120 = C020 * S112
+ "vscl.q C130, C130, S112\n" // C130 = C030 * S112
+ "vadd.q C120, C120, C000\n" // C120 = C120 + C000(S TEX)
+ "vadd.q C130, C130, C010\n" // C130 = C130 + C010(S XYZ)
+ "sv.s S120, 0($12)\n" // cv->uv[0] = S120 TEX U
+ "sv.s S121, 4($12)\n" // cv->uv[1] = S121 TEX V
+ "sv.s S130, 8($12)\n" // cv->xyz[0] = S130 XYZ X
+ "sv.s S131, 12($12)\n" // cv->xyz[1] = S131 XYZ Y
+ "sv.s S132, 16($12)\n" // cv->xyz[2] = S132 XYZ Z
+ "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) )
+ "addiu %0, %0, 1\n" // cvc += 1
+ "sv.s S020, 0($12)\n" // cv->uv[0] = S020 TEX U
+ "sv.s S021, 4($12)\n" // cv->uv[1] = S021 TEX V
+ "sv.s S030, 8($12)\n" // cv->xyz[0] = S030 XYZ X
+ "sv.s S031, 12($12)\n" // cv->xyz[1] = S031 XYZ Y
+ "sv.s S032, 16($12)\n" // cv->xyz[2] = S032 XYZ Z
+ "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) )
+ "addiu %0, %0, 1\n" // cvc += 1
+ "3:\n" // next
+ "addiu $8, $8, 20\n" // $8 = $8 + 20( sizeof( gu_vert_t ) )
+ "addiu $9, $8, 20\n" // $9 = $8 + 20( sizeof( gu_vert_t ) )
+ "bne $9, $11, 4f\n" // if ( $11 != $9 ) jump to next
+ "nop\n"
+ "move $9, %1\n" // $9 = &uv[0] set P
+ "4:\n"
+ "bne $11, $8, 0b\n" // if ( $11 != $8 ) jump to loop
+ "nop\n" // ( delay slot )
+ ".set pop\n" // Restore assembler option
+ : "+r"( *clipped_vertex_count )
+ : "r"( unclipped_vertices ), "r"( unclipped_vertex_count ), "r"( clipped_vertices ), "m"( plane )
+ : "$8", "$9", "$10", "$11", "$12"
+ );
+ }
+
+ // Clips a polygon against the frustum.
+ void clip(
+ const struct glvert_s* unclipped_vertices,
+ std::size_t unclipped_vertex_count,
+ const struct glvert_s** clipped_vertices,
+ std::size_t* clipped_vertex_count)
+ {
+ // No vertices to clip?
+ if (!unclipped_vertex_count)
+ {
+ // Error.
+ Sys_Error("Calling clip with zero vertices");
+ }
+
+ // Set up constants.
+ const plane_type* const last_plane = &clipping_frustum[plane_count];
+
+ // Set up the work buffer pointers.
+ const glvert_t* src = unclipped_vertices;
+ glvert_t* dst = work_buffer[0];
+ std::size_t vertex_count = unclipped_vertex_count;
+
+ // For each frustum plane...
+ for (const plane_type* plane = &clipping_frustum[0]; plane != last_plane; ++plane)
+ {
+ // Clip the poly against this frustum plane.
+ clip_to_plane(*plane, src, vertex_count, dst, &vertex_count);
+
+ // No vertices left to clip?
+ if (!vertex_count)
+ {
+ // Quit early.
+ *clipped_vertex_count = 0;
+ return;
+ }
+
+ // Use the next pair of buffers.
+ src = dst;
+ if (dst == work_buffer[0])
+ {
+ dst = work_buffer[1];
+ }
+ else
+ {
+ dst = work_buffer[0];
+ }
+ }
+
+ // Fill in the return data.
+ *clipped_vertices = src;
+ *clipped_vertex_count = vertex_count;
+ }
+ }
+}
diff --git a/source/psp/clipping.hpp b/source/psp/clipping.hpp
new file mode 100644
index 0000000..3a786e4
--- /dev/null
+++ b/source/psp/clipping.hpp
@@ -0,0 +1,53 @@
+/*
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef QUAKE_CLIPPING_HPP
+#define QUAKE_CLIPPING_HPP
+
+#include
+
+struct glvert_s;
+
+namespace quake
+{
+ namespace clipping
+ {
+ // Calculates clipping planes from the GU view and projection matrices.
+ void begin_frame();
+
+ // Calculates clipping planes from the GU view and projection matrices.
+ void begin_brush_model();
+
+ // Resets the frustum to camera space.
+ void end_brush_model();
+
+ // Is clipping required?
+ bool is_clipping_required(const struct glvert_s* vertices, std::size_t vertex_count);
+
+ // Clips a polygon.
+ void clip(
+ const struct glvert_s* unclipped_vertices,
+ std::size_t unclipped_vertex_count,
+ const struct glvert_s** clipped_vertices,
+ std::size_t* clipped_vertex_count);
+ }
+}
+
+#endif
diff --git a/source/psp/dist/gpl.txt b/source/psp/dist/gpl.txt
new file mode 100644
index 0000000..e37680c
--- /dev/null
+++ b/source/psp/dist/gpl.txt
@@ -0,0 +1,280 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/source/psp/dist/readme.html b/source/psp/dist/readme.html
new file mode 100644
index 0000000..a76c707
--- /dev/null
+++ b/source/psp/dist/readme.html
@@ -0,0 +1,117 @@
+
+
+ PSP Quake
+
+
+
+ PSP Quake
+
+ Credits
+
+ Original game by id Software.
+ Ported to the PSP by Peter Mackay and Chris Swindle.
+ Ripped off and uncredited by several others! :-)
+
+
+ Links
+
+ Our home Page at SourceForge. You can get the latest version and the source code there.
+
+
+
+
+ Getting the game to run
+ Everybody!
+ To run Quake on your PSP, you need the data files from either the shareware version of Quake, or the full version.
+
+ Get the data files:
+
+ Buy the full version of Quake here , or on eBay; or
+ Download the demo version of Quake for free here .
+
+
+ Copy the ID1 directory from the demo to Quake/ID1 .
+
+
+ Firmware 3.10 OE-A2 and compatible
+
+ Copy the Quake directory to /PSP/GAME/Quake on your PSP.
+
+
+ Other firmware version?
+
+ Only custom firmwares are supported at the moment.
+
+
+
+
+ Troubleshooting
+
+ Known in-game error messages
+
+
+ Message
+ Cause
+ Fix
+
+
+ W_LoadWadFile: couldn't load gfx.wad
+ You've not put the ID directory on your memory stick.
+ Read this file again from the top.
+
+
+
+ Q & A
+
+
+ Question
+ Answer
+
+
+ Where do you get the full version of Quake?
+ You buy it. It's quite hard to find nowadays, but you should be able to get it on eBay without too much difficulty. I did.
+
+
+ Where do you get the demo version of Quake?
+ You download it. We have made it available here .
+
+
+ Why don't you bundle the shareware data files with your release?
+ Because the demo's license says not to do that. We have to distribute the complete demo , not just the data files. We did however package it up as .rar and .zip, making it a lot easier to get at the files compared to id's DOS installer.
+
+
+
+ Found a bug or want something added?
+ Don't just bitch about it on a forum - we may not read it. Please add it to our SourceForge Bug Tracker .
+
Notes:
+
+ Feature Requests are not Bugs , so please add things to the right tracker.
+ Check the "Closed" bugs in the tracker before adding yours. These are the ones which will be fixed in the next version. Duplicates will be deleted and the submitters potentially ridiculed.
+
+
+
+
+ Want to make a Quake-derived project?
+
+ Get our source code from SourceForge.
+ Credit us in your readme and release announcement. It's only fair.
+ Release your source. You have to because Quake's license says so.
+ Don't bundle data files from the full version of Quake with your game.
+
+
+
+
+ Greets
+
+ Hazel, for putting up with me working on this instea of giving you attention - Pete.
+ All the PSPSDK, PS2DEV and #PSPDEV guys.
+ Dark-AleX.
+ All the Noobz guys.
+
+
+
+
+ End of file. Thanks for making it this far!
+
+
+
diff --git a/source/psp/files-collected b/source/psp/files-collected
new file mode 100644
index 0000000..64e229e
Binary files /dev/null and b/source/psp/files-collected differ
diff --git a/source/psp/fnmatch.cpp b/source/psp/fnmatch.cpp
new file mode 100644
index 0000000..5331043
--- /dev/null
+++ b/source/psp/fnmatch.cpp
@@ -0,0 +1,142 @@
+
+#define FNM_NOMATCH (1) /* Match failed. */
+#define FNM_NOSYS (2) /* Function not implemented. */
+
+#define FNM_NOESCAPE (0x01) /* Disable backslash escaping. */
+#define FNM_PATHNAME (0x02) /* Slash must be matched by slash. */
+#define FNM_PERIOD (0x04) /* Period must be matched by period. */
+#define FNM_CASEFOLD (0x08) /* Pattern is matched case-insensitive */
+#define FNM_LEADING_DIR (0x10) /* Ignore / after Imatch. */
+
+#include
+#include
+#include "fnmatch.h"
+
+#define EOS '\0'
+
+static const char *rangematch(const char *, int, int);
+
+static inline int
+foldcase(int ch, int flags)
+{
+
+ if ((flags & FNM_CASEFOLD) != 0 && isupper(ch))
+ return (tolower(ch));
+ return (ch);
+}
+
+#define FOLDCASE(ch, flags) foldcase((unsigned char)(ch), (flags))
+
+int fnmatch(const char *pattern, const char *string, int flags)
+{
+ const char *stringstart;
+ char c, test;
+
+ for (stringstart = string;;)
+ switch (c = FOLDCASE(*pattern++, flags)) {
+ case EOS:
+ if ((flags & FNM_LEADING_DIR) && *string == '/')
+ return (0);
+ return (*string == EOS ? 0 : FNM_NOMATCH);
+ case '?':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && (flags & FNM_PATHNAME))
+ return (FNM_NOMATCH);
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ case '*':
+ c = FOLDCASE(*pattern, flags);
+ /* Collapse multiple stars. */
+ while (c == '*')
+ c = FOLDCASE(*++pattern, flags);
+
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+
+ /* Optimize for pattern with * at end or before /. */
+ if (c == EOS) {
+ if (flags & FNM_PATHNAME)
+ return ((flags & FNM_LEADING_DIR) ||
+ strchr(string, '/') == NULL ?
+ 0 : FNM_NOMATCH);
+ else
+ return (0);
+ } else if (c == '/' && flags & FNM_PATHNAME) {
+ if ((string = strchr(string, '/')) == NULL)
+ return (FNM_NOMATCH);
+ break;
+ }
+
+ /* General case, use recursion. */
+ while ((test = FOLDCASE(*string, flags)) != EOS) {
+ if (!fnmatch(pattern, string,
+ flags & ~FNM_PERIOD))
+ return (0);
+ if (test == '/' && flags & FNM_PATHNAME)
+ break;
+ ++string;
+ }
+ return (FNM_NOMATCH);
+ case '[':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && flags & FNM_PATHNAME)
+ return (FNM_NOMATCH);
+ if ((pattern =
+ rangematch(pattern, FOLDCASE(*string, flags),
+ flags)) == NULL)
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ case '\\':
+ if (!(flags & FNM_NOESCAPE)) {
+ if ((c = FOLDCASE(*pattern++, flags)) == EOS) {
+ c = '\\';
+ --pattern;
+ }
+ }
+ /* FALLTHROUGH */
+ default:
+ if (c != FOLDCASE(*string++, flags))
+ return (FNM_NOMATCH);
+ break;
+ }
+ /* NOTREACHED */
+}
+
+static const char * rangematch(const char *pattern, int test, int flags)
+{
+ int negate, ok;
+ char c, c2;
+
+ if ((negate = (*pattern == '!' || *pattern == '^')) != 0)
+ ++pattern;
+
+ for (ok = 0; (c = FOLDCASE(*pattern++, flags)) != ']';) {
+ if (c == '\\' && !(flags & FNM_NOESCAPE))
+ c = FOLDCASE(*pattern++, flags);
+ if (c == EOS)
+ return (NULL);
+ if (*pattern == '-'
+ && (c2 = FOLDCASE(*(pattern+1), flags)) != EOS &&
+ c2 != ']') {
+ pattern += 2;
+ if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+ c2 = FOLDCASE(*pattern++, flags);
+ if (c2 == EOS)
+ return (NULL);
+ if (c <= test && test <= c2)
+ ok = 1;
+ } else if (c == test)
+ ok = 1;
+ }
+ return (ok == negate ? NULL : pattern);
+}
+
diff --git a/source/psp/fnmatch.h b/source/psp/fnmatch.h
new file mode 100644
index 0000000..dcfdc5e
--- /dev/null
+++ b/source/psp/fnmatch.h
@@ -0,0 +1,10 @@
+#define FNM_NOMATCH (1) /* Match failed. */
+#define FNM_NOSYS (2) /* Function not implemented. */
+
+#define FNM_NOESCAPE (0x01) /* Disable backslash escaping. */
+#define FNM_PATHNAME (0x02) /* Slash must be matched by slash. */
+#define FNM_PERIOD (0x04) /* Period must be matched by period. */
+#define FNM_CASEFOLD (0x08) /* Pattern is matched case-insensitive */
+#define FNM_LEADING_DIR (0x10) /* Ignore / after Imatch. */
+
+int fnmatch(const char *pattern, const char *string, int flags);
diff --git a/source/psp/gethost.cpp b/source/psp/gethost.cpp
new file mode 100644
index 0000000..f9b975c
--- /dev/null
+++ b/source/psp/gethost.cpp
@@ -0,0 +1,115 @@
+
+// Copied from newlib as the current newlib did not include it
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MAX_NAME 512
+
+#ifdef F_h_errno
+int h_errno = NETDB_SUCCESS;
+#endif
+
+struct hostent *gethostbyaddr(const void *addr, int len, int type)
+{
+ static struct hostent ent;
+ static char * aliases[1] = { NULL };
+ char buf[1024];
+ static char sname[MAX_NAME] = "";
+ static struct in_addr saddr = { 0 };
+ static char *addrlist[2] = { (char *) &saddr, NULL };
+ int rid;
+ int err;
+
+ if((len != sizeof(struct in_addr)) || (type != AF_INET) || (addr == NULL))
+ {
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ memcpy(&saddr, addr, len);
+
+ if(sceNetResolverCreate(&rid, buf, sizeof(buf)) < 0)
+ {
+ h_errno = NO_RECOVERY;
+ return NULL;
+ }
+
+ err = sceNetResolverStartAtoN(rid, &saddr, sname, sizeof(sname), 2, 3);
+ sceNetResolverStop(rid);
+ sceNetResolverDelete(rid);
+ if(err < 0)
+ {
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ ent.h_name = sname;
+ ent.h_aliases = aliases;
+ ent.h_addrtype = AF_INET;
+ ent.h_length = sizeof(struct in_addr);
+ ent.h_addr_list = addrlist;
+ ent.h_addr = (char *) &saddr;
+
+ return &ent;
+}
+
+struct hostent *gethostbyname(const char *name)
+{
+ static struct hostent ent;
+ char buf[1024];
+ static char sname[MAX_NAME] = "";
+ static struct in_addr saddr = { 0 };
+ static char *addrlist[2] = { (char *) &saddr, NULL };
+ int rid;
+
+ if(sceNetInetInetAton(name, &saddr) == 0)
+ {
+ int err;
+
+ if(sceNetResolverCreate(&rid, buf, sizeof(buf)) < 0)
+ {
+ h_errno = NO_RECOVERY;
+ return NULL;
+ }
+
+ err = sceNetResolverStartNtoA(rid, name, &saddr, 2, 3);
+ sceNetResolverDelete(rid);
+ if(err < 0)
+ {
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ }
+
+ snprintf(sname, MAX_NAME, "%s", name);
+ ent.h_name = sname;
+ ent.h_aliases = 0;
+ ent.h_addrtype = AF_INET;
+ ent.h_length = sizeof(struct in_addr);
+ ent.h_addr_list = addrlist;
+ ent.h_addr = (char *) &saddr;
+
+ return &ent;
+}
+
+int gethostname(char *name, int namelen)
+{
+ memset(name, 0, namelen);
+ if(sceUtilityGetSystemParamString(PSP_SYSTEMPARAM_ID_STRING_NICKNAME,
+ name, namelen) == PSP_SYSTEMPARAM_RETVAL_FAIL)
+ {
+ strcpy(name, "UNNAMED");
+ return -1;
+ }
+
+ return 1;
+}
diff --git a/source/psp/gethost.hpp b/source/psp/gethost.hpp
new file mode 100644
index 0000000..680e56d
--- /dev/null
+++ b/source/psp/gethost.hpp
@@ -0,0 +1,9 @@
+
+#ifndef GETHOST
+#define GETHOST
+
+struct hostent *gethostbyaddr(const void *addr, int len, int type);
+struct hostent *gethostbyname(const char *name);
+int gethostname(char *name, int namelen);
+
+#endif
diff --git a/source/psp/input.cpp b/source/psp/input.cpp
new file mode 100644
index 0000000..58a4446
--- /dev/null
+++ b/source/psp/input.cpp
@@ -0,0 +1,362 @@
+/*
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+
+namespace quake
+{
+ namespace input
+ {
+ // A map from button mask to Quake key.
+ static const unsigned int buttonCount = sizeof(unsigned int) * 8;
+ typedef int ButtonToKeyMap[buttonCount];
+ static ButtonToKeyMap buttonToGameKeyMap;
+ static ButtonToKeyMap buttonToConsoleKeyMap;
+ static ButtonToKeyMap buttonToMessageKeyMap;
+ static ButtonToKeyMap buttonToMenuKeyMap;
+
+ // The previous key state (for checking if things changed).
+ static SceCtrlData lastPad;
+ static bool readyToBindKeys = false;
+
+ static unsigned int buttonMaskToShift(unsigned int mask)
+ {
+ // Bad mask?
+ if (!mask)
+ return 0;
+
+ // Shift right until we hit a 1.
+ unsigned int shift = 0;
+ while ((mask & 1) == 0)
+ {
+ mask >>= 1;
+ ++shift;
+ }
+ return shift;
+ }
+ }
+}
+
+// Quake globals.
+extern "C" int bind_grab;
+
+using namespace quake;
+using namespace quake::input;
+
+extern cvar_t in_analog_strafe;
+extern cvar_t in_x_axis_adjust;
+extern cvar_t in_y_axis_adjust;
+extern cvar_t in_mlook; //Heffo - mlook cvar
+
+void IN_Init (void)
+{
+ // Set up the controller.
+ sceCtrlSetSamplingCycle(0);
+ sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
+
+ // Japanese users would prefer to have X as cancel and O as OK.
+ //unsigned int okButton = PSP_CTRL_CIRCLE;
+ //unsigned int cancelButton = PSP_CTRL_CROSS;
+ unsigned int okButton = PSP_CTRL_CROSS;
+ unsigned int cancelButton = PSP_CTRL_CIRCLE;
+
+
+ // Build the button to Quake key maps.
+ // Common keys:
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_SELECT)] = K_SELECT;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_START)] = K_ESCAPE;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_HOME)] = K_HOME;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_NOTE)] = K_NOTE;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_SCREEN)] = K_SCREEN;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_UP)] = K_UPARROW;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_RIGHT)] = K_RIGHTARROW;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_DOWN)] = K_DOWNARROW;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_LEFT)] = K_LEFTARROW;
+ memcpy(buttonToConsoleKeyMap, buttonToGameKeyMap, sizeof(ButtonToKeyMap));
+ memcpy(buttonToMessageKeyMap, buttonToGameKeyMap, sizeof(ButtonToKeyMap));
+ memcpy(buttonToMenuKeyMap, buttonToGameKeyMap, sizeof(ButtonToKeyMap));
+
+ // Game keys:
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_LTRIGGER)] = K_AUX1;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_RTRIGGER)] = K_AUX2;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_TRIANGLE)] = K_JOY1;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_CIRCLE)] = K_JOY2;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_CROSS)] = K_JOY3;
+ buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_SQUARE)] = K_JOY4;
+
+ // Console keys:
+ buttonToConsoleKeyMap[buttonMaskToShift(PSP_CTRL_LTRIGGER)] = K_PGUP;
+ buttonToConsoleKeyMap[buttonMaskToShift(PSP_CTRL_RTRIGGER)] = K_PGDN;
+ buttonToConsoleKeyMap[buttonMaskToShift(okButton)] = K_ENTER;
+ buttonToConsoleKeyMap[buttonMaskToShift(cancelButton)] = K_ESCAPE;
+ buttonToConsoleKeyMap[buttonMaskToShift(PSP_CTRL_TRIANGLE)] = K_DEL;
+ buttonToConsoleKeyMap[buttonMaskToShift(PSP_CTRL_SQUARE)] = K_INS;
+
+ // Message keys:
+ memcpy(buttonToMessageKeyMap, buttonToConsoleKeyMap, sizeof(ButtonToKeyMap));
+
+ // Menu keys:
+ buttonToMenuKeyMap[buttonMaskToShift(PSP_CTRL_SQUARE)] = K_INS;
+ buttonToMenuKeyMap[buttonMaskToShift(cancelButton)] = K_ESCAPE;
+ buttonToMenuKeyMap[buttonMaskToShift(okButton)] = K_ENTER;
+ buttonToMenuKeyMap[buttonMaskToShift(PSP_CTRL_TRIANGLE)] = K_DEL;
+ buttonToMenuKeyMap[buttonMaskToShift(PSP_CTRL_LTRIGGER)] = K_AUX1;
+ buttonToMenuKeyMap[buttonMaskToShift(PSP_CTRL_RTRIGGER)] = K_AUX2;
+}
+
+void IN_Shutdown (void)
+{
+
+}
+
+void IN_Commands (void)
+{
+ // Changed in or out of key binding mode?
+ if ((bind_grab != 0) != readyToBindKeys)
+ {
+ // Was in key binding mode?
+ if (readyToBindKeys)
+ {
+ // Just left key binding mode.
+ // Release all keys which are pressed.
+ for (unsigned int button = 0; button < buttonCount; ++button)
+ {
+ // Was the button pressed?
+ if (lastPad.Buttons & (1 << button))
+ {
+ // Is the button in the map?
+ const int key = buttonToGameKeyMap[button];
+ if (key)
+ {
+ // Send a release event.
+ Key_Event(key, qfalse);
+ }
+ }
+ }
+
+ // We're not ready to bind keys any more.
+ readyToBindKeys = false;
+ }
+ else
+ {
+ // Entering key binding mode.
+ // Release all keys which are pressed.
+ for (unsigned int button = 0; button < buttonCount; ++button)
+ {
+ // Was the button pressed?
+ if (lastPad.Buttons & (1 << button))
+ {
+ // Is the button in the map?
+ const int key = buttonToMenuKeyMap[button];
+ if (key)
+ {
+ // Send a release event.
+ Key_Event(key, qfalse);
+ }
+ }
+ }
+
+ // We're now ready to bind keys.
+ readyToBindKeys = true;
+ }
+ }
+
+ // Use a different key mapping depending on where inputs are going to go.
+ const ButtonToKeyMap* buttonToKeyMap = 0;
+ switch (key_dest)
+ {
+ case key_game:
+ buttonToKeyMap = &buttonToGameKeyMap;
+ break;
+
+ case key_console:
+ buttonToKeyMap = &buttonToConsoleKeyMap;
+ break;
+
+ case key_message:
+ buttonToKeyMap = &buttonToMessageKeyMap;
+ break;
+
+ case key_menu:
+ case key_menu_pause:
+ // Binding keys?
+ if (readyToBindKeys)
+ {
+ buttonToKeyMap = &buttonToGameKeyMap;
+ }
+ else
+ {
+ buttonToKeyMap = &buttonToMenuKeyMap;
+ }
+ break;
+
+ default:
+ Sys_Error("Unhandled key destination %d", key_dest);
+ return;
+ }
+
+ // Read the pad state.
+ SceCtrlData pad;
+ sceCtrlPeekBufferPositive(&pad, 1);
+
+ // Find out which buttons have changed.
+ SceCtrlData deltaPad;
+ deltaPad.Buttons = pad.Buttons ^ lastPad.Buttons;
+ deltaPad.Lx = pad.Lx - lastPad.Lx;
+ deltaPad.Ly = pad.Ly - lastPad.Ly;
+ deltaPad.TimeStamp = pad.TimeStamp - lastPad.TimeStamp;
+
+ // Handle buttons which have changed.
+ for (unsigned int button = 0; button < buttonCount; ++button)
+ {
+ // Has the button changed?
+ const unsigned int buttonMask = 1 << button;
+
+ if (deltaPad.Buttons & buttonMask)
+ {
+ // Is the button in the map?
+ const int key = (*buttonToKeyMap)[button];
+ if (key)
+ {
+ // Send an event.
+ const qboolean state = (pad.Buttons & buttonMask) ? qtrue : qfalse;
+ Key_Event(key, state);
+ }
+ }
+ }
+
+ // Remember the pad state for next time.
+ lastPad = pad;
+}
+
+float IN_CalcInput(int axis, float speed, float tolerance, float acceleration) {
+
+ float value = ((float) axis / 128.0f) - 1.0f;
+
+ if (value == 0.0f) {
+ return 0.0f;
+ }
+
+ float abs_value = fabs(value);
+
+ if (abs_value < tolerance) {
+ return 0.0f;
+ }
+
+ abs_value -= tolerance;
+ abs_value /= (1.0f - tolerance);
+ abs_value = powf(abs_value, acceleration);
+ abs_value *= speed;
+
+ if (value < 0.0f) {
+ value = -abs_value;
+ } else {
+ value = abs_value;
+ }
+ return value;
+}
+
+extern cvar_t scr_fov;
+extern int original_fov, final_fov;
+void IN_Move (usercmd_t *cmd)
+{
+ unsigned char analog_strafe = 0;
+ // Don't let the pitch drift back to centre if analog nub look is on.
+ if (in_mlook.value)
+ V_StopPitchDrift();
+ else {
+ if (in_analog_strafe.value || (in_strafe.state & 1)) {
+ analog_strafe = 1;
+ }
+ }
+ // Read the pad state.
+ SceCtrlData pad;
+ sceCtrlPeekBufferPositive(&pad, 1);
+
+ // Convert the inputs to floats in the range [-1, 1].
+ // Implement the dead zone.
+ float speed;
+ float deadZone = in_tolerance.value;
+ float acceleration = in_acceleration.value;
+ int x_adjust = in_x_axis_adjust.value;
+ int y_adjust = in_y_axis_adjust.value;
+
+ //shpuld begin
+ if (!analog_strafe) {
+ speed = in_sensitivity.value;
+
+ // ==== Aim Assist + ====
+ // cut look speed in half when facing enemy, unless
+ // mag is empty
+ if ((in_aimassist.value) && (sv_player->v.facingenemy == 1) && cl.stats[STAT_CURRENTMAG] > 0) {
+ speed *= 0.5;
+ }
+ // additionally, slice look speed when ADS/scopes
+ if (cl.stats[STAT_ZOOM] == 1)
+ speed *= 0.5;
+ else if (cl.stats[STAT_ZOOM] == 2)
+ speed *= 0.25;
+ } else {
+ speed = sv_player->v.maxspeed/150;
+ if (cl.stats[STAT_ZOOM] == 1)
+ speed *= 2;
+ else if (cl.stats[STAT_ZOOM] == 2)
+ speed *= 4;
+ }
+ //shpuld end
+
+ float x = IN_CalcInput(pad.Lx+x_adjust, speed, deadZone, acceleration);
+ float y = IN_CalcInput(pad.Ly+y_adjust, speed, deadZone, acceleration);
+
+ // Set the yaw.
+
+ // Analog nub look?
+ if (!analog_strafe) {
+ const float yawScale = 30.0f;
+ cl.viewangles[YAW] -= yawScale * x * host_frametime;
+
+ if (in_mlook.value)
+ {
+ // Set the pitch.
+ const bool invertPitch = m_pitch.value < 0;
+ const float pitchScale = yawScale * (invertPitch ? -1 : 1);
+ cl.viewangles[PITCH] += pitchScale * y * host_frametime;
+
+ // Don't look too far up or down.
+ if (cl.viewangles[PITCH] > 80.0f)
+ cl.viewangles[PITCH] = 80.0f;
+ if (cl.viewangles[PITCH] < -70.0f)
+ cl.viewangles[PITCH] = -70.0f;
+ }
+ else
+ {
+ // Move using up and down.
+ cmd->forwardmove -= cl_forwardspeed * y;
+ }
+ } else {
+ cmd->sidemove += cl_sidespeed * x;
+ cmd->forwardmove -= cl_forwardspeed * y;
+ }
+}
diff --git a/source/psp/iridlibs/cred.txt b/source/psp/iridlibs/cred.txt
new file mode 100644
index 0000000..897e129
--- /dev/null
+++ b/source/psp/iridlibs/cred.txt
@@ -0,0 +1 @@
+creds to iridescentrose
diff --git a/source/psp/iridlibs/driver/me.h b/source/psp/iridlibs/driver/me.h
new file mode 100644
index 0000000..2157085
--- /dev/null
+++ b/source/psp/iridlibs/driver/me.h
@@ -0,0 +1,37 @@
+#ifndef me_h
+#define me_h
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct me_struct
+{
+ int start;
+ int done;
+ int (*func)(int); // function ptr - func takes an int argument and returns int
+ int param; // function argument
+ int result; // function return value
+ int precache_len; // amount of space to invalidate before running func, -1 = all
+ void *precache_addr; // address of space to invalidate before running func
+ int postcache_len; // amount of space to flush after running func, -1 = all
+ void *postcache_addr; // address of space to flush after running func
+ unsigned int signals;
+};
+
+int InitME(volatile struct me_struct *mei );
+void KillME(volatile struct me_struct *mei );
+int CallME(volatile struct me_struct *mei, int func, int param, int prelen, void *preadr, int postlen, void *postadr);
+int WaitME(volatile struct me_struct *mei);
+int BeginME(volatile struct me_struct *mei, int func, int param, int prelen, void *preadr, int postlen, void *postadr);
+int CheckME(volatile struct me_struct *mei);
+unsigned int SignalME(volatile struct me_struct *mei, unsigned int sigmask, unsigned int sigset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/psp/iridlibs/libME.a b/source/psp/iridlibs/libME.a
new file mode 100644
index 0000000..02ed73b
Binary files /dev/null and b/source/psp/iridlibs/libME.a differ
diff --git a/source/psp/iridlibs/libPerf.a b/source/psp/iridlibs/libPerf.a
new file mode 100644
index 0000000..369c8ca
Binary files /dev/null and b/source/psp/iridlibs/libPerf.a differ
diff --git a/source/psp/iridlibs/melib.h b/source/psp/iridlibs/melib.h
new file mode 100644
index 0000000..682f28f
--- /dev/null
+++ b/source/psp/iridlibs/melib.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+/**
+* The mode of execution for a specific job.
+*/
+#define MELIB_EXEC_DEFAULT 0x0 /** Executes on the ME, unless Dynamic Rebalancing is turned on */
+#define MELIB_EXEC_CPU 0x1 /** Executes specifically on the main CPU, regardless of job mode. Always runs synchronously (gives a small delay between jobs).*/
+#define MELIB_EXEC_ME 0x2 /** Executes specifically on the Media Engine, regardless of job mode. Always runs asynchronously.*/
+
+
+/**
+* This structure is used to determine job execution.
+* Given the properties in this structure, the manager will execute correctly
+*/
+struct JobInfo {
+ unsigned char id; /** This ID is purely useless for now - but may be used in the future for performance tracking */
+ unsigned char execMode; /** Uses execution mode to specify where the code will run and/or if said code can be dynamically rebalanced */
+};
+
+struct Job;
+
+/**
+* Job Data is an integer pointer to an address with the data.
+*/
+typedef int JobData;
+
+/**
+* This typedef defines a JobFunction as an integer function with given data.
+*/
+typedef int (*JobFunction)(JobData ptr);
+
+/**
+* This structure is used to give job information alongside the job itself and the data needed.
+*/
+struct Job {
+ struct JobInfo jobInfo;
+ JobFunction function;
+ JobData data;
+};
+
+/**
+* JobManager class. This class only can have one instance for the ME.
+*/
+
+void J_Init(bool dynamicRebalance); /** Initialize the job manager with the option to dynamically rebalance loads. */
+void J_Cleanup(); /** Cleans up and ends execution. */
+
+void J_AddJob(struct Job* job); /** Adds a job to the queue. If the queue is full (max 256) then it will force a dispatch before adding more. */
+void J_ClearJob(); /** Clears and deletes all jobs */
+
+void J_DispatchJobs(float cpuTime); /** Starts a thread to dispatch jobs and execute! CPU Time is the time used by the rest of the system for Dynamic Rebalancing - it's unused otherwise. */
+
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/source/psp/iridlibs/perflib.h b/source/psp/iridlibs/perflib.h
new file mode 100644
index 0000000..6c57125
--- /dev/null
+++ b/source/psp/iridlibs/perflib.h
@@ -0,0 +1,82 @@
+#pragma once
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /**
+ * This method clears the internal timers and mechanisms of PFL
+ * @param frameRelative - setting this to true means that you compare percentage of GPU vs CPU. This is useful for finding bottlenecks.
+ */
+ void PFL_Init(bool frameRelative);
+
+ /**
+ * This method begins a record of when the CPU has begun doing tasks. This should be put immediately at the beginning of your game loop.
+ */
+ void PFL_BeginCPURecord();
+
+ /**
+ * This method ends a record of when the CPU has finished doing tasks. This should be put before the final rendering call of your game loop.
+ */
+ void PFL_EndCPURecord();
+
+ /**
+ * This method extracts the CPU time taken between the last Start and End of the CPU performance recording (in milliseconds).
+ */
+ double PFL_GetCPUTime();
+
+ /**
+ * This method helps in calculation of the CPU and GPU percentages by setting a targeted framerate. Percentages above 100 mean that the CPU and or GPU is taking longer than expected. This only matters in non-full profiling mode.
+ */
+ void PFL_SetTargetFrameRate(int framerate);
+
+ /**
+ * Returns the current frame rate target. This only matters in non-full profiling mode.
+ */
+ int PFL_GetTargetFrameRate();
+
+ /**
+ * This method extracts the CPU time taken between the last Start and End of the CPU performance recording.
+ */
+ double PFL_GetCPUPercentage();
+
+ /**
+ * This method begins a record of when the GPU begins doing tasks. This should be put immediately after sceGuFinish() and before sceGuSync(0, 0)
+ */
+ void PFL_BeginGPURecord();
+
+ /**
+ * This method ends a record of when the GPU has finished doing tasks. This should be put immediately after sceGuSync(0, 0)
+ */
+ void PFL_EndGPURecord();
+
+ /**
+ * This method begins a record of when the system is waiting for a VBlank. This should be put immediately before sceDisplayWaitVblankStart() and swap buffers.
+ */
+ void PFL_BeginVBLRecord();
+
+ /**
+ * This method begins a record of when the system has finished a VBlank. This should be put immediately after sceGuSwapBuffers().
+ */
+ void PFL_EndVBLRecord();
+
+ /**
+ * This method extracts the total GPU time taken.
+ */
+ double PFL_GetGPUTime();
+
+ /**
+ * This method extracts the total GPU time as a percentage.
+ */
+ double PFL_GetGPUPercentage();
+
+ /**
+ * This method extracts the total VBL time taken.
+ */
+ double PFL_GetVBLTime();
+
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/source/psp/m33libs/kubridge.h b/source/psp/m33libs/kubridge.h
new file mode 100644
index 0000000..aa8a065
--- /dev/null
+++ b/source/psp/m33libs/kubridge.h
@@ -0,0 +1,98 @@
+#ifndef __KULIBRARY__
+
+#define __KULIBRARY__
+
+#include
+#include
+#include
+#include
+
+/**
+ * Functions to let user mode access certain functions only available in
+ * kernel mode
+*/
+
+/**
+ * Load a module using ModuleMgrForKernel.
+ *
+ * @param path - The path to the module to load.
+ * @param flags - Unused, always 0 .
+ * @param option - Pointer to a mod_param_t structure. Can be NULL.
+ *
+ * @returns The UID of the loaded module on success, otherwise one of ::PspKernelErrorCodes.
+ */
+SceUID kuKernelLoadModule(const char *path, int flags, SceKernelLMOption *option);
+
+
+/**
+ * Load a module with a specific apitype
+ *
+ * @param apìtype - The apitype
+ * @param path - The path to the module to load.
+ * @param flags - Unused, always 0 .
+ * @param option - Pointer to a mod_param_t structure. Can be NULL.
+ *
+ * @returns The UID of the loaded module on success, otherwise one of ::PspKernelErrorCodes.
+ */
+SceUID kuKernelLoadModuleWithApitype2(int apitype, const char *path, int flags, SceKernelLMOption *option);
+
+/**
+ * Gets the api type
+ *
+ * @returns the api type in which the system has booted
+*/
+int kuKernelInitApitype();
+
+/**
+ * Gets the filename of the executable to be launched after all modules of the api.
+ *
+ * @param initfilename - String where copy the initfilename
+ * @returns 0 on success
+*/
+int kuKernelInitFileName(char *initfilename);
+
+/**
+ *
+ * Gets the device in which the application was launched.
+ *
+ * @returns the device code, one of PSPBootFrom values.
+*/
+int kuKernelBootFrom();
+
+/**
+ * Get the key configuration in which the system has booted.
+ *
+ * @returns the key configuration code, one of PSPKeyConfig values
+*/
+int kuKernelInitKeyConfig();
+
+/**
+ * Get the user level of the current thread
+ *
+ * @return The user level, < 0 on error
+ */
+int kuKernelGetUserLevel(void);
+
+/**
+ * Set the protection of a block of ddr memory
+ *
+ * @param addr - Address to set protection on
+ * @param size - Size of block
+ * @param prot - Protection bitmask
+ *
+ * @return < 0 on error
+ */
+int kuKernelSetDdrMemoryProtection(void *addr, int size, int prot);
+
+/**
+ * Gets the model of the PSP from user mode.
+ * This function is available since 3.60 M33.
+ * In previous version, use the kernel function sceKernelGetModel
+ *
+ * @return one of PspModel values
+*/
+int kuKernelGetModel(void);
+
+
+#endif
+
diff --git a/source/psp/m33libs/libpng.a b/source/psp/m33libs/libpng.a
new file mode 100644
index 0000000..098840f
Binary files /dev/null and b/source/psp/m33libs/libpng.a differ
diff --git a/source/psp/m33libs/libpspaudiocodec.a b/source/psp/m33libs/libpspaudiocodec.a
new file mode 100644
index 0000000..56a287d
Binary files /dev/null and b/source/psp/m33libs/libpspaudiocodec.a differ
diff --git a/source/psp/m33libs/libpspkubridge.a b/source/psp/m33libs/libpspkubridge.a
new file mode 100644
index 0000000..779c54f
Binary files /dev/null and b/source/psp/m33libs/libpspkubridge.a differ
diff --git a/source/psp/m33libs/libz.a b/source/psp/m33libs/libz.a
new file mode 100644
index 0000000..a040180
Binary files /dev/null and b/source/psp/m33libs/libz.a differ
diff --git a/source/psp/main.cpp b/source/psp/main.cpp
new file mode 100644
index 0000000..09035fa
--- /dev/null
+++ b/source/psp/main.cpp
@@ -0,0 +1,794 @@
+/*
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#define CONSOLE_DEBUG 0
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+extern "C"
+{
+#include "../quakedef.h"
+#include "../thread.h"
+#include "m33libs/kubridge.h"
+#include "iridlibs/perflib.h"
+void VramSetSize(int kb);
+}
+
+#include "battery.hpp"
+#include "system.hpp"
+#include "module.h"
+
+// Running a dedicated server?
+qboolean isDedicated = qfalse;
+
+qboolean depthfl = qfalse;
+
+extern int com_argc;
+extern char **com_argv;
+
+int sys_psp_model;
+
+void Sys_ReadCommandLineFile (char* netpath);
+
+/*
+ MOTO - Can't go above heap of 10MB on PHAT models.. which is an issue, in theory
+ we should be able to cap at a heap size 14MB.. oh well.
+*/
+
+#define printf pspDebugScreenPrintf
+#define MIN_HEAP_MB 6
+#define MAX_HEAP_MB (PSP_HEAP_SIZE_MB-1)
+
+namespace quake
+{
+ namespace main
+ {
+ // Clock speeds.
+ extern const int cpuClockSpeed = scePowerGetCpuClockFrequencyInt();
+ extern const int ramClockSpeed = cpuClockSpeed;
+ extern const int busClockSpeed = scePowerGetBusClockFrequencyInt();
+
+#ifdef PSP_SOFTWARE_VIDEO
+ #ifdef SLIM
+ // How big a heap to allocate.
+ static size_t heapSize = 16 * 1024 * 1024;
+ #else
+ static size_t heapSize = 17 * 1024 * 1024;
+ #endif
+#endif // PSP_SOFTWARE_VIDEO
+#ifdef KERNEL_MODE
+ #ifdef SLIM
+ // How big a heap to allocate.
+ static size_t heapSize = 34 * 1024 * 1024;
+ #else
+ static size_t heapSize = 10 * 1024 * 1024;
+ #endif
+#else
+ #ifdef SLIM
+ // How big a heap to allocate.
+ static size_t heapSize = 34 * 1024 * 1024;
+ #else
+ static size_t heapSize = 10 * 1024 * 1024;
+ #endif
+#endif
+
+ // Should the main loop stop running?
+ static volatile bool quit = false;
+
+ // Is the PSP in suspend mode?
+ static volatile bool suspended = false;
+
+ static int exitCallback(int arg1, int arg2, void* common)
+ {
+ // Signal the main thread to stop.
+ quit = true;
+ return 0;
+ }
+
+ static int powerCallback(int unknown, int powerInfo, void* common)
+ {
+ if (powerInfo & PSP_POWER_CB_POWER_SWITCH || powerInfo & PSP_POWER_CB_SUSPENDING)
+ {
+ suspended = true;
+ }
+ else if (powerInfo & PSP_POWER_CB_RESUMING)
+ {
+ }
+ else if (powerInfo & PSP_POWER_CB_RESUME_COMPLETE)
+ {
+ suspended = false;
+ }
+
+ return 0;
+ }
+
+ static int callback_thread(SceSize args, void *argp)
+ {
+ // Register the exit callback.
+ const SceUID exitCallbackID = sceKernelCreateCallback("exitCallback", exitCallback, NULL);
+ sceKernelRegisterExitCallback(exitCallbackID);
+
+ // Register the power callback.
+ const SceUID powerCallbackID = sceKernelCreateCallback("powerCallback", powerCallback, NULL);
+ scePowerRegisterCallback(0, powerCallbackID);
+
+ // Sleep and handle callbacks.
+ sceKernelSleepThreadCB();
+ return 0;
+ }
+
+ static int setUpCallbackThread(void)
+ {
+ const int thid = sceKernelCreateThread("update_thread", callback_thread, 0x11, 0xFA0, PSP_THREAD_ATTR_USER, 0);
+ if (thid >= 0)
+ sceKernelStartThread(thid, 0, 0);
+ return thid;
+ }
+//#endif
+
+ static void disableFloatingPointExceptions()
+ {
+#ifndef _WIN32
+ asm(
+ ".set push\n"
+ ".set noreorder\n"
+ "cfc1 $2, $31\n" // Get the FP Status Register.
+ "lui $8, 0x80\n" // (?)
+ "and $8, $2, $8\n" // Mask off all bits except for 23 of FCR. (? - where does the 0x80 come in?)
+ "ctc1 $8, $31\n" // Set the FP Status Register.
+ ".set pop\n"
+ : // No inputs.
+ : // No outputs.
+ : "$2", "$8" // Clobbered registers.
+ );
+#endif
+ }
+ }
+}
+
+using namespace quake;
+using namespace quake::main;
+
+#define MAX_NUM_ARGVS 50
+#define MAX_ARG_LENGTH 64
+static char *empty_string = "";
+char *f_argv[MAX_NUM_ARGVS];
+int f_argc;
+int user_main(SceSize argc, void* argp);
+
+int CheckParm (char **args, int argc, char *parm)
+{
+ int i;
+
+ for (i=1 ; i= 0) {
+ SceIoDirent* p_dir_de = new SceIoDirent;
+ int res = -1;
+
+ do {
+ res = sceIoDread(dir_fd, p_dir_de);
+ if ((res > 0) && (p_dir_de->d_stat.st_attr & FIO_SO_IFDIR)) {
+ if (!(stricmp(p_dir_de->d_name, ".") == 0 || stricmp(p_dir_de->d_name, "..") == 0 ||
+ stricmp(p_dir_de->d_name, "mp3") == 0 || stricmp(p_dir_de->d_name, "id1") == 0)) {
+ dirs[j++] = strdup( p_dir_de->d_name);
+ }
+ }
+ } while (res > 0);
+ delete p_dir_de;
+ }
+ sceIoDclose(dir_fd);
+ //-- dirs setup
+
+ menu_max[0] = j-1;
+
+ if (CheckParm(args, f_argc, "-cpu333"))
+ menu_cur[1] = 1;
+
+ if (CheckParm(args, f_argc, "-heap")) {
+ int idx = CheckParm(args, f_argc, "-heap");
+
+ if (idx+1 < f_argc) {
+ for (int i = 0 ;i < MAX_HEAP_MB - MIN_HEAP_MB; i++) {
+ if (stricmp(heaps[i], args[idx+1]) == 0) {
+ menu_cur[2] = i;
+ }
+
+ if (args[idx+1][0] == '-') {
+ args[idx][0] = '\0';
+ }
+ }
+ }
+ } else {
+ for (int i = 0 ;i < MAX_HEAP_MB - MIN_HEAP_MB; i++) {
+ sprintf(temp_str, "%d", heapSize/(1024*1024));
+ if (stricmp(heaps[i],temp_str) == 0) {
+ menu_cur[2] = i;
+ }
+ }
+ }
+
+ if (CheckParm(args, f_argc, "-game")) {
+ int idx = CheckParm(args, f_argc, "-game");
+
+ if (idx+1 < f_argc) {
+ for (int i = 0 ;i < j; i++) {
+ if (stricmp(args[idx+1],dirs[i]) == 0) {
+ menu_cur[0] = i;
+ }
+
+ if (args[idx+1][0] == '-') {
+ args[idx][0] = '\0';
+ }
+ }
+ }
+ }
+
+ while(!done) {
+ sceDisplayWaitVblankStart();
+ sceDisplayWaitVblankStart();
+ sceDisplayWaitVblankStart();
+ sceDisplayWaitVblankStart();
+ sceDisplayWaitVblankStart();
+ sceDisplayWaitVblankStart();
+
+ pspDebugScreenSetXY(0, 2);
+ sceCtrlReadBufferPositive(&pad, 1);
+
+ pspDebugScreenSetTextColor(0xffffff);
+
+ printf("DQuake v%4.2f \n", (float)VERSION);
+ printf("---------------- \n");
+ printf("Command line file : %s \n", cmdlinePath);
+ printf("Startup directory : %s \n", currentDirectory);
+ printf("Game directory : %s \n", gameDirectory);
+ printf("\n");
+ printf("Press [X]Cross to Continue [O]Circle to Quit\n");
+ printf("\n");
+
+ pspDebugScreenSetTextColor(0x00ffff);
+
+ if (j > -1) {
+ if (menu_item_cur == 0)
+ pspDebugScreenSetTextColor(0x00ff00);
+ printf("Mod directory : [%d/%d] '%s' \t \n", 1+menu_cur[0], 1+menu_max[0], dirs[menu_cur[0]]);
+ pspDebugScreenSetTextColor(0x00ffff);
+ }
+ if (menu_item_cur == 1)
+ pspDebugScreenSetTextColor(0x00ff00);
+ printf("CPU Speed : [%d/%d] '%s' \t \n", 1+menu_cur[1], 1+menu_max[1], cpus[menu_cur[1]]);
+ pspDebugScreenSetTextColor(0x00ffff);
+
+ if (menu_item_cur == 2)
+ pspDebugScreenSetTextColor(0x00ff00);
+ printf("Heap Size : [%d/%d] '%s' \t \n", 1+menu_cur[2], 1+menu_max[2], heaps[menu_cur[2]]);
+ pspDebugScreenSetTextColor(0xffffff);
+
+ if (pad.Buttons != 0) {
+ if (pad.Buttons & PSP_CTRL_CIRCLE){
+ pspDebugScreenSetTextColor(0x0000ff);
+ printf("\n");
+ printf("Shutting down...\n");
+ printf("\n");
+ pspDebugScreenSetTextColor(0x00ffff);
+ Sys_Quit();
+ }
+ if (pad.Buttons & PSP_CTRL_CROSS){
+ done = true;
+ }
+ if (pad.Buttons & PSP_CTRL_UP){
+ menu_item_cur--;
+ menu_item_cur = (menu_item_cur < menu_item_min ? menu_item_min : menu_item_cur);
+ }
+ if (pad.Buttons & PSP_CTRL_DOWN){
+ menu_item_cur++;
+ menu_item_cur = (menu_item_cur > menu_item_max ? menu_item_max : menu_item_cur);
+ }
+ if (pad.Buttons & PSP_CTRL_LEFT){
+ menu_cur[menu_item_cur]--;
+ menu_cur[menu_item_cur] = (menu_cur[menu_item_cur] < menu_min[menu_item_cur] ? menu_min[menu_item_cur] : menu_cur[menu_item_cur]);
+ }
+ if (pad.Buttons & PSP_CTRL_RIGHT){
+ menu_cur[menu_item_cur]++;
+ menu_cur[menu_item_cur] = (menu_cur[menu_item_cur] > menu_max[menu_item_cur] ? menu_max[menu_item_cur] : menu_cur[menu_item_cur]);
+ }
+ }
+ if(sceHprmIsRemoteExist()){
+ unsigned int hprmkey;
+ sceHprmPeekCurrentKey(&hprmkey);
+ if (hprmkey != 0){
+ if (hprmkey & PSP_HPRM_BACK){
+ menu_cur[menu_item_cur]--;
+ menu_cur[menu_item_cur] = (menu_cur[menu_item_cur] < menu_min[menu_item_cur] ? menu_min[menu_item_cur] : menu_cur[menu_item_cur]);
+ }
+ if (hprmkey & PSP_HPRM_FORWARD){
+ menu_cur[menu_item_cur]++;
+ menu_cur[menu_item_cur] = (menu_cur[menu_item_cur] > menu_max[menu_item_cur] ? menu_max[menu_item_cur] : menu_cur[menu_item_cur]);
+ }
+ if (hprmkey & PSP_HPRM_PLAYPAUSE){
+ done = true;
+ }
+ }
+ }
+
+ }
+ if (CheckParm(args, f_argc, "-game")) {
+ int idx = CheckParm(args, f_argc, "-game");
+ if (stricmp(args[idx+1],dirs[menu_cur[0]]) != 0) {
+
+ if (strlen(dirs[menu_cur[0]]) > 0) {
+ int len2 = strlen(dirs[menu_cur[0]]);
+ args[idx+1] = new char[len2+1];
+ strcpy(args[idx+1], dirs[menu_cur[0]]);
+ } else {
+ strcpy(args[idx], "");
+ strcpy(args[idx+1], "");
+ }
+ }
+ }
+ else {
+ if (strlen(dirs[menu_cur[0]]) > 0) {
+ int len1 = strlen("-game");
+ int len2 = strlen(dirs[menu_cur[0]]);
+
+ args[f_argc] = new char[len1+1];
+ args[f_argc+1] = new char[len2+1];
+
+ strcpy(args[f_argc], "-game");
+ strcpy(args[f_argc+1], dirs[menu_cur[0]]);
+
+ f_argc += 2;
+ }
+ }
+
+ if (stricmp(cpus[menu_cur[1]],"333") == 0) {
+
+ if (!CheckParm(args, f_argc, "-cpu333")) {
+ int len1 = strlen("-cpu333");
+
+ args[f_argc] = new char[len1+1];
+ strcpy(args[f_argc], "-cpu333");
+
+ f_argc += 1;
+ }
+ } else {
+ if (CheckParm(args, f_argc, "-cpu333")) {
+ int idx = CheckParm(args, f_argc, "-cpu333");
+ strcpy(args[idx], "-cpu222");
+ }
+ }
+
+ if (CheckParm(args, f_argc, "-heap")) {
+
+ int idx = CheckParm(args, f_argc, "-heap");
+
+ int len2 = strlen(heaps[menu_cur[2]]);
+
+ args[idx+1] = new char[len2+1];
+ strcpy(args[idx+1], heaps[menu_cur[2]]);
+ }
+ else {
+ int len1 = strlen("-heap");
+ int len2 = strlen(heaps[menu_cur[2]]);
+
+ args[f_argc] = new char[len1+1];
+ args[f_argc+1] = new char[len2+1];
+
+ strcpy(args[f_argc], "-heap");
+ strcpy(args[f_argc+1], heaps[menu_cur[2]]);
+
+ f_argc += 2;
+ }
+
+ // get rid of temp. alloc memory
+ for (int i = 0 ;i < MAX_HEAP_MB - MIN_HEAP_MB; i++) {
+ free(heaps[i]);
+ }
+ for (int i = 0 ;i < j; i++) {
+ free(dirs[i]);
+ }
+ free(cpus[0]);
+ free(cpus[1]);
+ pspDebugScreenClear();
+ }
+}
+
+int ctrl_kernel = 0;
+SceUID mod[2];
+char mod_names[2][64];
+
+void InitExtModules (void)
+{
+ char currentDirectory[1024];
+ char gameDirectory[1024];
+ char path_f[256];
+
+ memset(gameDirectory, 0, sizeof(gameDirectory));
+ memset(currentDirectory, 0, sizeof(currentDirectory));
+ getcwd(currentDirectory, sizeof(currentDirectory) - 1);
+
+ strcpy(path_f,currentDirectory);
+ strcat(path_f,"/hooks/vramext.prx");
+ sprintf(mod_names[1], path_f);
+
+ mod[1] = pspSdkLoadStartModule(mod_names[1], PSP_MEMORY_PARTITION_KERNEL);
+ if (mod[1] >= 0)
+ {
+ if (kuKernelGetModel() != 0)
+ VramSetSize(4096);
+ else
+ VramSetSize(2048);
+ }
+}
+
+void ShutdownExtModules (void)
+{
+ if(mod[0] < 0)
+ return;
+
+ sceKernelStopModule(mod[0], 0, 0, 0, 0);
+ sceKernelUnloadModule(mod[0]);
+}
+
+int main(int argc, char *argv[])
+{
+#ifdef KERNEL_MODE
+ // Load the network modules
+
+ // create user thread, tweek stack size here if necessary
+ SceUID thid = sceKernelCreateThread("User Mode Thread", user_main,
+ 0x20, // default priority
+ 512 * 1024, // stack size (256KB is regular default)
+ PSP_THREAD_ATTR_USBWLAN | PSP_THREAD_ATTR_USER | PSP_THREAD_ATTR_VFPU , NULL);
+
+ // start user thread, then wait for it to do everything else
+ sceKernelStartThread(thid, 0, NULL);
+ sceKernelWaitThreadEnd(thid, NULL);
+
+ sceKernelExitGame();
+}
+
+int user_main(SceSize argc, void* argp)
+ {
+#endif
+ // Set up the callback thread, this is not appropriate for use with
+ // the loader, so don't bother calling it as it apparently seems to
+ // cause problems with firmware 2.0+
+ setUpCallbackThread();
+
+
+ // Disable floating point exceptions.
+ // If this isn't done, Quake crashes from (presumably) divide by zero
+ // operations.
+ disableFloatingPointExceptions();
+
+ // init perflib
+ PFL_Init(true);
+
+ // Initialise the Common module.
+ InitExtModules ();
+
+ // Get the current working dir.
+ char currentDirectory[1024];
+ char gameDirectory[1024];
+
+ memset(gameDirectory, 0, sizeof(gameDirectory));
+ memset(currentDirectory, 0, sizeof(currentDirectory));
+ getcwd(currentDirectory, sizeof(currentDirectory) - 1);
+
+ char path_f[256];
+ strcpy(path_f,currentDirectory);
+ strcat(path_f,"/setup.ini");
+ Sys_ReadCommandLineFile(path_f);
+
+ char *args[MAX_NUM_ARGVS];
+
+ for (int k =0; k < f_argc; k++) {
+ int len = strlen(f_argv[k]);
+ args[k] = new char[len+1];
+ strcpy(args[k], f_argv[k]);
+ }
+
+ if (CheckParm(args, f_argc,"-32depth"))
+ depthfl = qtrue;
+
+ if (CheckParm(args, f_argc, "-gamedir"))
+ {
+ char* tempStr = args[CheckParm(args, f_argc,"-gamedir")+1];
+ strncpy(gameDirectory, tempStr, sizeof(gameDirectory)-1);
+ }
+ else
+ {
+ strncpy(gameDirectory,currentDirectory,sizeof(gameDirectory)-1);
+ }
+
+ /////
+ StartUpParams(args, f_argc, path_f, currentDirectory, gameDirectory);
+
+ if (CheckParm(args, f_argc, "-heap")) {
+ char* tempStr = args[CheckParm(args, f_argc,"-heap")+1];
+ int heapSizeMB = atoi(tempStr);
+
+ if (heapSizeMB < MIN_HEAP_MB )
+ heapSizeMB = MIN_HEAP_MB;
+
+ if (heapSizeMB > MAX_HEAP_MB )
+ heapSizeMB = MAX_HEAP_MB;
+
+ heapSize = heapSizeMB * 1024 * 1024;
+ }
+ // Allocate the heap.
+ std::vector heap(heapSize, 0);
+
+#if CONSOLE_DEBUG
+ if (f_argc > 1) {
+ args[f_argc++] = "-condebug";
+ COM_InitArgv(f_argc, args);
+ }
+ else {
+ args[0] = "";
+ args[1] = "-condebug";
+ COM_InitArgv(2, args);
+ }
+#else
+ if (f_argc > 1)
+ COM_InitArgv(f_argc, args);
+ else {
+ args[0] = "";
+ COM_InitArgv(0, NULL);
+ }
+#endif
+
+#ifdef PSP_SOFTWARE_VIDEO
+ // Bump up the clock frequency.
+ if (!COM_CheckParm("-cpu222")) {
+ scePowerSetClockFrequency(333, 333, 166);
+ //PFL_SetCPUFrequency(222);
+ }
+#else
+ if (COM_CheckParm("-cpu333")) {
+ scePowerSetClockFrequency(333, 333, 166);
+ //PFL_SetCPUFrequency(333);
+ }
+#endif
+
+ // Catch exceptions from here.
+ try
+ {
+ // Initialise the Host module.
+ quakeparms_t parms;
+ memset(&parms, 0, sizeof(parms));
+ parms.argc = com_argc;
+ parms.argv = com_argv;
+ parms.basedir = gameDirectory;
+ parms.memsize = heap.size();
+ parms.membase = &heap.at(0);
+ Host_Init(&parms);
+
+ // Precalculate the tick rate.
+ const float oneOverTickRate = 1.0f / sceRtcGetTickResolution();
+
+ // Record the time that the main loop started.
+ u64 lastTicks;
+ sceRtcGetCurrentTick(&lastTicks);
+
+ // Set up threads
+ Sys_InitThreads();
+
+ // Enter the main loop.
+ while (!quit)
+ {
+ // start cpu profiling
+ PFL_BeginCPURecord();
+
+ // Handle suspend & resume.
+ if (suspended)
+ {
+ // Suspend.
+ S_ClearBuffer();
+ quake::system::suspend();
+
+ // Wait for resume.
+ while (suspended && !quit)
+ {
+ sceDisplayWaitVblankStart();
+ }
+
+ // Resume.
+ quake::system::resume();
+
+ // Reset the clock.
+ sceRtcGetCurrentTick(&lastTicks);
+ }
+
+ // What is the time now?
+ u64 ticks;
+ sceRtcGetCurrentTick(&ticks);
+
+ // How much time has elapsed?
+ const unsigned int deltaTicks = ticks - lastTicks;
+ const float deltaSeconds = deltaTicks * oneOverTickRate;
+
+ // Check the battery status.
+ battery::check();
+
+ // Run the frame.
+ Host_Frame(deltaSeconds);
+ // Remember the time for next frame.
+ lastTicks = ticks;
+
+ // end cpu profiling
+ PFL_EndCPURecord();
+ }
+ }
+ catch (const std::exception& e)
+ {
+ // Report the error and quit.
+ Sys_Error("C++ Exception: %s", e.what());
+ return 0;
+ }
+
+ // Quit.
+ Sys_Quit();
+ return 0;
+}
+
+void Sys_ReadCommandLineFile (char* netpath)
+{
+ int in;
+ int remaining, count;
+ char buf[4096];
+ int argc = 1;
+
+ remaining = Sys_FileOpenRead (netpath, &in);
+
+ if (in > 0 && remaining > 0) {
+ count = Sys_FileRead (in, buf, 4096);
+ f_argv[0] = empty_string;
+
+ char* lpCmdLine = buf;
+
+ while (*lpCmdLine && (argc < MAX_NUM_ARGVS))
+ {
+ while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
+ lpCmdLine++;
+
+ if (*lpCmdLine)
+ {
+ f_argv[argc] = lpCmdLine;
+ argc++;
+
+ while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
+ lpCmdLine++;
+
+ if (*lpCmdLine)
+ {
+ *lpCmdLine = 0;
+ lpCmdLine++;
+ }
+ }
+ }
+ f_argc = argc;
+ } else {
+ f_argc = 0;
+ }
+
+ if (in > 0)
+ Sys_FileClose (in);
+}
+
+char* Sys_GetPSPModel(void)
+{
+ // check for the vita prx
+ int vitaprx = sceIoOpen("flash0:/kd/registry.prx", PSP_O_RDONLY | PSP_O_WRONLY, 0777);
+
+ if (vitaprx >= 0) {
+ sceIoClose(vitaprx);
+ return "PS Vita (PSP2)";
+ }
+
+ // normal psp models
+ char* modelstring;
+
+ sys_psp_model = kuKernelGetModel();
+ switch(sys_psp_model) {
+ case 0:
+ modelstring = "PSP Phat (1000)";
+ break;
+ case 1:
+ modelstring = "PSP Slim (2000)";
+ break;
+ case 2:
+ modelstring = "PSP Brite (3000)";
+ break;
+ case 4:
+ modelstring = "PSP GO (4000)";
+ break;
+ case 10:
+ modelstring = "PSP Street (E1000)";
+ break;
+ default:
+ break;
+ }
+
+ return modelstring;
+}
\ No newline at end of file
diff --git a/source/psp/math.cpp b/source/psp/math.cpp
new file mode 100644
index 0000000..97dc2b2
--- /dev/null
+++ b/source/psp/math.cpp
@@ -0,0 +1,50 @@
+/*
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "math.hpp"
+
+namespace quake
+{
+ namespace math
+ {
+ void multiply(const ScePspFMatrix4& a, const ScePspFMatrix4& b, ScePspFMatrix4* out)
+ {
+ out->x.x = a.x.x * b.x.x + a.x.y * b.y.x + a.x.z * b.z.x + a.x.w * b.w.x;
+ out->x.y = a.x.x * b.x.y + a.x.y * b.y.y + a.x.z * b.z.y + a.x.w * b.w.y;
+ out->x.z = a.x.x * b.x.z + a.x.y * b.y.z + a.x.z * b.z.z + a.x.w * b.w.z;
+ out->x.w = a.x.x * b.x.w + a.x.y * b.y.w + a.x.z * b.z.w + a.x.w * b.w.w;
+
+ out->y.x = a.y.x * b.x.x + a.y.y * b.y.x + a.y.z * b.z.x + a.y.w * b.w.x;
+ out->y.y = a.y.x * b.x.y + a.y.y * b.y.y + a.y.z * b.z.y + a.y.w * b.w.y;
+ out->y.z = a.y.x * b.x.z + a.y.y * b.y.z + a.y.z * b.z.z + a.y.w * b.w.z;
+ out->y.w = a.y.x * b.x.w + a.y.y * b.y.w + a.y.z * b.z.w + a.y.w * b.w.w;
+
+ out->z.x = a.z.x * b.x.x + a.z.y * b.y.x + a.z.z * b.z.x + a.z.w * b.w.x;
+ out->z.y = a.z.x * b.x.y + a.z.y * b.y.y + a.z.z * b.z.y + a.z.w * b.w.y;
+ out->z.z = a.z.x * b.x.z + a.z.y * b.y.z + a.z.z * b.z.z + a.z.w * b.w.z;
+ out->z.w = a.z.x * b.x.w + a.z.y * b.y.w + a.z.z * b.z.w + a.z.w * b.w.w;
+
+ out->w.x = a.w.x * b.x.x + a.w.y * b.y.x + a.w.z * b.z.x + a.w.w * b.w.x;
+ out->w.y = a.w.x * b.x.y + a.w.y * b.y.y + a.w.z * b.z.y + a.w.w * b.w.y;
+ out->w.z = a.w.x * b.x.z + a.w.y * b.y.z + a.w.z * b.z.z + a.w.w * b.w.z;
+ out->w.w = a.w.x * b.x.w + a.w.y * b.y.w + a.w.z * b.z.w + a.w.w * b.w.w;
+ }
+ }
+}
diff --git a/source/psp/math.hpp b/source/psp/math.hpp
new file mode 100644
index 0000000..621ffbd
--- /dev/null
+++ b/source/psp/math.hpp
@@ -0,0 +1,54 @@
+/*
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef QUAKE_MATH_HPP
+#define QUAKE_MATH_HPP
+
+#include
+#include
+
+#ifdef PSP_VFPU
+#include
+#endif
+
+namespace quake
+{
+ namespace math
+ {
+ static inline float dot(const ScePspFVector4& a, const ScePspFVector4& b)
+ {
+ return (a.x * b.x) + (a.y * b.y) + (a.z * b.z);
+ }
+
+ static inline void normalise(ScePspFVector4* v)
+ {
+ const float scale = 1.0f / sqrtf(dot(*v, *v));
+
+ v->x *= scale;
+ v->y *= scale;
+ v->z *= scale;
+ v->w *= scale;
+ }
+
+ void multiply(const ScePspFMatrix4& a, const ScePspFMatrix4& b, ScePspFMatrix4* out);
+ }
+}
+
+#endif
diff --git a/source/psp/module.cpp b/source/psp/module.cpp
new file mode 100644
index 0000000..9d851b3
--- /dev/null
+++ b/source/psp/module.cpp
@@ -0,0 +1,28 @@
+/*
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include
+#include
+#include "module.h"
+
+// Set up the module info.
+PSP_MODULE_INFO("NZPortable", 0, 1, 1);
+PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER | PSP_THREAD_ATTR_VFPU);
+PSP_HEAP_SIZE_KB(-1024);
\ No newline at end of file
diff --git a/source/psp/module.h b/source/psp/module.h
new file mode 100644
index 0000000..9d9440a
--- /dev/null
+++ b/source/psp/module.h
@@ -0,0 +1,5 @@
+#ifdef SLIM
+#define PSP_HEAP_SIZE_MB 42
+#else
+#define PSP_HEAP_SIZE_MB 18
+#endif
\ No newline at end of file
diff --git a/source/psp/mp3.c b/source/psp/mp3.c
new file mode 100644
index 0000000..ff0e1c4
--- /dev/null
+++ b/source/psp/mp3.c
@@ -0,0 +1,399 @@
+/*
+ * PSP Software Development Kit - http://www.pspdev.org
+ * -----------------------------------------------------------------------
+ * Licensed under the BSD license, see LICENSE in PSPSDK root for details.
+ *
+ * main.c - Basic PRX template
+ *
+ * Copyright (c) 2005 Marcus R. Brown
+ * Copyright (c) 2005 James Forshaw
+ * Copyright (c) 2005 John Kelley
+ * 2012 dr_mabuse1981: well, i just kicked out Crow_bars shitty and buggy mp3 player
+ * and re-added the kurok one again and added bakers mp3 fix ;)
+ *
+ * $Id: main.c 1888 2006-05-01 08:47:04Z tyranid $
+ * $HeadURL$
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include "m33libs/kubridge.h"
+
+int mp3_last_error = 0;
+
+static int initialized = 0;
+static SceUID thread_job_sem = -1;
+static SceUID thread_busy_sem = -1;
+static int thread_exit = 0;
+
+int done = 0;
+
+int mp3_job_started = 0;
+
+int mp3_volume;
+
+static int mp3_src_pos = 0;
+
+int first_run = 0;
+
+// MPEG-1, layer 3
+static int bitrates[] = { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 };
+//static int samplerates[] = { 44100, 48000, 32000, 0 };
+
+#define MIN_INFRAME_SIZE 96
+#define IN_BUFFER_SIZE (2*1024)
+
+static unsigned long mp3_codec_struct[65];// __attribute__((aligned(64)));
+
+static unsigned char mp3_src_buffer[2][IN_BUFFER_SIZE];// __attribute__((aligned(64)));
+static short mp3_mix_buffer[2][1152*2];// __attribute__((aligned(64)));
+static int working_buf = 0;
+
+static const char *mp3_fname = NULL;
+static SceUID mp3_handle = -1;
+static int mp3_src_size = 0;
+
+static int decode_thread(SceSize args, void *argp);
+
+
+static void psp_sem_lock(SceUID sem)
+{
+ int ret = sceKernelWaitSema(sem, 1, 0);
+ if (ret < 0) printf("sceKernelWaitSema(%08x) failed with %08x\n", sem, ret);
+}
+
+static void psp_sem_unlock(SceUID sem)
+{
+ int ret = sceKernelSignalSema(sem, 1);
+ if (ret < 0) printf("sceKernelSignalSema(%08x) failed with %08x\n", sem, ret);
+}
+
+// only accepts MPEG-1, layer3
+static int find_sync_word(unsigned char *data, int len)
+{
+ int i;
+ for (i = 0; i < len-1; i++)
+ {
+ if ( data[i+0] != 0xff) continue;
+ if ((data[i+1] & 0xfe) == 0xfa) return i;
+ i++;
+ }
+ return -1;
+}
+
+// Baker: called only by decode_thread
+static int read_next_frame(int which_buffer)
+{
+ int i, bytes_read, frame_offset;
+ int bitrate, padding, frame_size = 0;
+
+ for (i = 0; i < 32; i++)
+ {
+ bytes_read = sceIoRead(mp3_handle, mp3_src_buffer[which_buffer], sizeof(mp3_src_buffer[which_buffer]));
+ mp3_src_pos += bytes_read;
+ if (bytes_read < MIN_INFRAME_SIZE)
+ {
+ // Baker: end of file hit, restart + re-read
+ mp3_src_pos = 0;
+ sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET);
+ bytes_read = sceIoRead(mp3_handle, mp3_src_buffer[which_buffer], sizeof(mp3_src_buffer[which_buffer]));
+ mp3_src_pos += bytes_read;
+
+ }
+ frame_offset = find_sync_word(mp3_src_buffer[which_buffer], bytes_read);
+ if (frame_offset < 0) {
+ printf("missing syncword, foffs=%i\n", mp3_src_pos - bytes_read);
+ mp3_src_pos--;
+ sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET);
+ continue;
+ }
+ if (bytes_read - frame_offset < 4) {
+ printf("syncword @ EOB, foffs=%i\n", mp3_src_pos - bytes_read);
+ mp3_src_pos--;
+ sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET);
+ continue;
+ }
+
+ bitrate = mp3_src_buffer[which_buffer][frame_offset+2] >> 4;
+ padding = (mp3_src_buffer[which_buffer][frame_offset+2] & 2) >> 1;
+
+ frame_size = 144000*bitrates[bitrate]/44100 + padding;
+ if (frame_size <= 0) {
+ printf("bad frame, foffs=%i\n", mp3_src_pos - bytes_read);
+ continue; // bad frame
+ }
+
+ if (bytes_read - frame_offset < frame_size)
+ {
+ printf("unfit, foffs=%i\n", mp3_src_pos - bytes_read);
+ mp3_src_pos -= bytes_read - frame_offset;
+ if (mp3_src_size - mp3_src_pos < frame_size) {
+ mp3_src_pos = mp3_src_size;
+ return 0; // EOF
+ }
+ sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET);
+ continue; // didn't fit, re-read..
+ }
+
+ if (frame_offset) {
+ //printf("unaligned, foffs=%i, offs=%i\n", mp3_src_pos - bytes_read, frame_offset);
+ memmove(mp3_src_buffer[which_buffer], mp3_src_buffer[which_buffer] + frame_offset, frame_size);
+ }
+
+ // align for next frame read
+ mp3_src_pos -= bytes_read - (frame_offset + frame_size);
+ sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET);
+
+ break;
+ }
+
+ return frame_size > 0 ? frame_size : -1;
+}
+
+
+static SceUID load_start_module(const char *prxname)
+{
+ SceUID mod, mod1;
+ int status, ret;
+
+ mod = pspSdkLoadStartModule(prxname, PSP_MEMORY_PARTITION_KERNEL);
+ if (mod < 0) {
+ printf("failed to load %s (%08x), trying kuKernelLoadModule\n", prxname, mod);
+ mod1 = kuKernelLoadModule(prxname, 0, NULL);
+ if (mod1 < 0) printf("kuKernelLoadModule failed with %08x\n", mod1);
+ else {
+ ret = sceKernelStartModule(mod1, 0, NULL, &status, 0);
+ if (ret < 0) printf("sceKernelStartModule failed with %08x\n", ret);
+ else mod = mod1;
+ }
+ }
+ return mod;
+}
+
+
+int mp3_init(void)
+{
+ SceUID thid, mod;
+ int ret;
+
+ /* load modules */
+ /* <= 1.5 (and probably some other, not sure which) fw need this to for audiocodec to work,
+ * so if it fails, assume we are just on new enough firmware and continue.. */
+ load_start_module("flash0:/kd/me_for_vsh.prx");
+
+ if (sceKernelDevkitVersion() < 0x02070010)
+ mod = load_start_module("flash0:/kd/audiocodec.prx");
+ else mod = load_start_module("flash0:/kd/avcodec.prx");
+ if (mod < 0) {
+ ret = mod = load_start_module("flash0:/kd/audiocodec_260.prx"); // last chance..
+ if (mod < 0) goto fail;
+ }
+
+ /* audiocodec init */
+ memset(mp3_codec_struct, 0, sizeof(mp3_codec_struct));
+ ret = sceAudiocodecCheckNeedMem(mp3_codec_struct, 0x1002);
+ if (ret < 0) {
+ printf("sceAudiocodecCheckNeedMem failed with %08x\n", ret);
+ goto fail;
+ }
+
+ ret = sceAudiocodecGetEDRAM(mp3_codec_struct, 0x1002);
+ if (ret < 0) {
+ printf("sceAudiocodecGetEDRAM failed with %08x\n", ret);
+ goto fail;
+ }
+
+ ret = sceAudiocodecInit(mp3_codec_struct, 0x1002);
+ if (ret < 0) {
+ printf("sceAudiocodecInit failed with %08x\n", ret);
+ goto fail1;
+ }
+
+ /* thread and stuff */
+ thread_job_sem = sceKernelCreateSema("p_mp3job_sem", 0, 0, 1, NULL);
+ if (thread_job_sem < 0) {
+ printf("sceKernelCreateSema() failed: %08x\n", thread_job_sem);
+ ret = thread_job_sem;
+ goto fail1;
+ }
+
+ thread_busy_sem = sceKernelCreateSema("p_mp3busy_sem", 0, 1, 1, NULL);
+ if (thread_busy_sem < 0) {
+ printf("sceKernelCreateSema() failed: %08x\n", thread_busy_sem);
+ ret = thread_busy_sem;
+ goto fail2;
+ }
+
+ thread_exit = 0;
+ thid = sceKernelCreateThread("mp3decode_thread", decode_thread, 30, 0x2000, 0, 0); /* use slightly higher prio then main */
+ if (thid < 0) {
+ printf("failed to create decode thread: %08x\n", thid);
+ ret = thid;
+ goto fail3;
+ }
+ ret = sceKernelStartThread(thid, 0, 0);
+ if (ret < 0) {
+ printf("failed to start decode thread: %08x\n", ret);
+ goto fail3;
+ }
+
+ mp3_last_error = 0;
+ initialized = 1;
+ return 0;
+
+fail3:
+ sceKernelDeleteSema(thread_busy_sem);
+ thread_busy_sem = -1;
+fail2:
+ sceKernelDeleteSema(thread_job_sem);
+ thread_job_sem = -1;
+fail1:
+ sceAudiocodecReleaseEDRAM(mp3_codec_struct);
+fail:
+ mp3_last_error = ret;
+ initialized = 0;
+ return 1;
+}
+
+void mp3_deinit(void)
+{
+ printf("mp3_deinit, initialized=%i\n", initialized);
+
+ if (!initialized) return;
+ thread_exit = 1;
+ psp_sem_lock(thread_busy_sem);
+ psp_sem_unlock(thread_busy_sem);
+
+ sceKernelSignalSema(thread_job_sem, 1);
+ sceKernelDelayThread(100*1000);
+
+ if (mp3_handle >= 0) sceIoClose(mp3_handle);
+ mp3_handle = -1;
+ mp3_fname = NULL;
+
+ psp_sem_lock(thread_job_sem);
+ psp_sem_unlock(thread_job_sem);
+
+ sceKernelDeleteSema(thread_busy_sem);
+ thread_busy_sem = -1;
+ sceKernelDeleteSema(thread_job_sem);
+ thread_job_sem = -1;
+ sceAudiocodecReleaseEDRAM(mp3_codec_struct);
+ initialized = 0;
+
+}
+
+
+short mp3_output_buffer[4][1152 * 2] __attribute__((aligned(64)));
+int mp3_output_index = 0;
+
+// may overflow stack?
+static int decode_thread(SceSize args, void *argp)
+{
+ int ret, frame_size;
+
+ int audio_channel = sceAudioChReserve(1, 1152, PSP_AUDIO_FORMAT_STEREO);
+
+ printf("decode_thread started with id %08x, priority %i\n",
+ sceKernelGetThreadId(), sceKernelGetThreadCurrentPriority());
+
+ while (!thread_exit)
+ {
+ psp_sem_lock(thread_job_sem);
+
+ if (thread_exit) {
+ psp_sem_unlock(thread_job_sem);
+ break;
+ }
+
+ psp_sem_lock(thread_busy_sem);
+
+ frame_size = read_next_frame(working_buf);
+
+ while (mp3_job_started)
+ {
+
+ if (thread_exit) break;
+
+ if(frame_size > 0)
+ {
+ mp3_codec_struct[6] = (unsigned long)mp3_src_buffer[working_buf];
+ mp3_codec_struct[8] = (unsigned long)mp3_mix_buffer[working_buf];
+ mp3_codec_struct[7] = mp3_codec_struct[10] = frame_size;
+ mp3_codec_struct[9] = 1152 * 4;
+
+ ret = sceAudiocodecDecode(mp3_codec_struct, 0x1002);
+ if (ret < 0) printf("sceAudiocodecDecode failed with %08x\n", ret);
+
+ memcpy(mp3_output_buffer[mp3_output_index], mp3_mix_buffer[working_buf], 1152*4);
+ sceAudioOutputBlocking(audio_channel, mp3_volume, mp3_output_buffer[mp3_output_index]);
+ mp3_output_index = (mp3_output_index+1)%4;
+
+ memset(mp3_mix_buffer, 0, 1152*2*2);
+
+ frame_size = read_next_frame(working_buf);
+ }
+
+
+ }
+
+ if (!mp3_job_started) {
+
+ if (mp3_handle >= 0) sceIoClose(mp3_handle);
+ mp3_handle = -1;
+ mp3_fname = NULL;
+ }
+
+ psp_sem_unlock(thread_busy_sem);
+
+
+ }
+
+ printf("leaving decode thread\n");
+ sceKernelExitDeleteThread(0);
+ return 0;
+}
+
+static int mp3_samples_ready = 0, mp3_buffer_offs = 0, mp3_play_bufsel = 0;
+
+int mp3_start_play(char *fname, int pos)
+{
+
+ printf("mp3_start_play(%s) @ %i\n", fname, pos);
+ psp_sem_lock(thread_busy_sem);
+
+ if (mp3_fname != fname || mp3_handle < 0)
+ {
+ if (mp3_handle >= 0) sceIoClose(mp3_handle);
+ mp3_handle = sceIoOpen(fname, PSP_O_RDONLY, 0777);
+ if (mp3_handle < 0) {
+ printf("sceIoOpen(%s) failed\n", fname);
+ psp_sem_unlock(thread_busy_sem);
+ sceIoClose(mp3_handle);
+ return 2;
+ }
+ mp3_src_size = sceIoLseek32(mp3_handle, 0, PSP_SEEK_END);
+ mp3_fname = fname;
+ }
+
+ // seek..
+ mp3_src_pos = (int) (((float)pos / 1023.0f) * (float)mp3_src_size);
+ sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET);
+ printf("seek %i: %i/%i\n", pos, mp3_src_pos, mp3_src_size);
+
+ mp3_job_started = 1;
+ mp3_samples_ready = mp3_buffer_offs = mp3_play_bufsel = 0;
+ working_buf = 0;
+
+ /* send a request to decode first frame */
+ psp_sem_unlock(thread_busy_sem);
+ psp_sem_unlock(thread_job_sem);
+ sceKernelDelayThread(1); // reschedule
+
+ return 0;
+}
+
+
diff --git a/source/psp/mp3.h b/source/psp/mp3.h
new file mode 100644
index 0000000..015e9cc
--- /dev/null
+++ b/source/psp/mp3.h
@@ -0,0 +1,9 @@
+// additional stuff for PSP mp3 decoder implementation
+
+extern "C" int mp3_init(void);
+extern "C" void mp3_deinit(void);
+extern "C" int mp3_start_play(char *fname, int pos);
+
+extern "C" int mp3_job_started;
+
+int mp3_volume;
diff --git a/source/psp/network.cpp b/source/psp/network.cpp
new file mode 100644
index 0000000..530c8fc
--- /dev/null
+++ b/source/psp/network.cpp
@@ -0,0 +1,122 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "network_psp.hpp"
+
+extern "C"
+{
+#include "../net_dgrm.h"
+#include "../net_loop.h"
+}
+
+net_driver_t net_drivers[MAX_NET_DRIVERS] =
+{
+ {
+ "Loopback",
+ qfalse,
+ Loop_Init,
+ Loop_Listen,
+ Loop_SearchForHosts,
+ Loop_Connect,
+ Loop_CheckNewConnections,
+ Loop_GetMessage,
+ Loop_SendMessage,
+ Loop_SendUnreliableMessage,
+ Loop_CanSendMessage,
+ Loop_CanSendUnreliableMessage,
+ Loop_Close,
+ Loop_Shutdown
+ }
+ ,
+ {
+ "Datagram",
+ qfalse,
+ Datagram_Init,
+ Datagram_Listen,
+ Datagram_SearchForHosts,
+ Datagram_Connect,
+ Datagram_CheckNewConnections,
+ Datagram_GetMessage,
+ Datagram_SendMessage,
+ Datagram_SendUnreliableMessage,
+ Datagram_CanSendMessage,
+ Datagram_CanSendUnreliableMessage,
+ Datagram_Close,
+ Datagram_Shutdown
+ }
+};
+
+int net_numdrivers = 2;
+
+using namespace quake;
+using namespace quake::network;
+
+net_landriver_t net_landrivers[MAX_NET_DRIVERS] =
+{
+ {
+ "Infrastructure",
+ qfalse,
+ 0,
+ infrastructure::init,
+ infrastructure::shut_down,
+ infrastructure::listen,
+ infrastructure::open_socket,
+ infrastructure::close_socket,
+ infrastructure::connect,
+ infrastructure::check_new_connections,
+ infrastructure::read,
+ infrastructure::write,
+ infrastructure::broadcast,
+ infrastructure::addr_to_string,
+ infrastructure::string_to_addr,
+ infrastructure::get_socket_addr,
+ infrastructure::get_name_from_addr,
+ infrastructure::get_addr_from_name,
+ infrastructure::addr_compare,
+ infrastructure::get_socket_port,
+ infrastructure::set_socket_port
+ },
+ {
+ "Adhoc",
+ qfalse,
+ 0,
+ adhoc::init,
+ adhoc::shut_down,
+ adhoc::listen,
+ adhoc::open_socket,
+ adhoc::close_socket,
+ adhoc::connect,
+ adhoc::check_new_connections,
+ adhoc::read,
+ adhoc::write,
+ adhoc::broadcast,
+ adhoc::addr_to_string,
+ adhoc::string_to_addr,
+ adhoc::get_socket_addr,
+ adhoc::get_name_from_addr,
+ adhoc::get_addr_from_name,
+ adhoc::addr_compare,
+ adhoc::get_socket_port,
+ adhoc::set_socket_port
+ }
+};
+
+int net_numlandrivers = 2;
diff --git a/source/psp/network_infrastructure.cpp b/source/psp/network_infrastructure.cpp
new file mode 100644
index 0000000..c971c89
--- /dev/null
+++ b/source/psp/network_infrastructure.cpp
@@ -0,0 +1,555 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+//#include
+#include
+#include
+#include
+#include
+
+#include "network_infrastructure.hpp"
+
+#include // inet_addr
+#include // sockaddr_in
+#include // PSP socket API.
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+#define MAXHOSTNAMELEN 64
+#define EWOULDBLOCK 111
+#define ECONNREFUSED 11
+
+int totalAccessPoints = 0;
+cvar_t accesspoint = {"accesspoint", "1", qtrue};
+int accessPointNumber[100];
+
+#ifdef PROFILE
+#define sceUtilityLoadNetModule
+#define sceUtilityUnLoadNetModule
+#endif
+
+namespace quake
+{
+ namespace network
+ {
+ namespace infrastructure
+ {
+ static int accept_socket = -1; // socket for fielding new connections
+ static int control_socket = -1;
+ static int broadcast_socket = 0;
+ static struct qsockaddr broadcast_addr;
+ static int my_addr = 0;
+
+ int init (void)
+ {
+ if(totalAccessPoints == 0)
+ {
+ int iNetIndex;
+ memset(accessPointNumber, 0, sizeof(accessPointNumber));
+ for (iNetIndex = 1; iNetIndex < 100; iNetIndex++) // skip the 0th connection
+ {
+ if (sceUtilityCheckNetParam(iNetIndex) == 0)
+ {
+ totalAccessPoints++;
+ accessPointNumber[totalAccessPoints] = iNetIndex;
+ }
+ }
+ if(accesspoint.value > totalAccessPoints)
+ Cvar_SetValue("accesspoint", 1);
+ }
+
+ if(!host_initialized)
+ {
+ Cvar_RegisterVariable(&accesspoint);
+ }
+
+ if(!tcpipAvailable)
+ return -1;
+
+ char szMyIPAddr[32];
+ struct qsockaddr addr;
+ char *colon;
+
+ char buff[MAXHOSTNAMELEN];
+
+ S_ClearBuffer (); // so dma doesn't loop current sound
+
+ // Load the network modules when they are required
+ sceUtilityLoadNetModule(PSP_NET_MODULE_COMMON);
+ sceUtilityLoadNetModule(PSP_NET_MODULE_INET);
+
+ // Initialise the network.
+ const int err = pspSdkInetInit();
+ if (err)
+ {
+ Con_Printf("Couldn't initialise the network %08X\n", err);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+ return -1;
+ }
+
+ if(!connect_to_apctl(accessPointNumber[(int)accesspoint.value]))
+ {
+ Con_Printf("Unable to connect to access point\n");
+ pspSdkInetTerm();
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+
+ return -1;
+ }
+
+ // connected, get my IPADDR and run test
+ if (sceNetApctlGetInfo(8, (SceNetApctlInfo*)szMyIPAddr) != 0)
+ strcpy(szMyIPAddr, "unknown IP address");
+
+ // determine my name & address
+ gethostname(buff, MAXHOSTNAMELEN);
+
+ my_addr = inet_addr(szMyIPAddr);
+
+ // if the quake hostname isn't set, set it to the machine name
+ if (Q_strcmp(hostname.string, "UNNAMED") == 0)
+ {
+ buff[15] = 0;
+ Cvar_Set ("hostname", buff);
+ }
+
+ if ((control_socket = open_socket(0)) == -1)
+ {
+ pspSdkInetTerm();
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+ Sys_Error("init: Unable to open control socket\n");
+ }
+
+ ((struct sockaddr_in *)&broadcast_addr)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&broadcast_addr)->sin_addr.s_addr = INADDR_BROADCAST;
+ ((struct sockaddr_in *)&broadcast_addr)->sin_port = htons(net_hostport);
+
+ get_socket_addr (control_socket, &addr);
+ Q_strcpy(my_tcpip_address, addr_to_string(&addr));
+ colon = Q_strrchr (my_tcpip_address, ':');
+ if (colon)
+ *colon = 0;
+
+ Con_Printf("UDP Initialized\n");
+ tcpipAvailable = qtrue;
+
+ return control_socket;
+ }
+
+ //=============================================================================
+
+ void shut_down (void)
+ {
+ listen(qfalse);
+ close_socket(control_socket);
+
+ pspSdkInetTerm();
+
+ // Now to unload the network modules, no need to keep them loaded all the time
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+ }
+
+ //=============================================================================
+
+ void listen (qboolean state)
+ {
+ // enable listening
+ if (state)
+ {
+ if (accept_socket != -1)
+ return;
+ if ((accept_socket = open_socket(net_hostport)) == -1)
+ Sys_Error ("listen: Unable to open accept socket\n");
+ return;
+ }
+
+ // disable listening
+ if (accept_socket == -1)
+ return;
+ close_socket(accept_socket);
+
+ accept_socket = -1;
+ }
+
+ //=============================================================================
+
+ int open_socket (int port)
+ {
+ int newsocket;
+ struct sockaddr_in address;
+
+ if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+ return -1;
+
+ int val = 1;
+ if(setsockopt(newsocket, SOL_SOCKET, SO_NONBLOCK, &val, sizeof(val)) < 0)
+ goto ErrorReturn;
+
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+ address.sin_port = htons(port);
+ if( bind (newsocket, (sockaddr *)&address, sizeof(address)) == -1)
+ goto ErrorReturn;
+
+ printf("got here4\n");
+ return newsocket;
+
+ ErrorReturn:
+ close(newsocket);
+ return -1;
+ }
+
+ //=============================================================================
+
+ int close_socket (int socket)
+ {
+ if (socket == broadcast_socket)
+ broadcast_socket = 0;
+ return close(socket);
+ }
+
+ int connect (int socket, struct qsockaddr *addr)
+ {
+ return 0;
+ }
+
+ //=============================================================================
+
+ int check_new_connections (void)
+ {
+ byte buf[1000];
+
+ if (accept_socket == -1)
+ return -1;
+
+ // Peek at the message and if there is a message waiting then return the
+ // socket
+ int ret = recvfrom(accept_socket, buf, 1000, MSG_PEEK, NULL, NULL);
+
+ if(ret > 0)
+ return accept_socket;
+
+ return -1;
+ }
+
+ //=============================================================================
+
+ int read (int socket, byte *buf, int len, struct qsockaddr *addr)
+ {
+ int addrlen = sizeof (struct qsockaddr);
+ int ret;
+
+ ret = recvfrom (socket, buf, len, 0, (struct sockaddr *)addr, (socklen_t*)&addrlen);
+ if (ret == -1)
+ {
+ int errno = sceNetInetGetErrno();
+ if(errno == EWOULDBLOCK || errno == ECONNREFUSED)
+ {
+ return 0;
+ }
+ }
+
+ return ret;
+ }
+
+ //=============================================================================
+
+ static int make_socket_broadcast_capable (int socket)
+ {
+ int i = 1;
+
+ // make this socket broadcast capable
+ if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i)) < 0)
+ return -1;
+ broadcast_socket = socket;
+
+ return 0;
+ }
+
+ //=============================================================================
+
+ int broadcast (int socket, byte *buf, int len)
+ {
+ int ret;
+
+ if (socket != broadcast_socket)
+ {
+ if (broadcast_socket != 0)
+ Sys_Error("Attempted to use multiple broadcasts sockets\n");
+ ret = make_socket_broadcast_capable (socket);
+ if (ret == -1)
+ {
+ Con_Printf("Unable to make socket broadcast capable\n");
+ return ret;
+ }
+ }
+
+ return write(socket, buf, len, &broadcast_addr);
+ }
+
+ //=============================================================================
+
+ int write (int socket, byte *buf, int len, struct qsockaddr *addr)
+ {
+ int ret;
+
+ ret = sendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr));
+ if (ret == -1)
+ {
+ int errno = sceNetInetGetErrno();
+ if(errno == EWOULDBLOCK || errno == ECONNREFUSED)
+ {
+ return 0;
+ }
+ Con_Printf("Failed to send message, errno=%08X\n", errno);
+ }
+ return ret;
+ }
+
+ //=============================================================================
+
+ char* addr_to_string (struct qsockaddr *addr)
+ {
+ static char buffer[22];
+ int haddr;
+
+ haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
+ sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port));
+ return buffer;
+ }
+
+ //=============================================================================
+
+ int string_to_addr (char *string, struct qsockaddr *addr)
+ {
+ int ha1, ha2, ha3, ha4, hp;
+ int ipaddr;
+
+ sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp);
+ ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4;
+
+ addr->sa_family = AF_INET;
+ ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr);
+ ((struct sockaddr_in *)addr)->sin_port = htons(hp);
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_socket_addr (int socket, struct qsockaddr *addr)
+ {
+ socklen_t addrlen = sizeof(struct qsockaddr);
+ unsigned int a;
+
+ Q_memset(addr, 0, sizeof(struct qsockaddr));
+ getsockname(socket, (struct sockaddr *)addr, &addrlen);
+ a = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
+ if (a == 0 || a == inet_addr("127.0.0.1"))
+ ((struct sockaddr_in *)addr)->sin_addr.s_addr = my_addr;
+
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_name_from_addr (struct qsockaddr *addr, char *name)
+ {
+ struct hostent *hostentry;
+
+ hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET);
+ if (hostentry)
+ {
+ Q_strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1);
+ return 0;
+ }
+
+ Q_strcpy (name, addr_to_string (addr));
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_addr_from_name(char *name, struct qsockaddr *addr)
+ {
+ struct hostent *hostentry;
+
+ if (name[0] >= '0' && name[0] <= '9')
+ return PartialIPAddress (name, addr);
+
+ hostentry = gethostbyname (name);
+ if (!hostentry)
+ return -1;
+
+ addr->sa_family = AF_INET;
+ ((struct sockaddr_in *)addr)->sin_port = htons(net_hostport);
+ ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0];
+
+ return 0;
+ }
+
+ //=============================================================================
+
+ int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2)
+ {
+ if (addr1->sa_family != addr2->sa_family)
+ return -1;
+
+ if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr)
+ return -1;
+
+ if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port)
+ return 1;
+
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_socket_port (struct qsockaddr *addr)
+ {
+ return ntohs(((struct sockaddr_in *)addr)->sin_port);
+ }
+
+
+ int set_socket_port (struct qsockaddr *addr, int port)
+ {
+ ((struct sockaddr_in *)addr)->sin_port = htons(port);
+ return 0;
+ }
+
+ /*
+ ============
+ PartialIPAddress
+
+ this lets you type only as much of the net address as required, using
+ the local network components to fill in the rest
+ ============
+ */
+ int PartialIPAddress (char *in, struct qsockaddr *hostaddr)
+ {
+ char buff[256];
+ char *b;
+ int addr;
+ int num;
+ int mask;
+ int run;
+ int port;
+
+ buff[0] = '.';
+ b = buff;
+ strcpy(buff+1, in);
+ if (buff[1] == '.')
+ b++;
+
+ addr = 0;
+ mask=-1;
+ while (*b == '.')
+ {
+ b++;
+ num = 0;
+ run = 0;
+ while (!( *b < '0' || *b > '9'))
+ {
+ num = num*10 + *b++ - '0';
+ if (++run > 3)
+ return -1;
+ }
+ if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0)
+ return -1;
+ if (num < 0 || num > 255)
+ return -1;
+ mask<<=8;
+ addr = (addr<<8) + num;
+ }
+
+ if (*b++ == ':')
+ port = Q_atoi(b);
+ else
+ port = net_hostport;
+
+ hostaddr->sa_family = AF_INET;
+ ((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port);
+ ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (my_addr & htonl(mask)) | htonl(addr);
+
+ return 0;
+ }
+ //=============================================================================
+ /* Connect to an access point */
+ int connect_to_apctl(int config)
+ {
+ int err;
+ int stateLast = -1;
+ int timeout = 0;
+
+ /* Connect using the first profile */
+ err = sceNetApctlConnect(config);
+ if (err != 0)
+ {
+ return 0;
+ }
+
+ while (1)
+ {
+ int state;
+ err = sceNetApctlGetState(&state);
+ if (err != 0)
+ {
+ break;
+ }
+ if (state > stateLast)
+ {
+ stateLast = state;
+ timeout = 0;
+ }
+ if (state == 4)
+ break; // connected with static IP
+
+ // wait a little before polling again
+ sceKernelDelayThread(50*1000); // 50ms
+
+ timeout++;
+ if(timeout > 200)
+ {
+ Con_Printf("Timeout connecting to access point. State=%d\n", state);
+ return 0;
+ }
+ }
+
+ if(err != 0)
+ {
+ return 0;
+ }
+
+ return 1;
+ }
+ //=============================================================================
+ }
+ }
+}
diff --git a/source/psp/network_infrastructure.hpp b/source/psp/network_infrastructure.hpp
new file mode 100644
index 0000000..1d70c55
--- /dev/null
+++ b/source/psp/network_infrastructure.hpp
@@ -0,0 +1,61 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef QUAKE_NETWORK_INFRASTRUCTURE_HPP
+#define QUAKE_NETWORK_INFRASTRUCTURE_HPP
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+
+namespace quake
+{
+ namespace network
+ {
+ namespace infrastructure
+ {
+ int init (void);
+ void shut_down (void);
+ void listen (qboolean state);
+ int open_socket (int port);
+ int close_socket (int socket);
+ int connect (int socket, struct qsockaddr *addr);
+ int check_new_connections (void);
+ int read (int socket, byte *buf, int len, struct qsockaddr *addr);
+ int write (int socket, byte *buf, int len, struct qsockaddr *addr);
+ int broadcast (int socket, byte *buf, int len);
+ char* addr_to_string (struct qsockaddr *addr);
+ int string_to_addr (char *string, struct qsockaddr *addr);
+ int get_socket_addr (int socket, struct qsockaddr *addr);
+ int get_name_from_addr (struct qsockaddr *addr, char *name);
+ int get_addr_from_name (char *name, struct qsockaddr *addr);
+ int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2);
+ int get_socket_port (struct qsockaddr *addr);
+ int set_socket_port (struct qsockaddr *addr, int port);
+
+ int connect_to_apctl(int config);
+ int PartialIPAddress (char *in, struct qsockaddr *hostaddr);
+ }
+ }
+}
+
+#endif
diff --git a/source/psp/network_psp.cpp b/source/psp/network_psp.cpp
new file mode 100644
index 0000000..0e1b5d8
--- /dev/null
+++ b/source/psp/network_psp.cpp
@@ -0,0 +1,946 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+//#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "network_psp.hpp"
+
+#include // inet_addr
+#include // sockaddr_in
+#include // PSP socket API.
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+#define ntohs(x) __builtin_bswap16(x)
+#define ntohl(x) __builtin_bswap32(x)
+#define htons(x) __builtin_bswap16(x)
+#define htonl(x) __builtin_bswap32(x)
+
+#define ADHOC_NET 29
+#define ADHOC_EWOULDBLOCK 0x80410709
+
+#define MAXHOSTNAMELEN 64
+#define EWOULDBLOCK 111
+#define ECONNREFUSED 11
+
+int totalAccessPoints = 0;
+cvar_t accesspoint = {"accesspoint", "1", qtrue};
+int accessPointNumber[100];
+
+
+typedef struct sockaddr_adhoc
+{
+ char len;
+ short family;
+ u16 port;
+ char mac[6];
+ char zero[6];
+} sockaddr_adhoc;
+
+#ifdef PROFILE
+#define sceUtilityLoadNetModule
+#define sceUtilityUnLoadNetModule
+#endif
+
+static pdpStatStruct gPdpStat;
+pdpStatStruct *findPdpStat(int socket, pdpStatStruct *pdpStat)
+{
+ if(socket == pdpStat->pdpId) {
+ memcpy(&gPdpStat, pdpStat, sizeof(pdpStatStruct));
+ return &gPdpStat;
+ }
+ if(pdpStat->next) return findPdpStat(socket, pdpStat->next);
+ return (pdpStatStruct *)-1;
+}
+
+
+namespace quake
+{
+ namespace network
+ {
+ namespace infrastructure
+ {
+ static int accept_socket = -1; // socket for fielding new connections
+ static int control_socket = -1;
+ static int broadcast_socket = 0;
+ static struct qsockaddr broadcast_addr;
+ static int my_addr = 0;
+
+ int init (void)
+ {
+ if(totalAccessPoints == 0)
+ {
+ int iNetIndex;
+ memset(accessPointNumber, 0, sizeof(accessPointNumber));
+ for (iNetIndex = 1; iNetIndex < 100; iNetIndex++) // skip the 0th connection
+ {
+ if (sceUtilityCheckNetParam(iNetIndex) == 0)
+ {
+ totalAccessPoints++;
+ accessPointNumber[totalAccessPoints] = iNetIndex;
+ }
+ }
+ if(accesspoint.value > totalAccessPoints)
+ Cvar_SetValue("accesspoint", 1);
+ }
+
+ if(!host_initialized)
+ {
+ Cvar_RegisterVariable(&accesspoint);
+ }
+
+ if(!tcpipAvailable)
+ return -1;
+
+ char szMyIPAddr[32];
+ struct qsockaddr addr;
+ char *colon;
+
+ char buff[MAXHOSTNAMELEN];
+
+ S_ClearBuffer (); // so dma doesn't loop current sound
+
+ // Load the network modules when they are required
+ const int err1 = sceUtilityLoadNetModule(PSP_NET_MODULE_COMMON);
+ if(err1 < 0)
+ {
+ Con_Printf("Couldn't load MODULE_COMMON: %08x\n",err1);
+ }
+ const int err2 = sceUtilityLoadNetModule(PSP_NET_MODULE_INET);
+ if(err2 < 0)
+ {
+ Con_Printf("Couldn't load MODULE_INET: %09x\n ",err2);
+ }
+
+ // Initialise the network.
+ const int err = pspSdkInetInit();
+ if (err)
+ {
+ Con_Printf("Couldn't initialize the network %08X\n", err);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+ return -1;
+ }
+
+ if(!connect_to_apctl(accessPointNumber[(int)accesspoint.value]))
+ {
+ Con_Printf("Unable to connect to access point\n");
+ pspSdkInetTerm();
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+
+ return -1;
+ }
+
+ // connected, get my IPADDR and run test
+ if (sceNetApctlGetInfo(8, (SceNetApctlInfo*)szMyIPAddr) != 0)
+ strcpy(szMyIPAddr, "unknown IP address");
+
+ // determine my name & address
+ gethostname(buff, MAXHOSTNAMELEN);
+
+ my_addr = inet_addr(szMyIPAddr);
+
+ // if the quake hostname isn't set, set it to the machine name
+ if (Q_strcmp(hostname.string, "UNNAMED") == 0)
+ {
+ buff[15] = 0;
+ Cvar_Set ("hostname", buff);
+ }
+
+ if ((control_socket = open_socket(0)) == -1)
+ {
+ pspSdkInetTerm();
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+ Sys_Error("init: Unable to open control socket\n");
+ }
+
+ ((struct sockaddr_in *)&broadcast_addr)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&broadcast_addr)->sin_addr.s_addr = INADDR_BROADCAST;
+ ((struct sockaddr_in *)&broadcast_addr)->sin_port = htons(net_hostport);
+
+ get_socket_addr (control_socket, &addr);
+ Q_strcpy(my_tcpip_address, addr_to_string(&addr));
+ colon = Q_strrchr (my_tcpip_address, ':');
+ if (colon)
+ *colon = 0;
+
+ Con_Printf("UDP Initialized\n");
+ tcpipAvailable = qtrue;
+
+ return control_socket;
+ }
+
+ //=============================================================================
+
+ void shut_down (void)
+ {
+ listen(qfalse);
+ close_socket(control_socket);
+
+ pspSdkInetTerm();
+
+ // Now to unload the network modules, no need to keep them loaded all the time
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+ }
+
+ //=============================================================================
+
+ void listen (qboolean state)
+ {
+ // enable listening
+ if (state)
+ {
+ if (accept_socket != -1)
+ return;
+ if ((accept_socket = open_socket(net_hostport)) == -1)
+ Sys_Error ("listen: Unable to open accept socket\n");
+ return;
+ }
+
+ // disable listening
+ if (accept_socket == -1)
+ return;
+ close_socket(accept_socket);
+
+ accept_socket = -1;
+ }
+
+ //=============================================================================
+
+ int open_socket (int port)
+ {
+ int newsocket;
+ struct sockaddr_in address;
+
+ if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+ return -1;
+
+ int val = 1;
+ if(setsockopt(newsocket, SOL_SOCKET, SO_NONBLOCK, &val, sizeof(val)) < 0)
+ goto ErrorReturn;
+
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+ address.sin_port = htons(port);
+ if( bind (newsocket, (sockaddr *)&address, sizeof(address)) == -1)
+ goto ErrorReturn;
+
+ printf("got here4\n");
+ return newsocket;
+
+ ErrorReturn:
+ close(newsocket);
+ return -1;
+ }
+
+ //=============================================================================
+
+ int close_socket (int socket)
+ {
+ if (socket == broadcast_socket)
+ broadcast_socket = 0;
+ return close(socket);
+ }
+
+ int connect (int socket, struct qsockaddr *addr)
+ {
+ return 0;
+ }
+
+ //=============================================================================
+
+ int check_new_connections (void)
+ {
+ byte buf[1000];
+
+ if (accept_socket == -1)
+ return -1;
+
+ // Peek at the message and if there is a message waiting then return the
+ // socket
+ int ret = recvfrom(accept_socket, buf, 1000, MSG_PEEK, NULL, NULL);
+
+ if(ret > 0)
+ return accept_socket;
+
+ return -1;
+ }
+
+ //=============================================================================
+
+ int read (int socket, byte *buf, int len, struct qsockaddr *addr)
+ {
+ int addrlen = sizeof (struct qsockaddr);
+ int ret;
+
+ ret = recvfrom (socket, buf, len, 0, (struct sockaddr *)addr, (socklen_t*)&addrlen);
+ if (ret == -1)
+ {
+ int errno = sceNetInetGetErrno();
+ if(errno == EWOULDBLOCK || errno == ECONNREFUSED)
+ {
+ return 0;
+ }
+ }
+
+ return ret;
+ }
+
+ //=============================================================================
+
+ static int make_socket_broadcast_capable (int socket)
+ {
+ int i = 1;
+
+ // make this socket broadcast capable
+ if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i)) < 0)
+ return -1;
+ broadcast_socket = socket;
+
+ return 0;
+ }
+
+ //=============================================================================
+
+ int broadcast (int socket, byte *buf, int len)
+ {
+ int ret;
+
+ if (socket != broadcast_socket)
+ {
+ if (broadcast_socket != 0)
+ Sys_Error("Attempted to use multiple broadcasts sockets\n");
+ ret = make_socket_broadcast_capable (socket);
+ if (ret == -1)
+ {
+ Con_Printf("Unable to make socket broadcast capable\n");
+ return ret;
+ }
+ }
+
+ return write(socket, buf, len, &broadcast_addr);
+ }
+
+ //=============================================================================
+
+ int write (int socket, byte *buf, int len, struct qsockaddr *addr)
+ {
+ int ret;
+
+ ret = sendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr));
+ //ret = sceNetInetSendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr));
+ if (ret == -1)
+ {
+ int errno = sceNetInetGetErrno();
+ if(errno == EWOULDBLOCK || errno == ECONNREFUSED)
+ {
+ return 0;
+ }
+ Con_Printf("Failed to send message, errno=%08X\n", errno);
+ }
+ return ret;
+ }
+
+ //=============================================================================
+
+ char* addr_to_string (struct qsockaddr *addr)
+ {
+ static char buffer[22];
+ int haddr;
+
+ haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
+ sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port));
+ return buffer;
+ }
+
+ //=============================================================================
+
+ int string_to_addr (char *string, struct qsockaddr *addr)
+ {
+ int ha1, ha2, ha3, ha4, hp;
+ int ipaddr;
+
+ sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp);
+ ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4;
+
+ addr->sa_family = AF_INET;
+ ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr);
+ ((struct sockaddr_in *)addr)->sin_port = htons(hp);
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_socket_addr (int socket, struct qsockaddr *addr)
+ {
+ socklen_t addrlen = sizeof(struct qsockaddr);
+ unsigned int a;
+
+ Q_memset(addr, 0, sizeof(struct qsockaddr));
+ getsockname(socket, (struct sockaddr *)addr, &addrlen);
+ a = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
+ if (a == 0 || a == inet_addr("127.0.0.1"))
+ ((struct sockaddr_in *)addr)->sin_addr.s_addr = my_addr;
+
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_name_from_addr (struct qsockaddr *addr, char *name)
+ {
+ struct hostent *hostentry;
+
+ hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET);
+ if (hostentry)
+ {
+ Q_strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1);
+ return 0;
+ }
+
+ Q_strcpy (name, addr_to_string (addr));
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_addr_from_name(char *name, struct qsockaddr *addr)
+ {
+ struct hostent *hostentry;
+
+ if (name[0] >= '0' && name[0] <= '9')
+ return PartialIPAddress (name, addr);
+
+ hostentry = gethostbyname (name);
+ if (!hostentry)
+ return -1;
+
+ addr->sa_family = AF_INET;
+ ((struct sockaddr_in *)addr)->sin_port = htons(net_hostport);
+ ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0];
+
+ return 0;
+ }
+
+ //=============================================================================
+
+ int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2)
+ {
+ if (addr1->sa_family != addr2->sa_family)
+ return -1;
+
+ if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr)
+ return -1;
+
+ if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port)
+ return 1;
+
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_socket_port (struct qsockaddr *addr)
+ {
+ return ntohs(((struct sockaddr_in *)addr)->sin_port);
+ }
+
+
+ int set_socket_port (struct qsockaddr *addr, int port)
+ {
+ ((struct sockaddr_in *)addr)->sin_port = htons(port);
+ return 0;
+ }
+
+ /*
+ ============
+ PartialIPAddress
+
+ this lets you type only as much of the net address as required, using
+ the local network components to fill in the rest
+ ============
+ */
+ int PartialIPAddress (char *in, struct qsockaddr *hostaddr)
+ {
+ char buff[256];
+ char *b;
+ int addr;
+ int num;
+ int mask;
+ int run;
+ int port;
+
+ buff[0] = '.';
+ b = buff;
+ strcpy(buff+1, in);
+ if (buff[1] == '.')
+ b++;
+
+ addr = 0;
+ mask=-1;
+ while (*b == '.')
+ {
+ b++;
+ num = 0;
+ run = 0;
+ while (!( *b < '0' || *b > '9'))
+ {
+ num = num*10 + *b++ - '0';
+ if (++run > 3)
+ return -1;
+ }
+ if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0)
+ return -1;
+ if (num < 0 || num > 255)
+ return -1;
+ mask<<=8;
+ addr = (addr<<8) + num;
+ }
+
+ if (*b++ == ':')
+ port = Q_atoi(b);
+ else
+ port = net_hostport;
+
+ hostaddr->sa_family = AF_INET;
+ ((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port);
+ ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (my_addr & htonl(mask)) | htonl(addr);
+
+ return 0;
+ }
+ //=============================================================================
+ /* Connect to an access point */
+ int connect_to_apctl(int config)
+ {
+ int err;
+ int stateLast = -1;
+ int timeout = 0;
+
+ /* Connect using the first profile */
+ err = sceNetApctlConnect(config);
+ if (err != 0)
+ {
+ return 0;
+ }
+
+ while (1)
+ {
+ int state;
+ err = sceNetApctlGetState(&state);
+ if (err != 0)
+ {
+ break;
+ }
+ if (state > stateLast)
+ {
+ stateLast = state;
+ timeout = 0;
+ }
+ if (state == 4)
+ break; // connected with static IP
+
+ // wait a little before polling again
+ sceKernelDelayThread(50*1000); // 50ms
+
+ timeout++;
+ if(timeout > 200)
+ {
+ Con_Printf("Timeout connecting to access point. State=%d\n", state);
+ return 0;
+ }
+ }
+
+ if(err != 0)
+ {
+ return 0;
+ }
+
+ return 1;
+ }
+ //=============================================================================
+ }
+ namespace adhoc
+ {
+ static int accept_socket = -1; // socket for fielding new connections
+ static int control_socket = -1;
+ static int broadcast_socket = 0;
+ static struct qsockaddr broadcast_addr;
+
+ int init (void)
+ {
+ if(!tcpipAvailable) return -1;
+
+ struct qsockaddr addr;
+ char *colon;
+ char buff[MAXHOSTNAMELEN];
+ int i,rc;
+
+ S_ClearBuffer ();
+
+ sceUtilityLoadNetModule(PSP_NET_MODULE_COMMON);
+ sceUtilityLoadNetModule(PSP_NET_MODULE_ADHOC);
+
+ int stateLast = -1;
+ rc = pspSdkAdhocInit("ULUS00443");
+ if(rc < 0) {
+ Con_Printf("Couldn't initialise the network %08X\n", rc);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_ADHOC);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+ return -1;
+ }
+
+ rc = sceNetAdhocctlConnect((const char *)"quake");
+ if(rc < 0) {
+ Con_Printf("Couldn't initialise the network %08X\n", rc);
+ pspSdkAdhocTerm();
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_ADHOC);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+ return -1;
+ }
+
+ while (1) {
+ int state;
+ rc = sceNetAdhocctlGetState(&state);
+ if (rc != 0) {
+ Con_Printf("Couldn't initialise the network %08X\n", rc);
+ pspSdkAdhocTerm();
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_ADHOC);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+ return -1;
+ }
+ if (state > stateLast) {
+ stateLast = state;
+ }
+ if (state == 1) {
+ break;
+ }
+ sceKernelDelayThread(50*1000); // 50ms
+ }
+
+ gethostname(buff, MAXHOSTNAMELEN);
+ if (Q_strcmp(hostname.string, "UNNAMED") == 0)
+ {
+ buff[15] = 0;
+ Cvar_Set ("hostname", buff);
+ }
+
+ if ((control_socket = open_socket(0)) == -1) {
+ pspSdkAdhocTerm();
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_ADHOC);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+ Sys_Error("init: Unable to open control socket\n");
+ }
+
+ ((sockaddr_adhoc *)&broadcast_addr)->family = ADHOC_NET;
+ for(i=0; i<6; i++) ((sockaddr_adhoc *)&broadcast_addr)->mac[i] = 0xFF;
+ ((sockaddr_adhoc *)&broadcast_addr)->port = net_hostport;
+
+ get_socket_addr (control_socket, &addr);
+ Q_strcpy(my_tcpip_address, addr_to_string(&addr));
+ colon = Q_strrchr(my_tcpip_address, ':');
+ if (colon) *colon = 0;
+
+ listen(qtrue);
+
+ Con_Printf("AdHoc Initialized\n");
+ tcpipAvailable = qtrue;
+ tcpipAdhoc = qtrue;
+ return control_socket;
+ }
+
+ //=============================================================================
+
+ void shut_down (void)
+ {
+ listen(qfalse);
+ close_socket(control_socket);
+ pspSdkAdhocTerm();
+
+ // Now to unload the network modules, no need to keep them loaded all the time
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_ADHOC);
+ sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
+ }
+
+ //=============================================================================
+
+ void listen (qboolean state)
+ {
+ // enable listening
+ if (state)
+ {
+ if (accept_socket != -1)
+ return;
+ if ((accept_socket = open_socket(net_hostport)) == -1)
+ Sys_Error ("listen: Unable to open accept socket\n");
+ return;
+ }
+
+ // disable listening
+ if (accept_socket == -1)
+ return;
+ close_socket(accept_socket);
+ accept_socket = -1;
+ }
+
+ //=============================================================================
+
+ int open_socket (int port)
+ {
+ u8 mac[8];
+ sceWlanGetEtherAddr(mac);
+ int rc = sceNetAdhocPdpCreate(mac, port, 0x2000, 0);
+ if(rc < 0) return -1;
+ return rc;
+ }
+
+ //=============================================================================
+
+ int close_socket (int socket)
+ {
+ if (socket == broadcast_socket) broadcast_socket = 0;
+ return sceNetAdhocPdpDelete(socket, 0);
+ }
+
+ int connect (int socket, struct qsockaddr *addr)
+ {
+ return 0;
+ }
+
+ //=============================================================================
+
+ int check_new_connections (void)
+ {
+ pdpStatStruct pdpStat[20];
+ int length = sizeof(pdpStatStruct) * 20;
+
+ if (accept_socket == -1)
+ return -1;
+
+ int err = sceNetAdhocGetPdpStat(&length, pdpStat);
+ if(err < 0) return -1;
+
+ pdpStatStruct *tempPdp = findPdpStat(accept_socket, pdpStat);
+
+ if(tempPdp < 0) return -1;
+
+ if(tempPdp->rcvdData > 0) return accept_socket;
+
+ return -1;
+ }
+
+ //=============================================================================
+
+ int read (int socket, byte *buf, int len, struct qsockaddr *addr)
+ {
+ unsigned short port;
+ int datalength = len;
+ unsigned int ret;
+
+ sceKernelDelayThread(1);
+
+ ret = sceNetAdhocPdpRecv(socket, (unsigned char *)((sockaddr_adhoc *)addr)->mac, &port, buf, &datalength, 0, 1);
+ if(ret == ADHOC_EWOULDBLOCK) return 0;
+
+ ((sockaddr_adhoc *)addr)->port = port;
+
+ return datalength;
+ }
+
+ //=============================================================================
+
+ static int make_socket_broadcast_capable (int socket)
+ {
+ broadcast_socket = socket;
+ return 0;
+ }
+
+ //=============================================================================
+
+ int broadcast (int socket, byte *buf, int len)
+ {
+ int ret;
+
+ if (socket != broadcast_socket)
+ {
+ if (broadcast_socket != 0)
+ Sys_Error("Attempted to use multiple broadcasts sockets\n");
+ ret = make_socket_broadcast_capable (socket);
+ if (ret == -1)
+ {
+ Con_Printf("Unable to make socket broadcast capable\n");
+ return ret;
+ }
+ }
+
+ return write(socket, buf, len, &broadcast_addr);
+ }
+
+ //=============================================================================
+
+ int write (int socket, byte *buf, int len, struct qsockaddr *addr)
+ {
+ int ret = -1;
+
+ ret = sceNetAdhocPdpSend(socket, (unsigned char*)((sockaddr_adhoc *)addr)->mac, ((sockaddr_adhoc *)addr)->port, buf, len, 0, 1);
+
+ //if(ret < 0) Con_Printf("Failed to send message, errno=%08X\n", ret);//blubs
+ return ret;
+ }
+
+ //=============================================================================
+
+ char* addr_to_string (struct qsockaddr *addr)
+ {
+ static char buffer[22];
+
+ sceNetEtherNtostr((unsigned char *)((sockaddr_adhoc *)addr)->mac, buffer);
+ sprintf(buffer + strlen(buffer), ":%d", ((sockaddr_adhoc *)addr)->port);
+ return buffer;
+ }
+
+ //=============================================================================
+
+ int string_to_addr (char *string, struct qsockaddr *addr)
+ {
+ int ha1, ha2, ha3, ha4, ha5, ha6, hp;
+
+ sscanf(string, "%x:%x:%x:%x:%x:%x:%d", &ha1, &ha2, &ha3, &ha4, &ha5, &ha6, &hp);
+ addr->sa_family = ADHOC_NET;
+ ((struct sockaddr_adhoc *)addr)->mac[0] = ha1 & 0xFF;
+ ((struct sockaddr_adhoc *)addr)->mac[1] = ha2 & 0xFF;
+ ((struct sockaddr_adhoc *)addr)->mac[2] = ha3 & 0xFF;
+ ((struct sockaddr_adhoc *)addr)->mac[3] = ha4 & 0xFF;
+ ((struct sockaddr_adhoc *)addr)->mac[4] = ha5 & 0xFF;
+ ((struct sockaddr_adhoc *)addr)->mac[5] = ha6 & 0xFF;
+ ((struct sockaddr_adhoc *)addr)->port = hp & 0xFFFF;
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_socket_addr (int socket, struct qsockaddr *addr)
+ {
+ pdpStatStruct pdpStat[20];
+ int length = sizeof(pdpStatStruct) * 20;
+
+ int err = sceNetAdhocGetPdpStat(&length, pdpStat);
+ if(err<0) return -1;
+
+ pdpStatStruct *tempPdp = findPdpStat(socket, pdpStat);
+ if(tempPdp < 0) return -1;
+
+ memcpy(((struct sockaddr_adhoc *)addr)->mac, tempPdp->mac, 6);
+ ((struct sockaddr_adhoc *)addr)->port = tempPdp->port;
+ addr->sa_family = ADHOC_NET;
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_name_from_addr (struct qsockaddr *addr, char *name)
+ {
+ strcpy(name, addr_to_string(addr));
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_addr_from_name(char *name, struct qsockaddr *addr)
+ {
+ return string_to_addr(name, addr);
+ }
+
+ //=============================================================================
+
+ int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2)
+ {
+ //if (addr1->sa_family != addr2->sa_family) return -1;
+ if (memcmp(((struct sockaddr_adhoc *)addr1)->mac, ((struct sockaddr_adhoc *)addr2)->mac, 6) != 0) return -1;
+ if (((struct sockaddr_adhoc *)addr1)->port != ((struct sockaddr_adhoc *)addr2)->port) return 1;
+ return 0;
+ }
+
+ //=============================================================================
+
+ int get_socket_port (struct qsockaddr *addr)
+ {
+ return ((struct sockaddr_adhoc *)addr)->port;
+ }
+
+
+ int set_socket_port (struct qsockaddr *addr, int port)
+ {
+ ((struct sockaddr_adhoc *)addr)->port = port;
+ return 0;
+ }
+
+ //=============================================================================
+
+ int pspSdkAdhocInit(char *product)
+ {
+ u32 retVal;
+ struct productStruct temp;
+
+ retVal = sceNetInit(0x20000, 0x20, 0x1000, 0x20, 0x1000);
+ if (retVal != 0) return retVal;
+
+ retVal = sceNetAdhocInit();
+ if (retVal != 0) return retVal;
+
+ strcpy(temp.product, product);
+ temp.unknown = 0;
+
+ retVal = sceNetAdhocctlInit(0x2000, 0x20, &temp);
+ if (retVal != 0) return retVal;
+
+ return 0;
+ }
+
+ //=============================================================================
+
+ void pspSdkAdhocTerm()
+ {
+ sceNetAdhocctlTerm();
+ sceNetAdhocTerm();
+ sceNetTerm();
+ }
+
+ //=============================================================================
+
+ }
+ }
+}
diff --git a/source/psp/network_psp.hpp b/source/psp/network_psp.hpp
new file mode 100644
index 0000000..ba6297a
--- /dev/null
+++ b/source/psp/network_psp.hpp
@@ -0,0 +1,84 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef QUAKE_NETWORK_PSP_HPP
+#define QUAKE_NETWORK_PSP_HPP
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+
+namespace quake
+{
+ namespace network
+ {
+ namespace infrastructure
+ {
+ int init (void);
+ void shut_down (void);
+ void listen (qboolean state);
+ int open_socket (int port);
+ int close_socket (int socket);
+ int connect (int socket, struct qsockaddr *addr);
+ int check_new_connections (void);
+ int read (int socket, byte *buf, int len, struct qsockaddr *addr);
+ int write (int socket, byte *buf, int len, struct qsockaddr *addr);
+ int broadcast (int socket, byte *buf, int len);
+ char* addr_to_string (struct qsockaddr *addr);
+ int string_to_addr (char *string, struct qsockaddr *addr);
+ int get_socket_addr (int socket, struct qsockaddr *addr);
+ int get_name_from_addr (struct qsockaddr *addr, char *name);
+ int get_addr_from_name (char *name, struct qsockaddr *addr);
+ int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2);
+ int get_socket_port (struct qsockaddr *addr);
+ int set_socket_port (struct qsockaddr *addr, int port);
+
+ int connect_to_apctl(int config);
+ int PartialIPAddress (char *in, struct qsockaddr *hostaddr);
+ }
+ namespace adhoc
+ {
+ int init (void);
+ void shut_down (void);
+ void listen (qboolean state);
+ int open_socket (int port);
+ int close_socket (int socket);
+ int connect (int socket, struct qsockaddr *addr);
+ int check_new_connections (void);
+ int read (int socket, byte *buf, int len, struct qsockaddr *addr);
+ int write (int socket, byte *buf, int len, struct qsockaddr *addr);
+ int broadcast (int socket, byte *buf, int len);
+ char* addr_to_string (struct qsockaddr *addr);
+ int string_to_addr (char *string, struct qsockaddr *addr);
+ int get_socket_addr (int socket, struct qsockaddr *addr);
+ int get_name_from_addr (struct qsockaddr *addr, char *name);
+ int get_addr_from_name (char *name, struct qsockaddr *addr);
+ int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2);
+ int get_socket_port (struct qsockaddr *addr);
+ int set_socket_port (struct qsockaddr *addr, int port);
+ int pspSdkAdhocInit(char *product);
+ void pspSdkAdhocTerm();
+ }
+ }
+}
+
+#endif
diff --git a/source/psp/pics/icon.png b/source/psp/pics/icon.png
new file mode 100644
index 0000000..885fc65
Binary files /dev/null and b/source/psp/pics/icon.png differ
diff --git a/source/psp/pics/pic.png b/source/psp/pics/pic.png
new file mode 100644
index 0000000..872a6eb
Binary files /dev/null and b/source/psp/pics/pic.png differ
diff --git a/source/psp/pics/snd0.at3 b/source/psp/pics/snd0.at3
new file mode 100644
index 0000000..bee6d4d
Binary files /dev/null and b/source/psp/pics/snd0.at3 differ
diff --git a/source/psp/readme.txt b/source/psp/readme.txt
new file mode 100644
index 0000000..df71d1b
--- /dev/null
+++ b/source/psp/readme.txt
@@ -0,0 +1,12 @@
+used parts from:
+QRack by R00K,
+JoeQuake by joseph,
+FitzQuake by john fitzburg,
+Kurok by mdave,
+FuhQuake,
+FteQuake,
+and DarkPlaces!
+Many not fixed bugs!
+
+
+Crow_Bar. 2009
diff --git a/source/psp/sound.cpp b/source/psp/sound.cpp
new file mode 100644
index 0000000..e77dae2
--- /dev/null
+++ b/source/psp/sound.cpp
@@ -0,0 +1,177 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include
+#include
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+
+namespace quake
+{
+ namespace sound
+ {
+ struct Sample
+ {
+ short left;
+ short right;
+ };
+
+ static const unsigned int channelCount = 2;
+ static const unsigned int inputBufferSize = 16384;
+ static const unsigned int inputFrequency = 11025;
+ static const unsigned int outputFrequency = 44100;
+ static const unsigned int inputSamplesPerOutputSample = outputFrequency / inputFrequency;
+ static Sample inputBuffer[inputBufferSize];
+ static volatile unsigned int samplesRead;
+
+ static inline void copySamples(const Sample* first, const Sample* last, Sample* destination)
+ {
+ switch (inputSamplesPerOutputSample)
+ {
+ case 1:
+ memcpy(destination, first, (last - first) * sizeof(Sample));
+ break;
+
+ case 2:
+ for (const Sample* source = first; source != last; ++source)
+ {
+ const Sample sample = *source;
+ *destination++ = sample;
+ *destination++ = sample;
+ }
+ break;
+
+ case 4:
+ for (const Sample* source = first; source != last; ++source)
+ {
+ const Sample sample = *source;
+ *destination++ = sample;
+ *destination++ = sample;
+ *destination++ = sample;
+ *destination++ = sample;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ static void fillOutputBuffer(void* buffer, unsigned int samplesToWrite, void* userData)
+ {
+ // Where are we writing to?
+ Sample* const destination = static_cast (buffer);
+
+ // Where are we reading from?
+ const Sample* const firstSampleToRead = &inputBuffer[samplesRead];
+
+ // How many samples to read?
+ const unsigned int samplesToRead = samplesToWrite / inputSamplesPerOutputSample;
+
+ // Going to wrap past the end of the input buffer?
+ const unsigned int samplesBeforeEndOfInput = inputBufferSize - samplesRead;
+ if (samplesToRead > samplesBeforeEndOfInput)
+ {
+ // Yes, so write the first chunk from the end of the input buffer.
+ copySamples(
+ firstSampleToRead,
+ firstSampleToRead + samplesBeforeEndOfInput,
+ &destination[0]);
+
+ // Write the second chunk from the start of the input buffer.
+ const unsigned int samplesToReadFromBeginning = samplesToRead - samplesBeforeEndOfInput;
+ copySamples(
+ &inputBuffer[0],
+ &inputBuffer[samplesToReadFromBeginning],
+ &destination[samplesBeforeEndOfInput * inputSamplesPerOutputSample]);
+ }
+ else
+ {
+ // No wrapping, just copy.
+ copySamples(
+ firstSampleToRead,
+ firstSampleToRead + samplesToRead,
+ &destination[0]);
+ }
+
+ // Update the read offset.
+ samplesRead = (samplesRead + samplesToRead) % inputBufferSize;
+ }
+ }
+}
+
+using namespace quake;
+using namespace quake::sound;
+
+qboolean SNDDMA_Init(void)
+{
+ // Set up Quake's audio.
+ shm = &sn;
+ shm->channels = channelCount;
+ shm->samplebits = 16;
+ shm->speed = inputFrequency;
+ shm->soundalive = qtrue;
+ shm->splitbuffer = qfalse;
+ shm->samples = inputBufferSize * channelCount;
+ shm->samplepos = 0;
+ shm->submission_chunk = 1;
+ shm->buffer = (unsigned char *) inputBuffer;
+
+ // Initialise the audio system. This initialises it for the CD audio module
+ // too.
+ pspAudioInit();
+
+ // Set the channel callback.
+ // Sound effects use channel 0, CD audio uses channel 1.
+ pspAudioSetChannelCallback(0, fillOutputBuffer, 0);
+
+ return qtrue;
+}
+
+void SNDDMA_Shutdown(void)
+{
+ // Clear the mixing buffer so we don't get any noise during cleanup.
+ memset(inputBuffer, 0, sizeof(inputBuffer));
+
+ // Clear the channel callback.
+ pspAudioSetChannelCallback(0, 0, 0);
+
+ // Stop the audio system?
+ pspAudioEndPre();
+
+ // Insert a false delay so the thread can be cleaned up.
+ sceKernelDelayThread(50 * 1000);
+
+ // Shut down the audio system.
+ pspAudioEnd();
+}
+
+int SNDDMA_GetDMAPos(void)
+{
+ return samplesRead * channelCount;
+}
+
+void SNDDMA_Submit(void)
+{
+}
diff --git a/source/psp/system.cpp b/source/psp/system.cpp
new file mode 100644
index 0000000..6990fa6
--- /dev/null
+++ b/source/psp/system.cpp
@@ -0,0 +1,526 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+//
+// 12/5/2020 - Use ADQuake's system.cpp and its move from std to sceIO for file I/O
+// because std::fclose() is bunked -- motolegacy
+// creds to st1x51
+//
+
+#define ENABLE_PRINTF 0
+
+#include "system.hpp"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#ifdef PROFILE
+#include
+#endif
+
+extern "C"
+{
+#include "../sys.h"
+#include "../quakedef.h"
+}
+#include "fnmatch.h"
+
+void CDAudio_Stop(void);
+
+namespace quake
+{
+ namespace main
+ {
+ extern const int cpuClockSpeed;
+ extern const int ramClockSpeed;
+ extern const int busClockSpeed;
+ }
+
+ namespace system
+ {
+ struct file
+ {
+ // Set on open.
+ char name[MAX_OSPATH + 1];
+ bool write;
+#if 0
+ // Set on open, suspend, restore.
+ FILE* handle;
+#else
+ SceUID handle;
+#endif
+ // Set on suspend.
+ SceOff offset;
+ };
+
+ static bool debugScreenInitialized = false;
+ static const std::size_t file_count = 64;
+ static file files[file_count];
+
+ void suspend()
+ {
+ Con_Printf("Suspend requested \n");
+ CDAudio_Pause();
+ // Close each file.
+ for (std::size_t file_index = 0; file_index < file_count; ++file_index)
+ {
+ // Is the file in use?
+ file& file = files[file_index];
+ if (file.name[0])
+ {
+ // Save the offset;
+#if 0
+ file.offset = ftell(file.handle);
+#else
+ file.offset = sceIoLseek(file.handle, 0, SEEK_CUR);
+#endif
+ // Close the file.
+#if 0
+ fclose(file.handle);
+#else
+ sceIoClose(file.handle);
+#endif
+ file.handle = 0;
+ }
+ }
+
+ Con_Printf("Filesystem sunspended\n");
+ }
+
+ void resume()
+ {
+ Con_Printf("Resume requested\n");
+ // Check each file.
+ for (std::size_t file_index = 0; file_index < file_count; ++file_index)
+ {
+ // Is the file in use?
+ file& file = files[file_index];
+ if (file.name[0])
+ {
+ // Reopen the file. This can repeatedly fail, so we keep trying.
+#if 0
+ const char* mode = file.write ? "ab" : "rb";
+#else
+ int mode = file.write ? PSP_O_APPEND : PSP_O_RDONLY;
+#endif
+#if 0
+ do
+ {
+ file.handle = fopen(file.name, mode);
+ }
+ while (!file.handle);
+
+ // Restore the offset;
+ if (fseek(file.handle, file.offset, SEEK_SET) != 0)
+ {
+ throw std::runtime_error("Couldn't seek in file");
+ }
+#else
+ file.handle = sceIoOpen(file.name, mode, 0777);
+ if(file.handle < 0)
+ {
+ throw std::runtime_error("Couldn't open file");
+ }
+
+ // Restore the offset;
+ if (sceIoLseek(file.handle, file.offset, SEEK_SET) != 0)
+ {
+ throw std::runtime_error("Couldn't seek in file");
+ }
+
+#endif
+ }
+ }
+
+ CDAudio_Resume();
+ Con_Printf("Filesystem resumed\n");
+
+ }
+ }
+}
+
+using namespace quake;
+using namespace quake::system;
+
+int Sys_FileOpenRead (char *path, int *hndl)
+{
+ // Find an unused file.
+ for (std::size_t file_index = 1; file_index < file_count; ++file_index)
+ {
+ // Is the file in use?
+ file& file = files[file_index];
+ if (file.name[0])
+ {
+ continue;
+ }
+
+ // Open the file.
+#if 0
+ file.handle = fopen(path, "rb");
+ if (!file.handle)
+#else
+ file.handle = sceIoOpen(path, PSP_O_RDONLY, 0777);
+ if (file.handle < 0)
+#endif
+ {
+ *hndl = -1;
+ return -1;
+ }
+
+ // Get the length.
+#if 0
+ if (fseek(file.handle, 0, SEEK_END) != 0)
+ {
+ Sys_Error("fseek failed");
+ }
+ const long length = ftell(file.handle);
+ if (fseek(file.handle, 0, SEEK_SET) != 0)
+ {
+ Sys_Error("fseek failed");
+ }
+#else
+ SceOff pos = sceIoLseek(file.handle, 0, SEEK_CUR);
+ const long length = sceIoLseek(file.handle, 0, SEEK_END);
+ sceIoLseek(file.handle, pos, SEEK_SET);
+#endif
+ // The file is now in use!
+ Q_strncpy(file.name, path, MAX_OSPATH);
+ file.write = false;
+
+ // Done.
+ *hndl = file_index;
+ return length;
+ }
+
+ Sys_Error("Out of file slots");
+ return -1;
+}
+
+int Sys_FileOpenWrite (char *path)
+{
+ // Find an unused file.
+ for (std::size_t file_index = 1; file_index < file_count; ++file_index)
+ {
+ // Is the file in use?
+ file& file = files[file_index];
+ if (file.name[0])
+ {
+ continue;
+ }
+
+ // Open the file.
+#if 0
+ file.handle = fopen(path, "wb");
+ if (!file.handle)
+#else
+ file.handle = sceIoOpen(path, PSP_O_WRONLY, 0777);
+ if (file.handle < 0)
+#endif
+ {
+ return -1;
+ }
+
+ // The file is now in use!
+ Q_strncpy(file.name, path, MAX_OSPATH);
+ file.write = true;
+
+ // Done.
+ return file_index;
+ }
+
+ Sys_Error("Out of file slots");
+ return -1;
+}
+
+void Sys_FileClose (int handle)
+{
+ // Close the file.
+ file& file = files[handle];
+#if 0
+ fclose(file.handle);
+#else
+ sceIoClose(file.handle);
+#endif
+ file.handle = 0;
+ file.name[0] = 0;
+}
+
+void Sys_FileSeek (int handle, int position)
+{
+ file& file = files[handle];
+#if 0
+ if (fseek(file.handle, position, SEEK_SET) != 0)
+ {
+ Sys_Error("fseek failed");
+ }
+#else
+ sceIoLseek(file.handle, position, SEEK_SET);
+#endif
+}
+
+int Sys_FileRead (int handle, void *dest, int count)
+{
+ file& file = files[handle];
+#if 0
+ return fread(dest, 1, count, file.handle);
+#else
+ return sceIoRead(file.handle, dest, count);
+#endif
+}
+
+int Sys_FileWrite (int handle, void *data, int count)
+{
+ file& file = files[handle];
+#if 0
+ return fwrite(data, 1, count, file.handle);
+#else
+ return sceIoWrite(file.handle, data, count);
+#endif
+}
+
+int Sys_FileTime (char *path)
+{
+ /*
+#ifdef _WIN32
+ return -1;
+#else
+ */
+ struct stat s;
+ memset(&s, 0, sizeof(s));
+
+ if (stat(path, &s) < 0)
+ {
+ return -1;
+ }
+
+ return s.st_ctime;
+ /*
+#endif
+ */
+}
+
+void Sys_mkdir (char *path)
+{
+ Sys_Printf("Mkdir: %s\n", path);
+ sceIoMkdir(path, 0x777);
+}
+
+void Sys_Error (char *error, ...)
+{
+ // Clear the sound buffer.
+ S_ClearBuffer();
+
+ // Put the error message in a buffer.
+ va_list args;
+ va_start(args, error);
+ char buffer[1024];
+ memset(buffer, 0, sizeof(buffer));
+ vsnprintf(buffer, sizeof(buffer) - 1, error, args);
+ va_end(args);
+
+ Con_Printf(buffer);
+ // Print the error message to the debug screen.
+ if (!debugScreenInitialized)
+ {
+ pspDebugScreenInit();
+ debugScreenInitialized = true;
+ }
+ pspDebugScreenSetTextColor(0xffffff);
+ pspDebugScreenPrintf("The following error occurred:\n");
+ pspDebugScreenSetTextColor(0x0000ff);
+ pspDebugScreenPrintData(buffer, strlen(buffer));
+ pspDebugScreenSetTextColor(0xffffff);
+ pspDebugScreenPrintf("\n\nPress CROSS to quit.\n");
+
+ // Wait for a button press.
+ sceCtrlSetSamplingCycle(0);
+ sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
+ SceCtrlData pad;
+ do {
+ sceCtrlReadBufferPositive(&pad, 1);
+ } while (pad.Buttons & PSP_CTRL_CROSS);
+ do {
+ sceCtrlReadBufferPositive(&pad, 1);
+ } while ((pad.Buttons & PSP_CTRL_CROSS) == 0);
+ do {
+ sceCtrlReadBufferPositive(&pad, 1);
+ } while (pad.Buttons & PSP_CTRL_CROSS);
+
+ // Quit.
+ pspDebugScreenPrintf("Shutting down...\n");
+ Sys_Quit();
+}
+
+void Sys_Printf (char *fmt, ...)
+{
+#if ENABLE_PRINTF
+ char buffer[1024];
+
+ va_list args;
+ va_start(args, fmt);
+ memset(buffer, 0, sizeof(buffer));
+ vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
+ va_end(args);
+
+ if (!debugScreenInitialized)
+ {
+ pspDebugScreenInit();
+ debugScreenInitialized = true;
+ }
+ pspDebugScreenPrintData(buffer, strlen(buffer));
+#endif
+}
+
+void Sys_Quit (void)
+{
+ // Shut down the host system.
+ if (host_initialized)
+ {
+ Host_Shutdown();
+ }
+
+ // Restore the old clock frequency.
+ scePowerSetClockFrequency(main::cpuClockSpeed, main::ramClockSpeed, main::busClockSpeed);
+
+ // Insert a false delay so files and stuff can be saved before the kernel kills us.
+ sceKernelDelayThread(50 * 1000);
+#ifdef PROFILE
+ gprof_cleanup();
+#endif
+ // Exit.
+ sceKernelExitGame();
+}
+
+double Sys_FloatTime (void)
+{
+ u64 ticks;
+ sceRtcGetCurrentTick(&ticks);
+ return ticks * 0.000001;
+}
+
+char *Sys_ConsoleInput (void)
+{
+ return 0;
+}
+
+void Sys_SendKeyEvents (void)
+{
+}
+
+void Sys_LowFPPrecision (void)
+{
+}
+
+void Sys_HighFPPrecision (void)
+{
+}
+
+/*
+=================================================
+simplified findfirst/findnext implementation:
+Sys_FindFirstFile and Sys_FindNextFile return
+filenames only, not a dirent struct. this is
+what we presently need in this engine.
+=================================================
+*/
+
+static DIR *finddir;
+static struct dirent *finddata;
+static char *findpath, *findpattern;
+
+char *Sys_FindFirstFile (char *path, char *pattern);
+void Sys_FindClose (void);
+char *Sys_FindNextFile (void);
+
+char *Sys_FindFirstFile (char *path, char *pattern)
+{
+ size_t tmp_len;
+
+ if (finddir)
+ Sys_Error ("Sys_FindFirst without FindClose");
+
+ finddir = opendir (path);
+ if (!finddir)
+ return NULL;
+
+ tmp_len = strlen (pattern);
+ findpattern = (char*) malloc (tmp_len + 1);
+ if (!findpattern)
+ return NULL;
+ strcpy (findpattern, pattern);
+ findpattern[tmp_len] = '\0';
+ tmp_len = strlen (path);
+ findpath = (char*) malloc (tmp_len + 1);
+ if (!findpath)
+ return NULL;
+ strcpy (findpath, path);
+ findpath[tmp_len] = '\0';
+
+ return Sys_FindNextFile();
+}
+
+char *Sys_FindNextFile (void)
+{
+ struct stat test;
+
+ if (!finddir)
+ return NULL;
+
+ do {
+ finddata = readdir(finddir);
+ if (finddata != NULL)
+ {
+ if (!fnmatch (findpattern, finddata->d_name, FNM_PATHNAME))
+ {
+ if ( (stat(va("%s/%s", findpath, finddata->d_name), &test) == 0) && S_ISREG(test.st_mode) )
+ return finddata->d_name;
+ }
+ }
+ } while (finddata != NULL);
+
+ return NULL;
+}
+
+void Sys_FindClose (void)
+{
+ if (finddir != NULL)
+ closedir(finddir);
+ if (findpath != NULL)
+ free (findpath);
+ if (findpattern != NULL)
+ free (findpattern);
+ finddir = NULL;
+ findpath = NULL;
+ findpattern = NULL;
+}
+
diff --git a/source/psp/system.hpp b/source/psp/system.hpp
new file mode 100644
index 0000000..2b17226
--- /dev/null
+++ b/source/psp/system.hpp
@@ -0,0 +1,37 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef QUAKE_SYSTEM_HPP
+#define QUAKE_SYSTEM_HPP
+
+namespace quake
+{
+ namespace system
+ {
+ // Prepares the system for suspension.
+ void suspend();
+
+ // Restores the system after suspension.
+ void resume();
+ }
+}
+
+#endif
diff --git a/source/psp/video_hardware.cpp b/source/psp/video_hardware.cpp
new file mode 100644
index 0000000..2a1604e
--- /dev/null
+++ b/source/psp/video_hardware.cpp
@@ -0,0 +1,694 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+extern "C"
+{
+#include "../quakedef.h"
+}
+
+#include
+
+#include "video_hardware_images.h"
+
+#include "iridlibs/perflib.h"
+
+#ifdef _WIN32
+# define ALIGNED(x)
+#else
+# define ALIGNED(x) __attribute__((aligned(x)))
+#endif
+
+#define DLIST_SIZE_KB 512 //256
+
+namespace quake
+{
+ namespace video
+ {
+ // Types.
+ //ScePspRGB565;
+ //ScePspRGBA8888
+ typedef ScePspRGBA8888 pixel;
+ typedef u8 texel;
+ typedef u16 depth_value;
+
+ // Constants.
+ static const std::size_t screen_width = 480;
+ static const std::size_t screen_height = 272;
+ static const std::size_t palette_size = 256;
+ static pixel* display_buffer = 0;
+ static pixel* draw_buffer = 0;
+ static depth_value* depth_buffer = 0;
+
+ //! The GU display list.
+ //! @note Aligned to 64 bytes so it doesn't share a cache line with anything.
+ unsigned int ALIGNED(64) display_list[DLIST_SIZE_KB * 1024];
+ }
+}
+
+using namespace quake;
+using namespace quake::video;
+
+extern qboolean depthfl;
+
+void* framebuffer;
+
+// Regular globals.
+ScePspRGBA8888 ALIGNED(16) d_8to24table[palette_size];
+ScePspRGBA8888 ALIGNED(16) d_8to24tableLM[palette_size];
+ScePspRGBA8888 ALIGNED(16) d_8to24tableQ2[palette_size];
+ScePspRGBA8888 ALIGNED(16) d_8to24tableH2[palette_size];
+int reloaded_pallete = 1;
+
+extern cvar_t r_vsync;
+extern cvar_t r_dithering;
+
+void VID_ConvectPalette(ScePspRGBA8888 *out, unsigned char* in)
+{
+ // Convert the palette to PSP format.
+ for (ScePspRGBA8888* color = &out[0]; color < &out[palette_size]; ++color)
+ {
+ const unsigned int r = *in++;
+ const unsigned int g = *in++;
+ const unsigned int b = *in++;
+ *color = GU_RGBA(r, g, b, 0xff);
+ }
+}
+void VID_InitPaleteLM()
+{
+ // Convert the palette to PSP format.
+ ScePspRGBA8888* color2 = &d_8to24tableLM[0];
+ for (unsigned int i=0; color2 < &d_8to24tableLM[palette_size]; ++color2, i++)
+ {
+ *color2 = GU_RGBA(i, i, i, i);
+ }
+}
+
+void VID_SetPaletteLM()
+{
+ // Upload the palette.
+ sceGuClutMode(GU_PSM_8888, 0, palette_size - 1, 0);
+ sceKernelDcacheWritebackRange(d_8to24tableLM, sizeof(d_8to24tableLM));
+ sceGuClutLoad(palette_size / 8, d_8to24tableLM);
+ reloaded_pallete = 1;
+}
+
+void VID_SetPaletteTX()
+{
+ // Upload the palette.
+ sceGuClutMode(GU_PSM_8888, 0, palette_size - 1, 0);
+ sceKernelDcacheWritebackRange(d_8to24table, sizeof(d_8to24table));
+ sceGuClutLoad(palette_size / 8, d_8to24table);
+ reloaded_pallete = 1;
+}
+
+void VID_SetPalette4(unsigned char* clut4pal) {
+ sceGuClutMode(GU_PSM_8888, 0, 0xFF, 0);
+ sceKernelDcacheWritebackRange(clut4pal, sizeof(clut4pal));
+ sceGuClutLoad(2, clut4pal);
+}
+
+void VID_SetPalette(unsigned char* palette)
+{
+ // Convert the palette to PSP format.
+ for (ScePspRGBA8888* color = &d_8to24table[0]; color < &d_8to24table[palette_size]; ++color)
+ {
+ const unsigned int r = *palette++;
+ const unsigned int g = *palette++;
+ const unsigned int b = *palette++;
+ *color = GU_RGBA(r, g, b, 0xff);
+ }
+
+ // Color 255 is transparent black.
+ // This is a bit of a dirty hack.
+ d_8to24table[255] = 0;
+
+ // Upload the palette.
+ sceGuClutMode(GU_PSM_8888, 0, palette_size - 1, 0);
+ sceKernelDcacheWritebackRange(d_8to24table, sizeof(d_8to24table));
+ sceGuClutLoad(palette_size / 8, d_8to24table);
+ reloaded_pallete = 1;
+}
+
+void VID_ShiftPalette(unsigned char* palette)
+{
+ VID_SetPalette(palette);
+}
+
+void VID_Init(unsigned char* palette)
+{
+ Sys_Printf("VID_Init\n");
+
+ // Allocate the buffers.
+ display_buffer = static_cast(valloc(screen_height * 512 * sizeof(pixel)));
+ if (!display_buffer)
+ {
+ Sys_Error("Couldn't allocate display buffer");
+ }
+
+ draw_buffer = static_cast(valloc(screen_height * 512 * sizeof(pixel)));
+ if (!draw_buffer)
+ {
+ Sys_Error("Couldn't allocate draw buffer");
+ }
+
+ depth_buffer = static_cast(valloc(screen_height * 512 * sizeof(depth_value)));
+ if (!depth_buffer)
+ {
+ Sys_Error("Couldn't allocate depth buffer");
+ }
+
+ // Initialise the GU.
+ sceGuInit();
+
+ // Set up the GU.
+ sceGuStart(GU_DIRECT, display_list);
+ {
+ //sceGuDrawBuffer(GU_PSM_5650, vrelptr(draw_buffer), 512);
+ sceGuDrawBuffer(GU_PSM_8888, vrelptr(draw_buffer), 512);
+ sceGuDispBuffer(screen_width, screen_height, vrelptr(display_buffer), 512);
+ sceGuDepthBuffer(vrelptr(depth_buffer), 512);
+
+ // Set the rendering offset and viewport.
+ sceGuOffset(2048 - (screen_width / 2), 2048 - (screen_height / 2));
+ sceGuViewport(2048, 2048, screen_width, screen_height);
+
+ // Set up scissoring.
+ sceGuEnable(GU_SCISSOR_TEST);
+ sceGuScissor(0, 0, screen_width, screen_height);
+
+ // Set up texturing.
+ sceGuEnable(GU_TEXTURE_2D);
+
+ // Set up clearing.
+ sceGuClearDepth(65535);
+ sceGuClearColor(GU_RGBA(0x10,0x20,0x40,0xff));
+
+ // Set up depth.
+ sceGuDepthRange(0, 65535);
+ sceGuDepthFunc(GU_LEQUAL);
+ sceGuEnable(GU_DEPTH_TEST);
+
+ // Set the matrices.
+ sceGumMatrixMode(GU_PROJECTION);
+ sceGumLoadIdentity();
+ sceGumUpdateMatrix();
+ sceGumMatrixMode(GU_VIEW);
+ sceGumLoadIdentity();
+ sceGumUpdateMatrix();
+ sceGumMatrixMode(GU_MODEL);
+ sceGumLoadIdentity();
+ sceGumUpdateMatrix();
+
+ // Set up culling.
+ sceGuFrontFace(GU_CW);
+ sceGuEnable(GU_CULL_FACE);
+ sceGuEnable(GU_CLIP_PLANES);
+
+ // Set up blending.
+ sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
+ }
+
+ sceGuFinish();
+ sceGuSync(0,0);
+
+ // Turn on the display.
+ sceDisplayWaitVblankStart();
+ sceGuDisplay(GU_TRUE);
+
+ // Set up Quake's video parameters.
+ vid.aspect = (static_cast(screen_height) / static_cast(screen_width)) * (4.0f / 3.0f);
+ vid.buffer = 0;
+ vid.colormap = host_colormap;
+ vid.colormap16 = 0;
+ vid.conbuffer = 0;
+ vid.conheight = screen_height;
+ vid.conrowbytes = 0;
+ vid.conwidth = screen_width;
+ vid.direct = 0;
+ vid.fullbright = palette_size - LittleLong(*((int *) vid.colormap + 2048));
+ vid.height = screen_height;
+ vid.maxwarpheight = screen_width;
+ vid.maxwarpwidth = screen_height;
+ vid.numpages = INT_MAX;
+ vid.recalc_refdef = 0;
+ vid.rowbytes = 0;
+ vid.width = screen_width;
+
+ // Start a render.
+ sceGuStart(GU_DIRECT, display_list);
+
+ // Set the palette.
+ VID_SetPalette(palette);
+ VID_InitPaleteLM();
+ VID_ConvectPalette(d_8to24tableQ2, host_q2pal);
+ VID_ConvectPalette(d_8to24tableH2, host_h2pal);
+}
+
+void VID_Shutdown(void)
+{
+ // Finish rendering.
+ sceGuFinish();
+ sceGuSync(0,0);
+
+ // Shut down the display.
+ sceGuTerm();
+
+ // Free the buffers.
+ vfree(depth_buffer);
+ depth_buffer = 0;
+ vfree(draw_buffer);
+ draw_buffer = 0;
+ vfree(display_buffer);
+ display_buffer = 0;
+}
+
+void GL_BeginRendering (int *x, int *y, int *width, int *height)
+{
+ PFL_BeginGPURecord();
+ *x = 2048;
+ *y = 2048;
+ *width = screen_width;
+ *height = screen_height;
+
+ if (r_dithering.value)
+ sceGuEnable(GU_DITHER);
+ else
+ sceGuDisable(GU_DITHER);
+}
+
+void GL_EndRendering (void)
+{
+ // Finish rendering.
+ sceGuFinish();
+ sceGuSync(0, 0);
+ PFL_EndGPURecord();
+
+ // At the moment only do this if we are in network mode, once we get above
+ // 60fps we might as well leave it on for all games
+ if (tcpipAvailable || r_vsync.value)
+ sceDisplayWaitVblankStart();
+
+ // Switch the buffers.
+ sceGuSwapBuffers();
+ std::swap(draw_buffer, display_buffer);
+
+ // Start a new render.
+ sceGuStart(GU_DIRECT, display_list);
+}
+
+void GL_GetPixelsBGR(byte *buffer, int width, int height, int i)
+{
+ for (int y = 0; y < height; ++y)
+ {
+ const pixel* src = display_buffer + ((height - y - 1) * 512);
+ for (int x = 0; x < width; ++x)
+ {
+ const pixel argb = *src++;
+#if 0
+ buffer[i++] = ((argb >> 11) & 0x1f) << 3; //B
+ buffer[i++] = ((argb >> 5 ) & 0x3f) << 2; //G
+ buffer[i++] = (argb & 0x1f) << 3; //R
+#else
+ buffer[i++] = (argb >> 16) & 0xff;//B
+ buffer[i++] = (argb >> 8) & 0xff; //G
+ buffer[i++] = argb & 0xff; //R
+#endif
+ }
+ }
+
+}
+
+void GL_GetPixelsRGB(byte *buffer, int width, int height, int i)
+{
+ for (int y = 0; y < height; ++y)
+ {
+ const pixel* src = display_buffer + ((height - y - 1) * 512);
+ for (int x = 0; x < width; ++x)
+ {
+ const pixel argb = *src++;
+#if 0
+ buffer[i++] = (argb & 0x1f) << 3; //R
+ buffer[i++] = ((argb >> 5 ) & 0x3f) << 2; //G
+ buffer[i++] = ((argb >> 11) & 0x1f) << 3; //B
+#else
+ buffer[i++] = ((argb)& 0xff); //R
+ buffer[i++] = ((argb >> 8)& 0xff); //G
+ buffer[i++] = ((argb >> 16)& 0xff); //B
+#endif
+ }
+ }
+
+}
+
+void GL_GetPixelsRGBA(byte *buffer, int width, int height, int i)
+{
+ for (int y = 0; y < height; ++y)
+ {
+ const pixel* src = display_buffer + ((height - y - 1) * 512);
+ for (int x = 0; x < width; ++x)
+ {
+ const pixel argb = *src++;
+#if 0
+ buffer[i++] = (argb & 0x1f) << 3; //R
+ buffer[i++] = ((argb >> 5 ) & 0x3f) << 2; //G
+ buffer[i++] = ((argb >> 11) & 0x1f) << 3; //B
+ buffer[i++] = 0xff; //A
+#else
+ buffer[i++] = ((argb)& 0xff); //R
+ buffer[i++] = ((argb >> 8)& 0xff); //G
+ buffer[i++] = ((argb >> 16)& 0xff); //B
+ buffer[i++] = ((argb >> 24)& 0xff); //A
+#endif
+ }
+ }
+
+}
+
+u32 GL_GetDrawBuffer(void)
+{
+ return (u32)vrelptr(draw_buffer);
+}
+
+void D_StartParticles (void)
+{
+ sceGuDepthMask(GU_TRUE);
+ if (r_particles_simple.value == qtrue)
+ {
+ sceGuDisable(GU_TEXTURE_2D);
+ }
+ else
+ {
+ sceGuDisable(GU_FOG);
+ GL_Bind(particletexture);
+ sceGuEnable(GU_BLEND);
+ sceGuDepthMask(GU_TRUE);
+ sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0xFFFFFFFF);
+ sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA);
+ }
+}
+
+void D_EndParticles (void)
+{
+ sceGuDepthMask(GU_FALSE);
+ if (r_particles_simple.value == qtrue)
+ {
+ sceGuEnable(GU_TEXTURE_2D);
+ }
+ else
+ {
+ sceGuEnable(GU_FOG);
+ sceGuDisable(GU_BLEND);
+ sceGuDepthMask(GU_FALSE);
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+ sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
+ }
+}
+
+
+
+static int BufIdx = 0;
+static int BufSize = 0;
+
+psp_particle* D_CreateBuffer (int size)
+{
+ psp_particle* const vertices = static_cast(sceGuGetMemory(size*sizeof(psp_particle)));
+
+ BufSize = size;
+ BufIdx = 0;
+
+ return vertices;
+}
+
+
+void D_DeleteBuffer (psp_particle* vertices)
+{
+ if (BufIdx > 0)
+ {
+ sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF|GU_TEXTURE_32BITF|GU_COLOR_8888, BufIdx, 0, vertices);
+ BufIdx = 0;
+ BufSize = -1;
+ }
+
+}
+
+int D_DrawParticleBuffered (psp_particle* vertices, particle2_t *pparticle, vec3_t up, vec3_t right, float scale)
+{
+ unsigned int color = d_8to24table[static_cast(pparticle->color)];
+ int i = BufIdx;
+
+ vertices[i].first.x = pparticle->org[0];
+ vertices[i].first.y = pparticle->org[1];
+ vertices[i].first.z = pparticle->org[2];
+ vertices[i].first.s = 0.0;
+ vertices[i].first.t = 0.0;
+ vertices[i].first.color = color;
+
+ vertices[i].second.x = pparticle->org[0] + scale*(up[0] + right[0]);
+ vertices[i].second.y = pparticle->org[1] + scale*(up[1] + right[1]);
+ vertices[i].second.z = pparticle->org[2] + scale*(up[2] + right[2]);
+ vertices[i].second.s = 1.0;
+ vertices[i].second.t = 1.0;
+ vertices[i].second.color = color;
+
+ BufIdx++;
+
+ if (BufIdx >= BufSize)
+ {
+ sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF|GU_TEXTURE_32BITF|GU_COLOR_8888, BufSize, 0, vertices);
+ BufIdx = 0;
+ BufSize = -1;
+ return -1;
+ }
+
+ return BufIdx;
+}
+
+
+void D_DrawParticle (particle2_t *pparticle, vec3_t up, vec3_t right, float scale)
+{
+ unsigned int color = d_8to24table[static_cast(pparticle->color)];
+
+ struct part_vertex
+ {
+ float s, t;
+ unsigned int color;
+ float x, y, z;
+ };
+
+ part_vertex* const vertices = static_cast(sceGuGetMemory(2*sizeof(part_vertex)));
+
+ vertices[0].x = pparticle->org[0];
+ vertices[0].y = pparticle->org[1];
+ vertices[0].z = pparticle->org[2];
+ vertices[0].s = 0.0;
+ vertices[0].t = 0.0;
+ vertices[0].color = color;
+
+ vertices[1].x = pparticle->org[0] + scale*(up[0] + right[0]);
+ vertices[1].y = pparticle->org[1] + scale*(up[1] + right[1]);
+ vertices[1].z = pparticle->org[2] + scale*(up[2] + right[2]);
+ vertices[1].s = 1.0;
+ vertices[1].t = 1.0;
+ vertices[1].color = color;
+
+ sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF|GU_TEXTURE_32BITF|GU_COLOR_8888, 2, 0, vertices);
+
+}
+
+
+/*
+==============================================================================
+
+ SCREEN SHOTS
+
+==============================================================================
+*/
+typedef struct _TargaHeader {
+ unsigned char id_length, colormap_type, image_type;
+ unsigned short colormap_index, colormap_length;
+ unsigned char colormap_size;
+ unsigned short x_origin, y_origin, width, height;
+ unsigned char pixel_size, attributes;
+} TargaHeader;
+
+int Image_WriteJPEG (char *filename, int compression, byte *pixels, int width, int height);
+
+/*
+==================
+SCR_ScreenShot_f
+==================
+*/
+void SCR_ScreenShot_f (void)
+{
+ byte *buffer;
+ char name[80];
+ char checkname[MAX_OSPATH];
+ int buffersize = glwidth * glheight * 3;
+//
+// find a file name to save it to
+//
+ strcpy(name,"shots/DQshot00.jpg");
+
+ int i;
+
+ for (i=0 ; i<=100 ; i++)
+ {
+ name[12] = i/10 + '0';
+ name[13] = i%10 + '0';
+ sprintf (checkname, "%s/%s", com_gamedir, name);
+ if (Sys_FileTime(checkname) == -1)
+ break; // file doesn't exist
+ }
+
+ if (i==100)
+ {
+ Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n");
+ return;
+ }
+
+ buffer = static_cast(malloc(buffersize));
+ memset(buffer, 0, buffersize);
+
+ GL_GetPixelsRGB(buffer, glwidth, glheight, 0);
+
+ Image_WriteJPEG (name, 75, buffer + buffersize - 3 * glwidth, -glwidth, glheight);
+
+ free (buffer);
+
+ Con_Printf ("Wrote %s\n", name);
+}
+
+#if 0
+//======================Utility dialog functions================================
+
+pspUtilityMsgDialogParams dialog;
+
+static void ConfigureDialog(pspUtilityMsgDialogParams *dialog, size_t dialog_size)
+{
+ memset(dialog, 0, dialog_size);
+
+ dialog->base.size = dialog_size;
+ sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_LANGUAGE,
+ &dialog->base.language); // Prompt language
+ sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_UNKNOWN,
+ &dialog->base.buttonSwap); // X/O button swap
+
+ dialog->base.graphicsThread = 0x11;
+ dialog->base.accessThread = 0x13;
+ dialog->base.fontThread = 0x12;
+ dialog->base.soundThread = 0x10;
+}
+
+void ShowErrorDialog(const unsigned int error)
+{
+ ConfigureDialog(&dialog, sizeof(dialog));
+ dialog.mode = PSP_UTILITY_MSGDIALOG_MODE_ERROR;
+ dialog.options = PSP_UTILITY_MSGDIALOG_OPTION_ERROR;
+ dialog.errorValue = error;
+
+ sceUtilityMsgDialogInitStart(&dialog);
+
+ for(;;)
+ {
+
+
+ switch(sceUtilityMsgDialogGetStatus())
+ {
+
+ case PSP_UTILITY_DIALOG_VISIBLE:
+ sceUtilityMsgDialogUpdate(1);
+ break;
+
+ case PSP_UTILITY_DIALOG_QUIT:
+ sceUtilityMsgDialogShutdownStart();
+ break;
+
+ case PSP_UTILITY_DIALOG_NONE:
+ return;
+
+ }
+
+ ..upd main frame
+ }
+}
+
+void ShowMessageDialog(const char *message, int enableYesno)
+{
+ ConfigureDialog(&dialog, sizeof(dialog));
+ dialog.mode = PSP_UTILITY_MSGDIALOG_MODE_TEXT;
+ dialog.options = PSP_UTILITY_MSGDIALOG_OPTION_TEXT;
+
+ if(enableYesno)
+ dialog.options |= PSP_UTILITY_MSGDIALOG_OPTION_YESNO_BUTTONS|PSP_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO;
+
+ strcpy(dialog.message, message);
+
+ sceUtilityMsgDialogInitStart(&dialog);
+
+ for(;;)
+ {
+
+
+
+ switch(sceUtilityMsgDialogGetStatus())
+ {
+
+ case 2:
+ sceUtilityMsgDialogUpdate(1);
+ break;
+
+ case 3:
+ sceUtilityMsgDialogShutdownStart();
+ break;
+
+ case 0:
+ return;
+
+ }
+
+ ..upd main frame
+ }
+
+}
+
+void M_Exit_f (void)
+{
+ ShowMessageDialog(" Really quit? ", 1);
+
+ if(dialog.buttonPressed == PSP_UTILITY_MSGDIALOG_RESULT_YES)
+ {
+ CL_Disconnect ();
+ Host_ShutdownServer(qfalse);
+ Sys_Quit ();
+ }
+}
+#endif
diff --git a/source/psp/video_hardware.h b/source/psp/video_hardware.h
new file mode 100644
index 0000000..1fa5ec8
--- /dev/null
+++ b/source/psp/video_hardware.h
@@ -0,0 +1,419 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include
+
+void GL_BeginRendering (int *x, int *y, int *width, int *height);
+void GL_EndRendering (void);
+u32 GL_GetDrawBuffer(void);
+
+void GL_Upload8(int texture_index, const byte *data, int width, int height);
+void GL_Upload16(int texture_index, const byte *data, int width, int height);
+int GL_LoadTexture(const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level);
+// CLUT4
+int GL_LoadTexture4(const char *identifier, unsigned int width, unsigned int height, const byte *data, int filter);
+
+int GL_LoadTextureLM (const char *identifier, int width, int height, const byte *data, int bpp, int filter, qboolean update, int forcopy);
+int GL_LoadImages (const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level, int bpp);
+int GL_LoadTexturePixels (byte *data, char *identifier, int width, int height, int mode);
+int loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, int filter);
+int GL_LoadPaletteTexture (const char *identifier, int width, int height, const byte *data, byte *palette, int paltype, qboolean stretch_to_power_of_two, int filter, int mipmap_level);
+
+//Crow_bar
+void GL_GetPixelsBGR (byte *buffer, int width, int height, int i);
+void GL_GetPixelsRGB (byte *buffer, int width, int height, int i);
+void GL_GetPixelsRGBA(byte *buffer, int width, int height, int i);
+
+
+#define PAL_RGB 24
+#define PAL_RGBA 32
+#define PAL_Q2 64 //Quake II palette
+#define PAL_H2 65 //Hexen II palette
+
+int GL_LoadPalTex (const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level, byte *palette, int paltype);
+
+int GL_LoadPalletedTexture (byte *in, char *identifier, int width, int height, int mode);
+
+void GL_UnloadTexture (const int texture_index);
+
+extern int glx, gly, glwidth, glheight;
+
+/*
+---------------------------------
+half-life Render Modes. Crow_bar
+---------------------------------
+*/
+
+#define TEX_COLOR 1
+#define TEX_TEXTURE 2
+#define TEX_GLOW 3
+#define TEX_SOLID 4
+#define TEX_ADDITIVE 5
+#define TEX_LMPOINT 6 //for light point
+
+#define ISCOLOR(ent) ((ent)->rendermode == TEX_COLOR && ((ent)->rendercolor[0] <= 1|| \
+ (ent)->rendercolor[1] <= 1|| \
+ (ent)->rendercolor[2] <= 1))
+
+#define ISTEXTURE(ent) ((ent)->rendermode == TEX_TEXTURE && (ent)->renderamt > 0 && (ent)->renderamt <= 1)
+#define ISGLOW(ent) ((ent)->rendermode == TEX_GLOW && (ent)->renderamt > 0 && (ent)->renderamt <= 1)
+#define ISSOLID(ent) ((ent)->rendermode == TEX_SOLID && (ent)->renderamt > 0 && (ent)->renderamt <= 1)
+#define ISADDITIVE(ent) ((ent)->rendermode == TEX_ADDITIVE && (ent)->renderamt > 0 && (ent)->renderamt <= 1)
+
+#define ISLMPOINT(ent) ((ent)->rendermode == TEX_LMPOINT && ((ent)->rendercolor[0] <= 1|| \
+ (ent)->rendercolor[1] <= 1|| \
+ (ent)->rendercolor[2] <= 1))
+/*
+---------------------------------
+//half-life Render Modes
+---------------------------------
+*/
+
+#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0)
+ // normalizing factor so player model works out to about
+ // 1 pixel per triangle
+#define MAX_LBM_HEIGHT 480
+
+#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf
+
+#define SKYSHIFT 7
+#define SKYSIZE (1 << SKYSHIFT)
+#define SKYMASK (SKYSIZE - 1)
+
+#define BACKFACE_EPSILON 0.01
+
+//#ifdef SLIM
+#define MAX_LIGHTMAPS 64
+//#else
+//#define MAX_LIGHTMAPS 16
+//#endif
+
+
+void R_TimeRefresh_f (void);
+void R_ReadPointFile_f (void);
+texture_t *R_TextureAnimation (texture_t *base);
+
+typedef struct surfcache_s
+{
+ struct surfcache_s *next;
+ struct surfcache_s **owner; // NULL is an empty chunk of memory
+ int lightadj[MAXLIGHTMAPS]; // checked for strobe flush
+ int dlight;
+ int size; // including header
+ unsigned width;
+ unsigned height; // DEBUG only needed for debug
+ float mipscale;
+ struct texture_s *texture; // checked for animating textures
+ byte data[4]; // width*height elements
+} surfcache_t;
+
+typedef enum
+{
+ pm_classic, pm_qmb, pm_quake3, pm_mixed
+} part_mode_t;
+
+typedef struct
+{
+ pixel_t *surfdat; // destination for generated surface
+ int rowbytes; // destination logical width in bytes
+ msurface_t *surf; // description for surface to generate
+ fixed8_t lightadj[MAXLIGHTMAPS];
+ // adjust for lightmap levels for dynamic lighting
+ texture_t *texture; // corrected for animating textures
+ int surfmip; // mipmapped ratio of surface texels / world pixels
+ int surfwidth; // in mipmapped texels
+ int surfheight; // in mipmapped texels
+} drawsurf_t;
+
+
+typedef enum {
+ pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2
+} ptype_t;
+
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+typedef struct particle2_s
+{
+// driver-usable fields
+ vec3_t org;
+ float color;
+// drivers never touch the following fields
+ struct particle2_s *next;
+ vec3_t vel;
+ float ramp;
+ float die;
+ ptype_t type;
+} particle2_t;
+
+//====================================================
+
+
+extern entity_t r_worldentity;
+extern qboolean r_cache_thrash; // compatability
+extern vec3_t modelorg, r_entorigin;
+extern entity_t *currententity;
+extern int r_visframecount; // ??? what difs?
+extern int r_framecount;
+extern mplane_t frustum[4];
+extern int c_brush_polys, c_alias_polys;
+
+
+//
+// view origin
+//
+extern vec3_t vup;
+extern vec3_t vpn;
+extern vec3_t vright;
+extern vec3_t r_origin;
+
+//
+// screen size info
+//
+extern refdef_t r_refdef;
+extern mleaf_t *r_viewleaf, *r_oldviewleaf;
+extern texture_t *r_notexture_mip;
+extern int d_lightstylevalue[256]; // 8.8 fraction of base light value
+
+extern int skyimage[6]; // Where sky images are stored
+extern int lightmap_index[MAX_LIGHTMAPS]; // Where lightmaps are stored
+
+extern int reloaded_pallete;
+
+extern qboolean envmap;
+extern int currenttexture;
+extern int cnttextures[2];
+extern int particletexture;
+extern int playertextures;
+//extern int playertextures[MAX_SCOREBOARD];
+
+extern int skytexturenum; // index in cl.loadmodel, not gl texture object
+
+extern cvar_t scr_conheight;
+extern cvar_t scr_fov;
+
+extern cvar_t r_partalpha;
+
+extern cvar_t r_maxrange;
+
+extern cvar_t r_restexf;
+extern cvar_t r_texcompr;
+
+extern cvar_t r_skyfog;
+extern cvar_t r_skyvflip;
+extern cvar_t r_skydis;
+
+extern cvar_t r_caustics;
+extern cvar_t r_detail;
+extern cvar_t r_detail_mipmaps;
+extern cvar_t r_detail_mipmaps_func;
+extern cvar_t r_detail_mipmaps_bias;
+extern cvar_t r_model_brightness;
+extern cvar_t r_farclip;
+extern cvar_t r_loadq3models;
+
+extern cvar_t r_i_model_animation;
+extern cvar_t r_i_model_transform;
+extern cvar_t r_ipolations;
+extern cvar_t r_asynch;
+
+extern cvar_t cl_loadmapcfg;
+extern cvar_t r_fastsky;
+extern cvar_t r_skycolor;
+extern cvar_t r_waterripple;
+extern cvar_t r_norefresh;
+extern cvar_t r_drawentities;
+extern cvar_t r_drawworld;
+extern cvar_t r_drawviewmodel;
+extern cvar_t r_speeds;
+extern cvar_t r_waterwarp;
+extern cvar_t r_fullbright;
+extern cvar_t r_lightmap;
+extern cvar_t r_shadows;
+extern cvar_t r_mirroralpha;
+extern cvar_t r_wateralpha;
+extern cvar_t r_dynamic;
+extern cvar_t r_novis;
+extern cvar_t r_tex_scale_down;
+extern cvar_t r_particles_simple;
+extern cvar_t r_vsync;
+extern cvar_t r_mipmaps;
+extern cvar_t r_mipmaps_func;
+extern cvar_t r_mipmaps_bias;
+extern cvar_t r_retro; // dr_mabuse1981: "retro filter" (makes textures drawn with GU_NEAREST).
+extern cvar_t gl_keeptjunctions;
+extern cvar_t r_waterwarp;
+
+extern cvar_t r_showbboxes;
+extern cvar_t r_showbboxes_full;
+
+extern cvar_t r_polyblend;
+
+extern cvar_t r_showtris;
+extern cvar_t r_showtris_full;
+
+extern cvar_t r_laserpoint;
+extern cvar_t r_particle_count;
+extern cvar_t r_part_explosions;
+extern cvar_t r_part_trails;
+extern cvar_t r_part_sparks;
+extern cvar_t r_part_spikes;
+extern cvar_t r_part_gunshots;
+extern cvar_t r_part_blood;
+extern cvar_t r_part_telesplash;
+extern cvar_t r_part_blobs;
+extern cvar_t r_part_lavasplash;
+extern cvar_t r_part_flames;
+extern cvar_t r_part_lightning;
+extern cvar_t r_part_flies;
+extern cvar_t r_particle_count;
+extern cvar_t r_bounceparticles;
+extern cvar_t r_explosiontype;
+extern cvar_t r_part_muzzleflash;
+extern cvar_t r_flametype;
+extern cvar_t r_bounceparticles;
+extern cvar_t r_decal_blood;
+extern cvar_t r_decal_bullets;
+extern cvar_t r_decal_sparks;
+extern cvar_t r_decal_explosions;
+extern cvar_t r_coronas;
+
+// MotoLegacy - simplified cvars for decals/particles (5/27/2020)
+extern cvar_t nzp_particles;
+extern cvar_t nzp_decals;
+
+
+extern int mirrortexturenum; // quake texturenum, not gltexturenum
+extern qboolean mirror;
+extern mplane_t *mirror_plane;
+
+extern ScePspFMatrix4 r_world_matrix;
+
+void GL_Bind (int texture_index);
+void GL_BindLM (int texture_index);
+void GL_Copy (int texture_index, int sx, int sy, int dx, int dy, int w, int h);
+
+// Added by PM
+int R_LightPoint (vec3_t p);
+void R_DrawBrushModel (entity_t *e);
+void R_AnimateLight (void);
+void R_DrawWorld (void);
+void R_RenderDlights (void);
+void R_DrawParticles (void);
+void R_DrawWaterSurfaces (void);
+void R_RenderBrushPoly (msurface_t *fa);
+void R_InitParticles (void);
+void R_ClearParticles (void);
+void GL_BuildLightmaps (void);
+void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr);
+void GL_Set2D (void);
+void GL_SubdivideSurface (msurface_t *fa);
+void GL_Surface (msurface_t *fa); // dr_mabuse1981: fuck you lag.
+void EmitWaterPolys (msurface_t *fa);
+void EmitSkyPolys (msurface_t *fa);
+void EmitReflectivePolys (msurface_t *fa);
+void EmitScrollPolys (msurface_t *fa);
+void EmitBothSkyLayers (msurface_t *fa);
+void EmitUnderWaterPolys (void);
+void EmitDetailPolys (void);
+void R_DrawSkyChain (msurface_t *s);
+qboolean R_CullBox (vec3_t emins, vec3_t emaxs);
+qboolean R_CullSphere (vec3_t centre, float radius);
+void R_MarkLights (dlight_t *light, int bit, mnode_t *node);
+void R_RotateForEntity (entity_t *e, int shadow);
+void R_BlendedRotateForEntity (entity_t *e, int shadow);
+void R_RotateForViewEntity (entity_t *ent); //clone (R_RotateForEntity)
+void R_RotateForTagEntity (tagentity_t *tagent, md3tag_t *tag, float *m); //for q3 models
+void R_StoreEfrags (efrag_t **ppefrag);
+void D_StartParticles (void);
+// void D_DrawParticle (particle_t *pparticle);
+void D_DrawParticle (particle2_t *pparticle, vec3_t up, vec3_t right, float scale);
+void D_EndParticles (void);
+
+void Fog_Init (void);
+void Fog_NewMap (void);
+
+void Sky_LoadSkyBox (char *name);
+void Sky_NewMap (void);
+void Sky_Init (void);
+void R_ClearSkyBox (void);
+void R_DrawSkyBox (void);
+
+//-----------------------------------------------------
+void QMB_InitParticles (void);
+void QMB_ClearParticles (void);
+void QMB_DrawParticles (void);
+void QMB_Q3TorchFlame (vec3_t org, float size);
+void QMB_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count);
+void QMB_RocketTrail (vec3_t start, vec3_t end, trail_type_t type);
+void QMB_RayFlash (vec3_t org, float weapon);
+void QMB_BlobExplosion (vec3_t org);
+void QMB_ParticleExplosion (vec3_t org);
+void QMB_LavaSplash (vec3_t org);
+void QMB_TeleportSplash (vec3_t org);
+void QMB_InfernoFlame (vec3_t org);
+void QMB_StaticBubble (entity_t *ent);
+void QMB_ColorMappedExplosion (vec3_t org, int colorStart, int colorLength);
+void QMB_TorchFlame (vec3_t org);
+void QMB_FlameGt (vec3_t org, float size, float time);
+void QMB_BigTorchFlame (vec3_t org);
+void QMB_ShamblerCharge (vec3_t org);
+void QMB_LightningBeam (vec3_t start, vec3_t end);
+//void QMB_GenSparks (vec3_t org, byte col[3], float count, float size, float life);
+void QMB_EntityParticles (entity_t *ent);
+void QMB_MuzzleFlash (vec3_t org);
+void QMB_MuzzleFlashLG (vec3_t org);
+void QMB_Q3Gunshot (vec3_t org, int skinnum, float alpha);
+void QMB_Q3Teleport (vec3_t org, float alpha);
+void QMB_Q3TorchFlame (vec3_t org, float size);
+
+extern qboolean qmb_initialized;
+
+void R_SpawnDecal (vec3_t center, vec3_t normal, vec3_t tangent, int tex, int size, int isbsp);
+void R_SpawnDecalStatic(vec3_t org, int tex, int size);
+void R_SpawnDecalBSP (vec3_t org, char *texname, int size);
+
+void CheckParticles (void);
+
+void UnloadWads (void); //By Crow_bar
+#if 0
+void ShowErrorDialog(const unsigned int error);
+void ShowMessageDialog(const char *message, int enableYesno);
+#endif
+//====================================================
+
+void Fog_ParseServerMessage (void);
+
+typedef struct {
+ float s, t;
+ unsigned int color;
+ float x, y, z;
+} part_vertex;
+
+typedef struct {
+ part_vertex first, second;
+} psp_particle;
+
+
+psp_particle* D_CreateBuffer (int size);
+void D_DeleteBuffer (psp_particle* vertices);
+int D_DrawParticleBuffered (psp_particle* vertices, particle2_t *pparticle, vec3_t up, vec3_t right, float scale);
+
+
+extern int zombie_skins[2][2];
\ No newline at end of file
diff --git a/source/psp/video_hardware_QMB.cpp b/source/psp/video_hardware_QMB.cpp
new file mode 100644
index 0000000..90cd8d4
--- /dev/null
+++ b/source/psp/video_hardware_QMB.cpp
@@ -0,0 +1,3518 @@
+/*
+Copyright (C) 2002-2003, Dr Labman, A. Nourai
+Copyright (C) 2009, Crow_bar psp port
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// gl_rpart.c
+
+extern "C"
+{
+#include "../quakedef.h"
+//entity_t *CL_NewTempEntity (void);
+}
+
+#include
+#include
+
+//#define DEFAULT_NUM_PARTICLES 8192
+#define ABSOLUTE_MIN_PARTICLES 64
+#ifdef SLIM
+#define ABSOLUTE_MAX_PARTICLES 12288
+#else
+#define ABSOLUTE_MAX_PARTICLES 6144
+#endif
+extern int decal_blood1, decal_blood2, decal_blood3, decal_q3blood, decal_burn, decal_mark, decal_glow;
+
+typedef byte col_t[4];
+
+typedef enum
+{
+ p_spark,
+ p_rayspark,
+ p_raysmoke,
+ p_smoke,
+ p_fire,
+ p_fire2,
+ p_bubble,
+ p_lavasplash,
+ p_gunblast,
+ p_chunk,
+ p_shockwave,
+ p_inferno_flame,
+ p_inferno_trail,
+ p_sparkray,
+ p_staticbubble,
+ p_trailpart,
+ p_dpsmoke,
+ p_dpfire,
+ p_teleflare,
+ p_blood1,
+ p_blood2,
+ p_blood3,
+ p_bloodcloud,
+ p_q3blood,
+ p_flame,
+ p_lavatrail,
+ p_bubble2,
+ p_rain,
+ p_streak,
+ p_streaktrail,
+ p_streakwave,
+ p_lightningbeam,
+ p_glow,
+ p_alphatrail,//R00k
+ p_torch_flame,
+ p_flare,
+ p_dot,
+ p_muzzleflash,
+ p_muzzleflash2,
+ p_muzzleflash3,
+ p_muzzleflash8,
+ p_fly,
+ p_q3blood_trail,
+ p_q3blood_decal,
+ p_q3rocketsmoke,
+ p_q3grenadesmoke,
+ p_q3explosion,
+ p_q3flame,
+ p_q3gunshot,
+ p_q3teleport,
+ num_particletypes
+} part_type_t;
+
+typedef enum
+{
+ pm_static,
+ pm_normal,
+ pm_bounce,
+ pm_die,
+ pm_nophysics,
+ pm_float,
+ pm_rain,
+ pm_streak,
+ pm_streakwave,
+} part_move_t;
+
+typedef enum
+{
+ ptex_none,
+ ptex_smoke,
+ ptex_bubble,
+ ptex_generic,
+ ptex_dpsmoke,
+ ptex_lava,
+ ptex_blueflare,
+ ptex_blood1,
+ ptex_blood2,
+ ptex_blood3,
+ ptex_lightning,
+ ptex_flame,
+ ptex_muzzleflash,
+ ptex_muzzleflash2,
+ ptex_muzzleflash3,
+ ptex_muzzleflash8,
+ ptex_bloodcloud,
+ ptex_fly,
+ ptex_q3blood,
+ ptex_q3blood_trail,
+ ptex_q3smoke,
+ ptex_q3explosion,
+ ptex_q3flame,
+ num_particletextures
+} part_tex_t;
+
+typedef enum
+{
+ pd_spark,
+ pd_sparkray,
+ pd_billboard,
+ pd_billboard_vel,
+ pd_hide,
+ pd_beam,
+ pd_q3flame,
+ pd_q3gunshot,
+ pd_q3teleport
+} part_draw_t;
+
+typedef struct particle_s
+{
+ struct particle_s *next;
+ vec3_t org, endorg;
+ col_t color;
+ float growth;
+ vec3_t vel;
+ float rotangle;
+ float rotspeed;
+ float size;
+ float start;
+ float die;
+ byte hit;
+ byte texindex;
+ byte bounces;
+} particle_t;
+
+typedef struct particle_type_s
+{
+ particle_t *start;
+ part_type_t id;
+ part_draw_t drawtype;
+ int SrcBlend;
+ int DstBlend;
+ part_tex_t texture;
+ float startalpha;
+ float grav;
+ float accel;
+ part_move_t move;
+ float custom;
+} particle_type_t;
+
+#define MAX_PTEX_COMPONENTS 8
+
+typedef struct particle_texture_s
+{
+ int texnum;
+ int components;
+ float coords[MAX_PTEX_COMPONENTS][4];
+} particle_texture_t;
+
+static float sint[7] = {0.000000, 0.781832, 0.974928, 0.433884, -0.433884, -0.974928, -0.781832};
+static float cost[7] = {1.000000, 0.623490, -0.222521, -0.900969, -0.900969, -0.222521, 0.623490};
+
+static particle_t *particles, *free_particles;
+static particle_type_t particle_types[num_particletypes];//R00k
+static int particle_type_index[num_particletypes];
+static particle_texture_t particle_textures[num_particletextures];
+
+int lightning_texture;//Vult
+float varray_vertex[16];//Vult
+void R_CalcBeamVerts (float *vert, vec3_t org1, vec3_t org2, float width);//Vult
+
+vec3_t NULLVEC = {0,0,0};//r00k
+
+static int r_numparticles;
+static vec3_t zerodir = {22, 22, 22};
+static int particle_count = 0;
+static float particle_time;
+qboolean qmb_initialized = qfalse;
+int particle_mode = 0; // 0: classic (default), 1: QMB, 2: mixed
+
+qboolean OnChange_gl_particle_count (cvar_t *var, char *string)
+{
+ float f;
+
+ f = bound(ABSOLUTE_MIN_PARTICLES, (atoi(string)), ABSOLUTE_MAX_PARTICLES);
+ Cvar_SetValue("r_particle_count", f);
+
+ QMB_ClearParticles (); // also re-allocc particles
+
+ return qtrue;
+}
+
+extern cvar_t cl_gun_offset;
+#ifdef SLIM
+cvar_t r_particle_count = {"r_particle_count", "4096", qtrue};
+#else
+cvar_t r_particle_count = {"r_particle_count", "2048", qtrue};
+#endif
+cvar_t r_bounceparticles = {"r_bounceparticles", "1",qtrue};
+cvar_t r_decal_blood = {"r_decal_blood", "1",qtrue};
+cvar_t r_decal_bullets = {"r_decal_bullets","1",qtrue};
+cvar_t r_decal_sparks = {"r_decal_sparks","1",qtrue};
+cvar_t r_decal_explosions = {"r_decal_explosions","1",qtrue};
+
+int decals_enabled;
+
+void R_CalcBeamVerts (float *vert, vec3_t org1, vec3_t org2, float width);
+
+extern cvar_t sv_gravity;
+
+static byte *ColorForParticle (part_type_t type)
+{
+ static col_t color;
+ int lambda;
+
+ switch (type)
+ {
+ case p_spark:
+ color[0] = 224 + (rand() & 31);
+ color[1] = 100 + (rand() & 31);
+ color[2] = 50;
+ break;
+
+ case p_torch_flame:
+ case p_glow:
+ color[0] = color[1] = color[2] = 255;
+ break;
+
+ case p_smoke:
+ color[0] = color[1] = color[2] = 128;
+ color[3] = 64;
+ break;
+
+ case p_q3rocketsmoke:
+ case p_q3grenadesmoke:
+ color[0] = color[1] = color[2] = 160;
+ break;
+
+ case p_q3explosion:
+ case p_q3flame:
+ case p_q3gunshot: // not used
+ case p_q3teleport: // not used
+ color[0] = color[1] = color[2] = 255;
+ break;
+
+ case p_fire:
+ color[0] = 255;
+ color[1] = 122;
+ color[2] = 62;
+ break;
+ case p_fire2:
+ color[0] = 80;
+ color[1] = 80;
+ color[2] = 80;
+ color[3] = 64;
+ break;
+
+ case p_bubble:
+ case p_bubble2:
+ case p_staticbubble:
+ color[0] = color[1] = color[2] = 192 + (rand() & 63);
+ break;
+
+ case p_teleflare:
+ case p_lavasplash:
+ color[0] = color[1] = color[2] = 128 + (rand() & 127);
+ break;
+
+ case p_gunblast:
+ color[0]= 224 + (rand() & 31);
+ color[1] = 170 + (rand() & 31);
+ color[2] = 0;
+ break;
+
+ case p_chunk:
+ color[0] = color[1] = color[2] = (32 + (rand() & 127));
+ break;
+
+ case p_shockwave:
+ color[0] = color[1] = color[2] = 64 + (rand() & 31);
+ break;
+
+ case p_inferno_flame:
+ case p_inferno_trail:
+ color[0] = 255;
+ color[1] = 77;
+ color[2] = 13;
+ break;
+
+ case p_sparkray:
+ color[0] = 255;
+ color[1] = 102;
+ color[2] = 25;
+ break;
+
+ case p_dpsmoke:
+ color[0] = color[1] = color[2] = 48 + (((rand() & 0xFF) * 48) >> 8);
+ break;
+
+ case p_dpfire:
+ lambda = rand() & 0xFF;
+ color[0] = 160 + ((lambda * 48) >> 8);
+ color[1] = 16 + ((lambda * 148) >> 8);
+ color[2] = 16 + ((lambda * 16) >> 8);
+ break;
+
+ case p_blood1:
+ case p_blood2:
+ color[0] = 30;
+ color[1] = 5;
+ color[2] = 5;
+ color[3] = 255;
+ break;
+
+ case p_blood3:
+ color[0] = (50 + (rand() & 31));
+ color[1] = color[2] = 0;
+ color[3] = 200;
+ break;
+
+ case p_q3blood:
+ case p_q3blood_trail:
+ case p_q3blood_decal:
+ color[0] = 180;
+ color[1] = color[2] = 0;
+ break;
+
+ case p_flame:
+ color[0] = 255;
+ color[1] = 100;
+ color[2] = 25;
+ color[3] = 128;
+ break;
+
+ case p_lavatrail:
+ color[0] = 255;
+ color[1] = 102;
+ color[2] = 25;
+ color[3] = 255;
+ break;
+
+ default:
+ //assert (!"ColorForParticle: unexpected type");
+ break;
+ }
+
+ return color;
+}
+
+
+#define ADD_PARTICLE_TEXTURE(_ptex, _texnum, _texindex, _components, _s1, _t1, _s2, _t2)\
+do { \
+ particle_textures[_ptex].texnum = _texnum; \
+ particle_textures[_ptex].components = _components; \
+ particle_textures[_ptex].coords[_texindex][0] = (_s1 + 1) / max_s; \
+ particle_textures[_ptex].coords[_texindex][1] = (_t1 + 1) / max_t; \
+ particle_textures[_ptex].coords[_texindex][2] = (_s2 - 1) / max_s; \
+ particle_textures[_ptex].coords[_texindex][3] = (_t2 - 1) / max_t; \
+} while(0)
+
+#define ADD_PARTICLE_TYPE(_id, _drawtype, _SrcBlend, _DstBlend, _texture, _startalpha, _grav, _accel, _move, _custom)\
+do {\
+ particle_types[count].id = (_id);\
+ particle_types[count].drawtype = (_drawtype);\
+ particle_types[count].SrcBlend = (_SrcBlend);\
+ particle_types[count].DstBlend = (_DstBlend);\
+ particle_types[count].texture = (_texture);\
+ particle_types[count].startalpha = (_startalpha);\
+ particle_types[count].grav = 9.8 * (_grav);\
+ particle_types[count].accel = (_accel);\
+ particle_types[count].move = (_move);\
+ particle_types[count].custom = (_custom);\
+ particle_type_index[_id] = count;\
+ count++;\
+} while(0)
+
+void QMB_AllocParticles (void)
+{
+ extern cvar_t r_particle_count;
+
+ r_numparticles = bound(ABSOLUTE_MIN_PARTICLES, r_particle_count.value, ABSOLUTE_MAX_PARTICLES);
+
+ //if (particles)
+ // Con_Printf("QMB_AllocParticles: internal error >particles<\n");
+
+ if(r_numparticles < 1)
+ Con_Printf("QMB_AllocParticles: internal error >num particles<\n");
+
+ // can't alloc on Hunk, using native memory
+ particles = (particle_t *) malloc (r_numparticles * sizeof(particle_t));
+}
+
+void QMB_InitParticles (void)
+{
+ int i, j, ti, count = 0, particleimage;
+ float max_s, max_t; //For ADD_PARTICLE_TEXTURE
+
+ particle_mode = pm_classic;
+
+ Cvar_RegisterVariable (&r_bounceparticles);
+ Cvar_RegisterVariable (&r_flametype);
+ Cvar_RegisterVariable (&r_decal_blood);
+ Cvar_RegisterVariable (&r_decal_bullets);
+ Cvar_RegisterVariable (&r_decal_sparks);
+ Cvar_RegisterVariable (&r_decal_explosions);
+ Cvar_RegisterVariable (&r_particle_count);
+
+ loading_num_step = loading_num_step + 24;
+
+ if (!(particleimage = loadtextureimage("textures/particles/particlefont", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+
+ loading_cur_step++;
+ strcpy(loading_name, "Particles");
+ SCR_UpdateScreen ();
+
+ max_s = max_t = 256.0;
+ ADD_PARTICLE_TEXTURE(ptex_none, 0, 0, 1, 0, 0, 0, 0);
+ ADD_PARTICLE_TEXTURE(ptex_blood1, particleimage, 0, 1, 0, 0, 64, 64);
+ ADD_PARTICLE_TEXTURE(ptex_blood2, particleimage, 0, 1, 64, 0, 128, 64);
+ ADD_PARTICLE_TEXTURE(ptex_lava, particleimage, 0, 1, 128, 0, 192, 64);
+ ADD_PARTICLE_TEXTURE(ptex_blueflare, particleimage, 0, 1, 192, 0, 256, 64);
+ ADD_PARTICLE_TEXTURE(ptex_generic, particleimage, 0, 1, 0, 96, 96, 192);
+ ADD_PARTICLE_TEXTURE(ptex_smoke, particleimage, 0, 1, 96, 96, 192, 192);
+ ADD_PARTICLE_TEXTURE(ptex_blood3, particleimage, 0, 1, 192, 96, 256, 160);
+ ADD_PARTICLE_TEXTURE(ptex_bubble, particleimage, 0, 1, 192, 160, 224, 192);
+
+ for (i=0 ; i<8 ; i++)
+ ADD_PARTICLE_TEXTURE(ptex_dpsmoke, particleimage, i, 8, i * 32, 64, (i + 1) * 32, 96);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ // load the rest of the images
+ if(!(particleimage = loadtextureimage("textures/particles/q3particlefont", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+
+ max_s = 384.0; max_t = 192.0;
+ for (i = 0, ti = 0 ; i < 2 ; i++)
+ for (j = 0 ; j < 4 ; j++, ti++)
+ ADD_PARTICLE_TEXTURE(ptex_q3explosion, particleimage, ti, 8, j * 64, i * 64, (j + 1) * 64, (i + 1) * 64);
+
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ for (i = 0 ; i < 5 ; i++)
+ ADD_PARTICLE_TEXTURE(ptex_q3blood, particleimage, i, 5, i * 64, 128, (i + 1) * 64, 192);
+ ADD_PARTICLE_TEXTURE(ptex_q3smoke, particleimage, 0, 1, 256, 0, 384, 128);
+ ADD_PARTICLE_TEXTURE(ptex_q3blood_trail, particleimage, 0, 1, 320, 128, 384, 192);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ max_s = max_t = 128.0;
+
+ if (!(particleimage = loadtextureimage("textures/particles/flame", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+
+ /*max_s = max_t = 128.0;
+ for (i = 0, ti = 0 ; i < 2 ; i++)
+ for (j = 0 ; j < 2 ; j++, ti++)
+ ADD_PARTICLE_TEXTURE(ptex_q3flame, particleimage, ti, 8, j * 64, i * 64, (j + 1) * 64, (i + 1) * 64);*/
+ max_s = max_t = 64.0;
+ ADD_PARTICLE_TEXTURE(ptex_q3flame, particleimage, 0, 1, 0, 0, 64, 64);
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ if (!(particleimage = loadtextureimage("textures/particles/inferno", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+ max_s = max_t = 256.0;
+ ADD_PARTICLE_TEXTURE(ptex_flame, particleimage, 0, 1, 0, 0, 256, 256);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ if (!(particleimage = loadtextureimage("textures/particles/zing1", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+
+ max_s = 256.0; max_t = 128.0;
+ ADD_PARTICLE_TEXTURE(ptex_lightning, particleimage, 0, 1, 0, 0, 256, 128);//R00k changed
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+ max_s = max_t = 128.0;
+
+ if (!(particleimage = loadtextureimage("textures/mzfl/mzfl0", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+ //max_s = max_t = 256.0;
+ ADD_PARTICLE_TEXTURE(ptex_muzzleflash, particleimage, 0, 1, 0, 0, 128, 128);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ if (!(particleimage = loadtextureimage("textures/mzfl/mzfl1", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+ //max_s = max_t = 256.0;
+ ADD_PARTICLE_TEXTURE(ptex_muzzleflash2, particleimage, 0, 1, 0, 0, 128, 128);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+ if (!(particleimage = loadtextureimage("textures/mzfl/mzfl2", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+ //max_s = max_t = 256.0;
+ ADD_PARTICLE_TEXTURE(ptex_muzzleflash3, particleimage, 0, 1, 0, 0, 128, 128);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ if (!(particleimage = loadtextureimage("textures/mzfl/muzzleflash8", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+ //max_s = max_t = 256.0;
+ ADD_PARTICLE_TEXTURE(ptex_muzzleflash8, particleimage, 0, 1, 0, 0, 128, 128);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ max_s = max_t = 64.0;
+ if (!(particleimage = loadtextureimage("textures/particles/bloodcloud", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+ //max_s = max_t = 256.0;
+ ADD_PARTICLE_TEXTURE(ptex_bloodcloud, particleimage, 0, 1, 0, 0, 64, 64);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+/*
+ if (!(particleimage = loadtextureimage("textures/particles/blood1", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+
+ max_s = max_t = 256.0;
+ ADD_PARTICLE_TEXTURE(ptex_blood1, particleimage, 0, 1, 0, 0, 256, 256);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ if (!(particleimage = loadtextureimage("textures/particles/blood2", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+ max_s = max_t = 256.0;
+ ADD_PARTICLE_TEXTURE(ptex_blood2, particleimage, 0, 1, 0, 0, 256, 256);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+*/
+ if (!(particleimage = loadtextureimage("textures/particles/fly", 0, 0, qfalse, GU_LINEAR)))
+ {
+ Clear_LoadingFill ();
+ return;
+ }
+ max_s = max_t = 256.0;
+ ADD_PARTICLE_TEXTURE(ptex_fly, particleimage, 0, 1, 0, 0, 256, 256);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ QMB_AllocParticles ();
+
+ ADD_PARTICLE_TYPE(p_spark, pd_spark, GU_SRC_ALPHA, GU_FIX, ptex_none, 255, -8, 0, pm_normal, 1.3);
+ ADD_PARTICLE_TYPE(p_gunblast, pd_spark, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_none, 255, 0, 0, pm_normal, 1.3);
+ ADD_PARTICLE_TYPE(p_sparkray, pd_sparkray, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_none, 255, -0, 0, pm_nophysics, 0);
+ ADD_PARTICLE_TYPE(p_fire, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_smoke, 204, 0, -2.95, pm_die, 0);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ ADD_PARTICLE_TYPE(p_fire2, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_smoke, 204, 0, -2.95, pm_die, 0);
+ ADD_PARTICLE_TYPE(p_chunk, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, -16, 0, pm_bounce, 1.475);
+ ADD_PARTICLE_TYPE(p_shockwave, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, 0, -4.85, pm_nophysics, 0);
+ ADD_PARTICLE_TYPE(p_inferno_flame, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 153, 0, 0, pm_static, 0);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ ADD_PARTICLE_TYPE(p_inferno_trail, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 204, 0, 0, pm_die, 0);
+ ADD_PARTICLE_TYPE(p_trailpart, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 230, 0, 0, pm_static, 0);
+ ADD_PARTICLE_TYPE(p_smoke, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_smoke, 140, 3, 0, pm_normal, 0);
+ ADD_PARTICLE_TYPE(p_raysmoke, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_smoke, 140, 3, 0, pm_normal, 0);
+ ADD_PARTICLE_TYPE(p_dpfire, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_dpsmoke, 144, 0, 0, pm_die, 0);
+
+ loading_cur_step++;
+ SCR_UpdateScreen ();
+
+ ADD_PARTICLE_TYPE(p_dpsmoke, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_dpsmoke, 85, 3, 0, pm_die, 0);
+ ADD_PARTICLE_TYPE(p_teleflare, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_blueflare, 255, 0, 0, pm_die, 0);
+ ADD_PARTICLE_TYPE(p_flare, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_bubble, 255, 0, 0, pm_die, 0);
+ ADD_PARTICLE_TYPE(p_fly, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_fly, 255, 0, 0, pm_die, 0);
+
+ loading_cur_step++;
+ SCR_UpdateScreen();
+
+ ADD_PARTICLE_TYPE(p_dot, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, 0, 0, pm_static, 0);
+ ADD_PARTICLE_TYPE(p_blood1, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_COLOR, ptex_blood1, 255, -20, 0, pm_die, 0);
+ ADD_PARTICLE_TYPE(p_blood2, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_blood3, 255, -45, 0, pm_normal, 0.018);//disisgonnabethegibchunks
+ ADD_PARTICLE_TYPE(p_blood3, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_blood3, 255, -30, 0, pm_normal, 0);
+
+ loading_cur_step++;
+ SCR_UpdateScreen();
+
+ ADD_PARTICLE_TYPE(p_lavasplash, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_lava, 170, 0, 0, pm_nophysics, 0);
+ ADD_PARTICLE_TYPE(p_bubble, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_bubble, 204, 8, 0, pm_float, 0);
+ ADD_PARTICLE_TYPE(p_staticbubble, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_bubble, 204, 0, 0, pm_static, 0);
+ ADD_PARTICLE_TYPE(p_flame, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 200, 10, 0, pm_die, 0);
+
+ loading_cur_step++;
+ SCR_UpdateScreen();
+
+ ADD_PARTICLE_TYPE(p_lavatrail, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_dpsmoke, 255, 3, 0, pm_normal, 0);//R00k
+ ADD_PARTICLE_TYPE(p_bubble2, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_bubble, 204, 1, 0, pm_float, 0);
+ ADD_PARTICLE_TYPE(p_glow, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 204, 0, 0, pm_die, 0);
+ ADD_PARTICLE_TYPE(p_alphatrail, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 100, 0, 0, pm_static, 0);
+
+ loading_cur_step++;
+ SCR_UpdateScreen();
+
+ ADD_PARTICLE_TYPE(p_torch_flame, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_flame, 255, 12, 0, pm_die, 0);
+ ADD_PARTICLE_TYPE(p_streak, pd_hide, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_none, 255, -64, 0, pm_streak, 1.5);
+ ADD_PARTICLE_TYPE(p_streakwave, pd_hide, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_none, 255, 0, 0, pm_streakwave, 0);
+ ADD_PARTICLE_TYPE(p_streaktrail, pd_beam, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_none, 255, 0, 0, pm_die, 0);
+
+ loading_cur_step++;
+ SCR_UpdateScreen();
+
+ ADD_PARTICLE_TYPE(p_lightningbeam, pd_beam, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_lightning, 255, 0, 0, pm_die, 0);
+ ADD_PARTICLE_TYPE(p_muzzleflash, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_muzzleflash, 255, 0, 0, pm_static, 0);
+ ADD_PARTICLE_TYPE(p_muzzleflash2, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_muzzleflash2, 255, 0, 0, pm_static, 0);
+ ADD_PARTICLE_TYPE(p_muzzleflash3, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_muzzleflash3, 255, 0, 0, pm_static, 0);
+ ADD_PARTICLE_TYPE(p_muzzleflash8, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_muzzleflash8, 220, 0, 0, pm_static, 0);
+ ADD_PARTICLE_TYPE(p_rain, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, -16, 0, pm_rain, 0);
+
+ loading_cur_step++;
+ SCR_UpdateScreen();
+
+ //shpuldeditedthis(GI_ONE_MINUS_DST_ALPHA->GL_ONE_MINUS_SRC_ALPHA) (edited one right after this comment)
+ ADD_PARTICLE_TYPE(p_bloodcloud, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_bloodcloud, 255, -2, 0, pm_normal, 0);
+ ADD_PARTICLE_TYPE(p_q3blood, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_q3blood, 255, 0, 0, pm_static, -1);
+ ADD_PARTICLE_TYPE(p_q3blood_trail, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_q3blood_trail, 255, -1.5, 0, pm_die, -1);
+ ADD_PARTICLE_TYPE(p_q3rocketsmoke, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_q3smoke, 80, 0, 0, pm_die, 0);
+
+ loading_cur_step++;
+ SCR_UpdateScreen();
+
+ ADD_PARTICLE_TYPE(p_q3grenadesmoke, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_q3smoke, 80, 0, 0, pm_die, 0);
+ ADD_PARTICLE_TYPE(p_q3explosion, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_DST_ALPHA, ptex_q3explosion, 204, 0, 0, pm_static, -1);
+ //old: ADD_PARTICLE_TYPE(p_q3flame, pd_q3flame, GU_SRC_ALPHA, GU_ONE_MINUS_DST_ALPHA, ptex_q3flame, 204, 0, 0, pm_static, -1);
+ ADD_PARTICLE_TYPE(p_q3flame, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_q3flame, 180, 0.66, 0, pm_nophysics, 0);
+ ADD_PARTICLE_TYPE(p_q3gunshot, pd_q3gunshot, GU_SRC_ALPHA, GU_ONE_MINUS_DST_ALPHA, ptex_none, 255, 0, 0, pm_static, -1);
+ ADD_PARTICLE_TYPE(p_q3teleport, pd_q3teleport, GU_SRC_ALPHA, GU_ONE_MINUS_DST_ALPHA, ptex_none, 255, 0, 0, pm_static, -1);
+
+ loading_cur_step++;
+ strcpy(loading_name, "particles");
+ SCR_UpdateScreen ();
+
+ Clear_LoadingFill ();
+
+ qmb_initialized = qtrue;
+}
+
+#define INIT_NEW_PARTICLE(_pt, _p, _color, _size, _time) \
+ _p = free_particles; \
+ free_particles = _p->next; \
+ _p->next = _pt->start; \
+ _pt->start = _p; \
+ _p->size = _size; \
+ _p->hit = 0; \
+ _p->start = cl.time; \
+ _p->die = _p->start + _time; \
+ _p->growth = 0; \
+ _p->rotspeed = 0; \
+ _p->texindex = (rand() % particle_textures[_pt->texture].components); \
+ _p->bounces = 0; \
+ VectorCopy(_color, _p->color);
+
+__inline static void AddParticle (part_type_t type, vec3_t org, int count, float size, float time, col_t col, vec3_t dir)
+{
+ byte *color;
+ int i, j, k;
+ float tempSize; //stage;
+ particle_t *p;
+ particle_type_t *pt;
+ static unsigned long q3blood_texindex = 0;
+
+ if (!qmb_initialized)
+ Sys_Error ("QMB particle added without initialization");
+
+ //assert (size > 0 && time > 0);
+
+ if (type < 0 || type >= num_particletypes)
+ Sys_Error ("AddParticle: Invalid type (%d)", type);
+
+ pt = &particle_types[particle_type_index[type]];
+
+ for (i=0 ; i < count && free_particles ; i++)
+ {
+ color = col ? col : ColorForParticle (type);
+
+ INIT_NEW_PARTICLE(pt, p, color, size, time);
+
+ switch (type)
+ {
+ case p_spark:
+ p->size = 1.175;
+ VectorCopy (org, p->org);
+ tempSize = size * 2;
+ p->vel[0] = (rand() % (int)tempSize) - ((int)tempSize / 2);
+ p->vel[1] = (rand() % (int)tempSize) - ((int)tempSize / 2);
+ p->vel[2] = (rand() % (int)tempSize) - ((int)tempSize / 2);
+ break;
+ case p_rayspark:
+ p->size = 1.175;
+ VectorCopy (org, p->org);
+ tempSize = size * 2;
+ p->vel[0] = (rand() % (int)tempSize) - ((int)tempSize/6);
+ p->vel[1] = (rand() % (int)tempSize) - ((int)tempSize/6);
+ p->vel[2] = /*(rand() % (int)tempSize) - (*/(int)tempSize;
+ break;
+ case p_raysmoke:
+ for (j=0 ; j<3 ; j++)
+ p->org[j] = org[j] + ((rand() & 31) - 16) / 2.0;
+
+ p->vel[0] = ((rand() % 10)+2);
+ p->vel[1] = ((rand() % 10)+2);
+ p->vel[2] = ((rand() % 10)+2)*5;
+ p->growth = 7.5;
+ break;
+ case p_smoke:
+ for (j=0 ; j<3 ; j++)
+ p->org[j] = org[j] + ((rand() & 31) - 16) / 2.0;
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = ((rand() % 10) - 5) / 20.0;
+ p->growth = 4.5;
+ break;
+ case p_fire:
+ VectorCopy (org, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = (rand() & 159) - 80;
+ p->org[0] = org[0] + ((rand() & 63) - 32);
+ p->org[1] = org[1] + ((rand() & 63) - 32);
+ p->org[2] = org[2] + ((rand() & 63) - 10);
+ break;
+
+ case p_fire2:
+ VectorCopy (org, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = (rand() & 199) - 100;
+ p->org[0] = org[0] + ((rand() & 99) - 50);
+ p->org[1] = org[1] + ((rand() & 99) - 50);
+ p->org[2] = org[2] + ((rand() & 99) - 18);
+ p->growth = 12;
+ break;
+
+ case p_bubble:
+ p->start += (rand() & 15) / 36.0;
+ p->org[0] = org[0] + ((rand() & 31) - 16);
+ p->org[1] = org[1] + ((rand() & 31) - 16);
+ p->org[2] = org[2] + ((rand() & 63) - 32);
+ VectorClear (p->vel);
+ break;
+
+ case p_lavasplash:
+ case p_streak:
+ case p_streakwave:
+ case p_shockwave:
+ case p_q3teleport:
+ VectorCopy (org, p->org);
+ VectorCopy (dir, p->vel);
+ break;
+
+ case p_gunblast:
+ p->size = 1;
+ VectorCopy (org, p->org);
+ p->vel[0] = (rand() & 159) - 80;
+ p->vel[1] = (rand() & 159) - 80;
+ p->vel[2] = (rand() & 159) - 80;
+ break;
+
+ case p_chunk:
+ VectorCopy (org, p->org);
+ p->vel[0] = (rand() % 40) - 20;
+ p->vel[1] = (rand() % 40) - 20;
+ p->vel[2] = (rand() % 40) - 5;
+ break;
+
+ case p_rain:
+ VectorCopy(org, p->org);
+ p->vel[0] = (rand() % 180) - 90;
+ p->vel[1] = (rand() % 180) - 90;
+ p->vel[2] = (rand() % -100 - 1200);
+ break;
+
+ case p_inferno_trail:
+ for (j=0 ; j<3 ; j++)
+ p->org[j] = org[j] + (rand() & 15) - 8;
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = (rand() & 3) - 2;
+ p->growth = -1.5;
+ break;
+
+ case p_inferno_flame:
+ VectorCopy (org, p->org);
+ VectorClear (p->vel);
+ p->growth = -30;
+ break;
+
+ case p_q3explosion:
+ p->texindex = 0;
+ VectorCopy (org, p->org);
+ VectorClear (p->vel);
+ p->growth = 50;
+ for (j=1 ; j<8 ; j++)
+ {
+ INIT_NEW_PARTICLE(pt, p, color, size, time);
+ p->size = size + j * 2;
+ p->start = cl.time + (j * time / 2.0);
+ p->die = p->start + time;
+ p->texindex = j;
+ VectorCopy (org, p->org);
+ VectorClear (p->vel);
+ p->growth = 50;
+ }
+ break;
+
+ case p_sparkray:
+ VectorCopy (org, p->endorg);
+ VectorCopy (dir, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = (rand() & 127) - 64;
+ p->growth = -16;
+ break;
+
+ case p_bloodcloud: //shpuld
+ VectorCopy (org, p->org);
+ p->vel[0] = (rand() & 39) - 20;
+ p->vel[1] = (rand() & 39) - 20;
+ p->vel[2] = (rand() & 39) - 20;
+ p->growth = 24;
+ break;
+
+ case p_staticbubble:
+ VectorCopy (org, p->org);
+ VectorClear (p->vel);
+ break;
+
+ case p_muzzleflash:
+ case p_muzzleflash2:
+ case p_muzzleflash3:
+ case p_muzzleflash8:
+ VectorCopy (org, p->org);
+ p->rotspeed = (rand() & 45) - 90;
+ //p->size = size * (rand() % 6) / 4;//r00k
+ p->size = size * (0.75 +((0.05 * (rand() % 20)) * 0.5));//blubs: resultant size range: [size * 0.75, size * 1.25)
+ break;
+
+ case p_teleflare:
+ case p_flare:
+ case p_fly:
+ VectorCopy (org, p->org);
+ VectorCopy (dir, p->vel);
+ p->growth = 1.75;
+ break;
+
+ case p_blood1:
+ p->size = size * (rand() % 2) + 0.50;//r00k
+ for (j=0 ; j<3 ; j++)
+ p->org[j] = org[j] + (rand() & 15) - 8;
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = (rand() & 63) - 32;
+ break;
+
+ case p_blood2: //shpuld
+ VectorCopy (org, p->org);
+ p->vel[0] = (rand() & 200) - 100;
+ p->vel[1] = (rand() & 200) - 100;
+ p->vel[2] = (rand() & 250) - 70;
+ //p->growth = 24;
+ break;
+
+ case p_blood3:
+ p->size = size * (rand() % 20) / 5.0;
+ VectorCopy (org, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = (rand() % 40) - 20;
+ break;
+
+ case p_q3blood:
+ p->texindex = q3blood_texindex++ % 5;
+ for (k=0 ; k<3 ; k++)
+ p->org[k] = org[k] + (rand() & 15) - 8;
+ VectorClear (p->vel);
+ for (j=1 ; j<3 ; j++)
+ {
+ INIT_NEW_PARTICLE(pt, p, color, size, time);
+ p->start = cl.time + (j * time);
+ p->die = p->start + time;
+ p->texindex = q3blood_texindex++ % 5;
+ for (k=0 ; k<3 ; k++)
+ p->org[k] = org[k] + (rand() & 15) - 8;
+ VectorClear (p->vel);
+ }
+ break;
+
+ case p_flame:
+ VectorCopy (org, p->org);
+ p->growth = -p->size / 2;
+ VectorClear (p->vel);
+ for (j=0 ; j<2 ; j++)
+ p->vel[j] = (rand() % 6) - 3;
+ break;
+
+ case p_q3flame: //shpuld
+ VectorCopy (org, p->org);
+ p->vel[0] = (rand() & 3) - 2;
+ p->vel[1] = (rand() & 3) - 2;
+ p->vel[2] = (rand() & 2);
+ p->growth = 6;
+ break;
+
+ case p_q3gunshot:
+ p->texindex = 0; // used for animation here
+ VectorCopy (org, p->org);
+ for (j=1 ; j<3 ; j++)
+ {
+ INIT_NEW_PARTICLE(pt, p, color, size, time);
+ p->start = cl.time + (j * time / 2.0);
+ p->die = p->start + time;
+ p->texindex = j + 1;
+ VectorCopy (org, p->org);
+ }
+ break;
+
+ case p_torch_flame:
+ for (j=0 ; j<3 ; j++)
+ p->org[j] = org[j] + (rand() & 3) - 2;
+ p->vel[0] = rand() % 15 - 8;
+ p->vel[1] = rand() % 15 - 8;
+ p->vel[2] = rand() % 15;
+ p->rotspeed = (rand() & 31) + 32;
+ break;
+
+ case p_dot:
+ case p_glow:
+ VectorCopy (org, p->org);
+ VectorCopy (dir, p->vel);
+ p->growth = -1.5;
+ break;
+
+ case p_streaktrail:
+ case p_lightningbeam:
+ VectorCopy(org, p->org);
+ VectorCopy(dir, p->endorg);
+ VectorClear(p->vel);
+ p->growth = -p->size/time;
+ p->bounces = color[3];
+ break;
+
+ default:
+ //assert (!"AddParticle: unexpected type");
+ break;
+ }
+ }
+}
+
+__inline static void AddParticleTrail (part_type_t type, vec3_t start, vec3_t end, float size, float time, col_t col)
+{
+ byte *color;
+ int i, j, num_particles;
+ float count, length;
+ vec3_t point, delta;
+ particle_t *p;
+ particle_type_t *pt;
+ static float rotangle = 0;
+ count = 0;
+
+ if (!qmb_initialized)
+ Sys_Error ("QMB particle added without initialization");
+
+ //assert (size > 0 && time > 0);
+
+ if (type < 0 || type >= num_particletypes)
+ Sys_Error ("AddParticle: Invalid type (%d)", type);
+
+ pt = &particle_types[particle_type_index[type]];
+
+ VectorCopy(start, point);
+ VectorSubtract(end, start, delta);
+ if (!(length = VectorLength(delta)))
+ return;
+
+ switch (type)
+ {
+
+ case p_q3blood_trail:
+ case p_q3rocketsmoke:
+ count = length / 40.0;
+ break;
+
+ case p_q3grenadesmoke:
+ count = length / 12.0;
+ break;
+
+ case p_alphatrail:
+ case p_trailpart:
+ case p_lavatrail:
+ count = length / 1.1;
+ break;
+
+ case p_blood3:
+ count = length / 8;
+ break;
+
+ case p_bubble:
+ case p_bubble2:
+ count = length / 5.0;
+ break;
+
+ case p_smoke:
+ count = length / 3.8;
+ break;
+
+ case p_dpsmoke:
+ count = length / 2.5;
+ break;
+
+ case p_dpfire:
+ count = length / 2.8;
+ break;
+
+ default:
+ //assert (!"AddParticleTrail: unexpected type");
+ break;
+ }
+
+ if (!(num_particles = (int)count))
+ num_particles = 1;
+
+ VectorScale(delta, 1.0 / num_particles, delta);
+
+ for (i=0 ; i < num_particles && free_particles ; i++)
+ {
+ color = col ? col : ColorForParticle (type);
+ INIT_NEW_PARTICLE(pt, p, color, size, time);
+
+ switch (type)
+ {
+ case p_alphatrail:
+ case p_trailpart:
+ VectorCopy (point, p->org);
+ VectorClear (p->vel);
+ p->growth = -size / time;
+ break;
+
+ case p_blood3:
+ VectorCopy (point, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->org[j] += ((rand() & 15) - 8) / 8.0;
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = ((rand() & 15) - 8) / 2.0;
+ p->size = size * (rand() % 20) / 10.0;
+ p->growth = 6;
+ break;
+
+ case p_q3blood_trail:
+ VectorCopy (point, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->org[j] += ((rand() & 15) - 8) / 8.0;
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = ((rand() & 15) - 8) / 2.0;
+ p->growth = 6;
+ break;
+
+ case p_bubble2:
+ VectorCopy(point, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = (rand() % 10) - 5;
+ break;
+
+ //R00k added
+ case p_bubble:
+ VectorCopy (point, p->org);
+
+ for (j=0 ; j<3 ; j++)
+ p->org[j] += ((rand() & 15) - 8) / 8.0;
+
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = ((rand() & 15) - 8) / 2.0;
+
+ p->size = size * (rand() % 20) / 10.0;
+ p->growth = 1;
+ break;
+
+ case p_smoke:
+ VectorCopy (point, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->org[j] += ((rand() & 7) - 4) / 8.0;
+ p->vel[0] = p->vel[1] = 0;
+ p->vel[2] = rand() & 3;
+ p->growth = 4.5;
+ p->rotspeed = (rand() & 63) + 96;
+ break;
+
+ case p_q3rocketsmoke:
+ case p_q3grenadesmoke:
+ VectorCopy (point, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->org[j] += ((rand() & 7) - 4) / 8.0;
+ VectorClear (p->vel);
+ p->growth = 30;
+ if (rotangle >= 360)
+ rotangle = 0;
+ p->rotangle = rotangle;
+ rotangle += 30;
+ break;
+
+ case p_dpsmoke:
+ VectorCopy (point, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = (rand() % 10) - 5;
+ p->growth = 3;
+ p->rotspeed = (rand() & 63) + 96;
+ break;
+
+ case p_dpfire:
+ VectorCopy (point, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = (rand() % 40) - 20;
+ break;
+
+ case p_lavatrail:
+ VectorCopy (point, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->org[j] += ((rand() & 7) - 4);
+ p->vel[0] = p->vel[1] = 0;
+ p->vel[2] = rand() & 3;
+ break;
+
+ default:
+ //assert (!"AddParticleTrail: unexpected type");
+ break;
+ }
+
+ VectorAdd(point, delta, point);
+ }
+}
+
+void QMB_ClearParticles (void)
+{
+ int i;
+
+ if (!qmb_initialized)
+ return;
+
+ free (particles); // free
+ QMB_AllocParticles (); // and alloc again
+ particle_count = 0;
+ memset (particles, 0, r_numparticles * sizeof(particle_t));
+ free_particles = &particles[0];
+
+ for (i=0 ; i+1 < r_numparticles ; i++)
+ particles[i].next = &particles[i + 1];
+ particles[r_numparticles-1].next = NULL;
+
+ for (i=0 ; i < num_particletypes ; i++)
+ particle_types[i].start = NULL;
+}
+
+inline static void QMB_UpdateParticles(void)
+{
+ int i, c;
+ float grav, bounce, frametime, distance[3];
+ vec3_t oldorg, stop, normal;
+ particle_type_t *pt;
+ particle_t *p, *kill;
+
+ if (!qmb_initialized)
+ return;
+
+ particle_count = 0;
+ frametime = fabs(cl.ctime - cl.oldtime);
+ grav = sv_gravity.value / 800.0;
+
+ for (i=0 ; istart)
+ {
+ p = pt->start;
+ while (p && p->next)
+ {
+ kill = p->next;
+ if (kill->die <= particle_time)
+ {
+ p->next = kill->next;
+ kill->next = free_particles;
+ free_particles = kill;
+ }
+ else
+ {
+ p = p->next;
+ }
+ }
+ if (pt->start->die <= particle_time)
+ {
+ kill = pt->start;
+ pt->start = kill->next;
+ kill->next = free_particles;
+ free_particles = kill;
+ }
+ }
+
+ for (p = pt->start ; (p); p = p->next)
+ {
+ if (particle_time < p->start)
+ continue;
+
+ particle_count++;
+
+ p->size += p->growth * frametime;
+
+ if (p->size <= 0)
+ {
+ p->die = 0;
+ continue;
+ }
+ VectorCopy (p->org, oldorg);
+ VectorSubtract(r_refdef.vieworg,oldorg, distance);
+
+ if (VectorLength(distance) >= r_farclip.value)
+ p->die = 0;
+
+ switch (pt->id)
+ {
+
+ case p_q3blood: // avoid alpha for q3blood
+ p->color[3] = 255;
+ break;
+
+ case p_q3explosion:
+ case p_q3gunshot:
+ if (particle_time < (p->start + (p->die - p->start) / 2.0))
+ {
+ if (pt->id == p_q3gunshot && !p->texindex)
+ p->color[3] = 255;
+ else
+ p->color[3] = pt->startalpha * ((particle_time - p->start) / (p->die - p->start) * 2);
+ }
+ else
+ {
+ p->color[3] = pt->startalpha * ((p->die - particle_time) / (p->die - p->start) * 2);
+ }
+ break;
+ case p_streaktrail://R00k
+ case p_lightningbeam:
+ p->color[3] = p->bounces * ((p->die - particle_time) / (p->die - p->start));
+ break;
+
+ //shpuld
+ case p_q3flame:
+ p->color[3] = pt->startalpha * ((p->die - particle_time) / (p->die - p->start));
+ p->color[0] = p->color[1] = p->color[2] = pt->startalpha * ((p->die - particle_time) / (p->die - p->start));
+ break;
+
+ default:
+ p->color[3] = pt->startalpha * ((p->die - particle_time) / (p->die - p->start));
+ break;
+ }
+
+ p->rotangle += p->rotspeed * frametime;
+
+ if (p->hit)
+ continue;
+
+ p->vel[2] += pt->grav * grav * frametime;
+
+ VectorScale (p->vel, 1 + pt->accel * frametime, p->vel);
+
+ switch (pt->move)
+ {
+ case pm_static:
+ break;
+
+ case pm_normal:
+ VectorCopy (p->org, oldorg);
+ VectorMA (p->org, frametime, p->vel, p->org);
+
+ if (CONTENTS_SOLID == TruePointContents(p->org))
+ {
+ p->hit = 1;
+
+ if ((pt->id == p_blood3)&&(r_decal_blood.value) && (decals_enabled) && (particle_mode))
+ {
+ TraceLineN(oldorg, p->org, stop, normal);
+
+ if ((stop != p->org)&&(VectorLength(stop)!=0))
+ {
+ vec3_t tangent;
+ VectorCopy(stop, p->org);
+ VectorCopy(normal, p->vel);
+ CrossProduct(normal,p->vel,tangent);
+ R_SpawnDecal(p->org, normal, tangent, decal_blood3, 12, 0);
+ }
+ p->die = 0;
+ }
+ VectorCopy (oldorg, p->org);
+ VectorClear (p->vel);
+ }
+
+ break;
+
+ case pm_float:
+ VectorMA (p->org, frametime, p->vel, p->org);
+ p->org[2] += p->size + 1;
+ if (!ISUNDERWATER(TruePointContents(p->org)))
+ p->die = 0;
+ p->org[2] -= p->size + 1;
+ break;
+
+ case pm_nophysics:
+ VectorMA (p->org, frametime, p->vel, p->org);
+ break;
+
+ case pm_die:
+ VectorCopy (p->org, oldorg);
+ VectorMA (p->org, frametime, p->vel, p->org);
+
+ if (CONTENTS_SOLID == TruePointContents(p->org))
+ {
+ if ((decals_enabled) && (particle_mode))
+ {
+ TraceLineN(oldorg, p->org, stop, normal);
+
+ if ((stop != p->org)&&(VectorLength(stop)!=0))
+ {
+ vec3_t tangent;
+
+ VectorCopy(stop, p->org);
+ VectorCopy(normal, p->vel);
+ CrossProduct(normal,p->vel,tangent);
+/*
+ if ((pt->id == p_blood1)&&(r_decal_blood.value))
+ {
+ R_SpawnDecal(p->org, normal, tangent, decal_blood1, 12, 0);
+ }
+ else
+ {
+ if ((pt->id == p_blood2)&&(r_decal_blood.value))
+ {
+ R_SpawnDecal(p->org, normal, tangent, decal_blood2, 12, 0);
+ }
+ }
+*/
+ if ((pt->id == p_fire || pt->id == p_dpfire) && r_decal_explosions.value)
+ R_SpawnDecal (p->org, normal, tangent, decal_burn, 32, 0);
+ else if (pt->id == p_blood1 && r_decal_blood.value)
+ R_SpawnDecal (p->org, normal, tangent, decal_blood1, 12, 0);
+ else if (pt->id == p_blood2 && r_decal_blood.value)
+ R_SpawnDecal (p->org, normal, tangent, decal_blood2, 12, 0);
+ else if (pt->id == p_q3blood_trail && r_decal_blood.value)
+ R_SpawnDecal (p->org, normal, tangent, decal_q3blood, 48, 0);
+
+ }
+ }
+ VectorCopy (oldorg, p->org);
+ VectorClear (p->vel);//R00k added Needed?
+ p->die = 0;
+ }
+
+ break;
+
+ case pm_bounce:
+ if (!r_bounceparticles.value || p->bounces)
+ {
+ VectorMA(p->org, frametime, p->vel, p->org);
+ if (CONTENTS_SOLID == TruePointContents(p->org))
+ {
+ p->die = 0;
+ }
+ }
+ else
+ {
+ VectorCopy (p->org, oldorg);
+ VectorMA (p->org, frametime, p->vel, p->org);
+
+ if (CONTENTS_SOLID == TruePointContents(p->org))
+ {
+ if (TraceLineN(oldorg, p->org, stop, normal))
+ {
+ VectorCopy (stop, p->org);
+ bounce = -pt->custom * DotProduct(p->vel, normal);
+ VectorMA(p->vel, bounce, normal, p->vel);
+ p->bounces++;
+ }
+ }
+
+ }
+ break;
+
+ case pm_streak:
+ VectorCopy(p->org, oldorg);
+ VectorMA(p->org, frametime, p->vel, p->org);
+ if (CONTENTS_SOLID == TruePointContents(p->org))
+ {
+ if (TraceLineN(oldorg, p->org, stop, normal))
+ {
+ VectorCopy(stop, p->org);
+ bounce = -pt->custom * DotProduct(p->vel, normal);
+ VectorMA(p->vel, bounce, normal, p->vel);
+ }
+ }
+
+ AddParticle (p_streaktrail, oldorg, 1, p->size, 0.2, p->color, p->org);
+
+ if (!VectorLength(p->vel))
+ p->die = 0;
+ break;
+
+ case pm_rain:
+ VectorCopy(p->org, oldorg);
+ VectorMA(p->org, frametime, p->vel, p->org);
+
+ VectorSubtract(r_refdef.vieworg,oldorg, distance);
+
+ if (VectorLength(distance) < r_farclip.value)
+ {
+ if ((rand()%10+1 > 6))
+ AddParticle (p_streaktrail, oldorg, 1, ((rand() % 1) + 0.5), 0.2, p->color, p->org);
+
+ c = TruePointContents(p->org);
+
+ if ((CONTENTS_SOLID == c) || (ISUNDERWATER(c)))
+ {
+ VectorClear (p->vel);
+ p->die = 0;
+ }
+ }
+ break;
+
+ case pm_streakwave:
+ VectorCopy(p->org, oldorg);
+ VectorMA(p->org, frametime, p->vel, p->org);
+ AddParticle (p_streaktrail, oldorg, 1, p->size, 0.5, p->color, p->org);
+ p->vel[0] = 19 * p->vel[0] / 20;
+ p->vel[1] = 19 * p->vel[1] / 20;
+ p->vel[2] = 19 * p->vel[2] / 20;
+ break;
+
+ default:
+ //assert (!"QMB_UpdateParticles: unexpected pt->move");
+ break;
+ }
+ }
+ }
+}
+
+//from darkplaces engine - finds which corner of a particle goes where, so I don't have to :D
+void R_CalcBeamVerts (float *vert, vec3_t org1, vec3_t org2, float width)
+{
+ vec3_t right1, right2, diff, normal;
+
+ VectorSubtract (org2, org1, normal);
+ VectorNormalize (normal);
+
+ //width = width / 2;
+ // calculate 'right' vector for start
+ VectorSubtract (r_origin, org1, diff);
+ VectorNormalize (diff);
+ CrossProduct (normal, diff, right1);
+
+ // calculate 'right' vector for end
+ VectorSubtract (r_origin, org2, diff);
+ VectorNormalize (diff);
+ CrossProduct (normal, diff, right2);
+
+ vert[ 0] = org1[0] + width * right1[0];
+ vert[ 1] = org1[1] + width * right1[1];
+ vert[ 2] = org1[2] + width * right1[2];
+ vert[ 4] = org1[0] - width * right1[0];
+ vert[ 5] = org1[1] - width * right1[1];
+ vert[ 6] = org1[2] - width * right1[2];
+ vert[ 8] = org2[0] - width * right2[0];
+ vert[ 9] = org2[1] - width * right2[1];
+ vert[10] = org2[2] - width * right2[2];
+ vert[12] = org2[0] + width * right2[0];
+ vert[13] = org2[1] + width * right2[1];
+ vert[14] = org2[2] + width * right2[2];
+}
+
+#define DRAW_PARTICLE_BILLBOARD(_ptex, _p, _coord) \
+ sceGumPushMatrix (); \
+ \
+ const ScePspFVector3 translation = \
+ { \
+ _p->org[0], _p->org[1], _p->org[2] \
+ }; \
+ sceGumTranslate(&translation); \
+ \
+ const ScePspFVector3 scale = \
+ { \
+ _p->size, _p->size, _p->size \
+ }; \
+ sceGumScale(&scale); \
+ \
+ if (_p->rotspeed || pt->id == p_q3rocketsmoke || pt->id == p_q3grenadesmoke)\
+ { \
+ const ScePspFVector3 rotation = \
+ { \
+ vpn[0] * (GU_PI / 180.0f), \
+ vpn[1] * (GU_PI / 180.0f), \
+ vpn[2] * (GU_PI / 180.0f) \
+ }; \
+ sceGumRotateZYX(&rotation); \
+ } \
+ sceGuColor(GU_RGBA(_p->color[0], _p->color[1], _p->color[2], _p->color[3]));\
+ \
+ sceGumUpdateMatrix(); \
+ \
+ struct vertex \
+ { \
+ float u, v; \
+ float x, y, z; \
+ }; \
+ vertex* const df = static_cast(sceGuGetMemory(sizeof(vertex) * 4));\
+ \
+ df[0].u = _ptex->coords[_p->texindex][0]; df[0].v = _ptex->coords[_p->texindex][3];\
+ df[0].x = _coord[0][0]; df[0].y = _coord[0][1]; df[0].z = _coord[0][2]; \
+ df[1].u = _ptex->coords[_p->texindex][0]; df[1].v = _ptex->coords[_p->texindex][1];\
+ df[1].x = _coord[1][0]; df[1].y = _coord[1][1]; df[1].z = _coord[1][2]; \
+ df[2].u = _ptex->coords[_p->texindex][2]; df[2].v = _ptex->coords[_p->texindex][1];\
+ df[2].x = _coord[2][0]; df[2].y = _coord[2][1]; df[2].z = _coord[2][2]; \
+ df[3].u = _ptex->coords[_p->texindex][2]; df[3].v = _ptex->coords[_p->texindex][3];\
+ df[3].x = _coord[3][0]; df[3].y = _coord[3][1]; df[3].z = _coord[3][2]; \
+ sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 4, 0, df); \
+ sceGuColor(GU_RGBA(0xff,0xff,0xff,0xff)); \
+ \
+ sceGumPopMatrix (); \
+ sceGumUpdateMatrix();
+
+//extern ScePspFMatrix4 r_world_matrix[16];
+void QMB_DrawParticles (void)
+{
+ int j, i;
+ vec3_t up, right, billboard[4], velcoord[4], neworg;
+ particle_t *p;
+ particle_type_t *pt;
+ particle_texture_t *ptex;
+
+ float varray_vertex[16];
+ vec3_t distance;
+
+ if (!qmb_initialized)
+ return;
+
+ particle_time = cl.time;
+
+ if (!cl.paused)
+ QMB_UpdateParticles ();
+
+ VectorAdd (vup, vright, billboard[2]);
+ VectorSubtract (vright, vup, billboard[3]);
+ VectorNegate (billboard[2], billboard[0]);
+ VectorNegate (billboard[3], billboard[1]);
+
+ sceGuDepthMask (GU_TRUE);
+ sceGuEnable (GU_BLEND);
+ sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA);
+ sceGuShadeModel (GU_SMOOTH);
+
+ for (i = 0 ; i < num_particletypes ; i++)
+ {
+ pt = &particle_types[i];
+
+ if (!pt->start)
+ continue;
+
+ sceGuBlendFunc (GU_ADD, pt->SrcBlend, pt->DstBlend, 0, 0xFFFFFFFF); //edited fixed value to 1 from 0 - shpuld
+
+ switch (pt->drawtype)
+ {
+ case pd_hide:
+ break;
+ case pd_beam:
+ ptex = &particle_textures[pt->texture];
+ GL_Bind (ptex->texnum);
+ for (p = pt->start ; p ; p = p->next)
+ {
+ if (particle_time < p->start || particle_time >= p->die)
+ continue;
+
+ VectorSubtract(r_refdef.vieworg, p->org, distance);
+ if (VectorLength(distance) > r_farclip.value)
+ continue;
+ // Allocate the vertices.
+ struct vertex
+ {
+ float u, v;
+ float x, y, z;
+ };
+
+ vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * 4));
+
+ sceGuColor(GU_RGBA(p->color[0], p->color[1], p->color[2], p->color[3]));
+ R_CalcBeamVerts (varray_vertex, p->org, p->endorg, p->size / 3.0);
+
+ out[0].u = 1;
+ out[0].v = 0;
+
+ out[0].x = varray_vertex[0];
+ out[0].y = varray_vertex[1];
+ out[0].z = varray_vertex[2];
+
+ out[1].u = 1;
+ out[1].v = 1;
+
+ out[1].x = varray_vertex[4];
+ out[1].y = varray_vertex[5];
+ out[1].z = varray_vertex[6];
+
+ out[2].u = 0;
+ out[2].v = 1;
+
+ out[2].x = varray_vertex[8];
+ out[2].y = varray_vertex[9];
+ out[2].z = varray_vertex[10];
+
+ out[3].u = 0;
+ out[3].v = 0;
+
+ out[3].x = varray_vertex[12];
+ out[3].y = varray_vertex[13];
+ out[3].z = varray_vertex[14];
+
+ sceGuDrawArray(GU_TRIANGLE_FAN,GU_TEXTURE_32BITF | GU_VERTEX_32BITF,4, 0, out);
+ sceGuColor(GU_RGBA(0xff,0xff,0xff,0xff)); //return to normal color
+ }
+ break;
+ case pd_spark:
+ sceGuDisable (GU_TEXTURE_2D);
+ for (p = pt->start ; p ; p = p->next)
+ {
+ if (particle_time < p->start || particle_time >= p->die)
+ continue;
+
+ VectorSubtract(r_refdef.vieworg, p->org, distance);
+ if (VectorLength(distance) > r_farclip.value)
+ continue;
+
+ struct vertex
+ {
+ vec3_t xyz;
+ };
+
+ vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * 9));
+
+ sceGuColor(GU_RGBA(p->color[0], p->color[1], p->color[2], p->color[3]));
+
+ for (int gh=0 ; gh<3 ; gh++)
+ out[0].xyz[gh] = p->org[gh];
+
+ sceGuColor(GU_RGBA(p->color[0] >> 1, p->color[1] >> 1, p->color[2] >> 1, p->color[3] >> 1));
+
+ int vt = 1;
+
+ for (j=7; j>=0 ; j--)
+ {
+
+ for (int k=0 ; k<3 ; k++)
+ out[vt].xyz[k] = p->org[k] - p->vel[k] / 8 + vright[k] * cost[1%7] * p->size + vup[k] * sint[j%7] * p->size;
+ vt = vt + 1;
+ }
+ sceGuDrawArray(GU_TRIANGLE_FAN, GU_VERTEX_32BITF, 9, 0, out);
+
+ sceGuColor(GU_RGBA(0xff,0xff,0xff,0xff)); //return to normal color
+ }
+ sceGuEnable (GU_TEXTURE_2D);
+ break;
+ case pd_sparkray:
+ sceGuDisable (GU_TEXTURE_2D);
+ for (p = pt->start ; p ; p = p->next)
+ {
+ if (particle_time < p->start || particle_time >= p->die)
+ continue;
+
+ VectorSubtract(r_refdef.vieworg, p->org, distance);
+ if (VectorLength(distance) > r_farclip.value)
+ continue;
+
+ if (!TraceLineN(p->endorg, p->org, neworg, NULLVEC))
+ VectorCopy(p->org, neworg);
+
+ //R00k added -start-
+ //sceGuEnable (GU_BLEND);
+ //p->color[3] = bound(0, 0.3, 1) * 255;
+ //R00k added -end-
+ //glColor4ubv (p->color);
+
+ struct vertex
+ {
+ vec3_t xyz;
+ };
+
+ vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * 9));
+
+ sceGuColor(GU_RGBA(p->color[0], p->color[1], p->color[2], p->color[3]));
+
+ for (int gh=0 ; gh<3 ; gh++)
+ out[0].xyz[gh] = p->endorg[gh];
+
+
+ sceGuColor(GU_RGBA(p->color[0] >> 1, p->color[1] >> 1, p->color[2] >> 1, p->color[3] >> 1));
+
+ int vt = 1;
+
+ for (j=7 ; j>=0 ; j--)
+ {
+ for (int k=0 ; k<3 ; k++)
+ out[vt].xyz[k] = neworg[k] + vright[k] * cost[j%7] * p->size + vup[k] * sint[j%7] * p->size;
+
+ vt = vt + 1;
+ }
+ sceGuDrawArray(GU_TRIANGLE_FAN, GU_VERTEX_32BITF, 9, 0, out);
+
+ sceGuColor(GU_RGBA(0xff,0xff,0xff,0xff)); //return to normal color
+
+ }
+ sceGuEnable (GU_TEXTURE_2D);
+ break;
+
+ case pd_billboard:
+ ptex = &particle_textures[pt->texture];
+ GL_Bind (ptex->texnum);
+
+ for (p = pt->start ; p ; p = p->next)
+ {
+ if (particle_time < p->start || particle_time >= p->die)
+ continue;
+
+ for (j = 0 ; j < cl.maxclients ; j++)
+ {
+ if (pt->custom != -1 && VectorSupCompare(p->org, cl_entities[1+j].origin, 40))
+ {
+ p->die = 0;
+ continue;
+ }
+ }
+
+ if(pt->texture == ptex_muzzleflash || pt->texture == ptex_muzzleflash2 || pt->texture == ptex_muzzleflash3)
+ sceGuDepthRange(0, 19660);
+
+ DRAW_PARTICLE_BILLBOARD(ptex, p, billboard);
+
+ if(pt->texture == ptex_muzzleflash || pt->texture == ptex_muzzleflash2 || pt->texture == ptex_muzzleflash3)
+ sceGuDepthRange(0, 65535);
+ }
+ break;
+
+ case pd_billboard_vel:
+ ptex = &particle_textures[pt->texture];
+ GL_Bind (ptex->texnum);
+ for (p = pt->start ; p ; p = p->next)
+ {
+ if (particle_time < p->start || particle_time >= p->die)
+ continue;
+
+ VectorCopy (p->vel, up);
+ CrossProduct (vpn, up, right);
+ VectorNormalizeFast (right);
+ VectorScale (up, pt->custom, up);
+
+ VectorAdd (up, right, velcoord[2]);
+ VectorSubtract (right, up, velcoord[3]);
+ VectorNegate (velcoord[2], velcoord[0]);
+ VectorNegate (velcoord[3], velcoord[1]);
+ DRAW_PARTICLE_BILLBOARD(ptex, p, velcoord);
+ }
+ break;
+
+ case pd_q3flame:
+ ptex = &particle_textures[pt->texture];
+ GL_Bind (ptex->texnum);
+ for (p = pt->start ; p ; p = p->next)
+ {
+ float varray_vertex[16];
+ float xhalf = p->size / 2.0, yhalf = p->size;
+ // vec3_t org, v, end, normal;
+
+ if (particle_time < p->start || particle_time >= p->die)
+ continue;
+
+ sceGuDisable (GU_CULL_FACE);
+
+ for (j=0 ; j<2 ; j++)
+ {
+ sceGumPushMatrix ();
+
+ const ScePspFVector3 translation =
+ {
+ p->org[0], p->org[1], p->org[2]
+ };
+ sceGumTranslate(&translation);
+
+ //glRotatef (!j ? 45 : -45, 0, 0, 1);
+
+ sceGumRotateZ(!j ? 45 : -45 * (GU_PI / 180.0f));
+
+ sceGuColor(GU_RGBA(p->color[0], p->color[1], p->color[2], p->color[3]));
+
+ sceGumUpdateMatrix();
+
+ // sigh. The best would be if the flames were always orthogonal to their surfaces
+ // but I'm afraid it's impossible to get that work (w/o progs modification of course)
+ varray_vertex[0] = 0;
+ varray_vertex[1] = xhalf;
+ varray_vertex[2] = -yhalf;
+ varray_vertex[4] = 0;
+ varray_vertex[5] = xhalf;
+ varray_vertex[6] = yhalf;
+ varray_vertex[8] = 0;
+ varray_vertex[9] = -xhalf;
+ varray_vertex[10] = yhalf;
+ varray_vertex[12] = 0;
+ varray_vertex[13] = -xhalf;
+ varray_vertex[14] = -yhalf;
+
+ struct vertex
+ {
+ float u, v;
+ float x, y, z;
+ };
+
+ vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * 4));
+
+ out[0].u = ptex->coords[p->texindex][0];
+ out[0].v = ptex->coords[p->texindex][3];
+ out[0].x = varray_vertex[0];
+ out[0].y = varray_vertex[1];
+ out[0].z = varray_vertex[2];
+
+
+ out[1].u = ptex->coords[p->texindex][0];
+ out[1].v = ptex->coords[p->texindex][1];
+ out[1].x = varray_vertex[4];
+ out[1].y = varray_vertex[5];
+ out[1].z = varray_vertex[6];
+
+
+ out[2].u = ptex->coords[p->texindex][2];
+ out[2].v = ptex->coords[p->texindex][1];
+ out[2].x = varray_vertex[8];
+ out[2].y = varray_vertex[9];
+ out[2].z = varray_vertex[10];
+
+
+ out[3].u = ptex->coords[p->texindex][2];
+ out[3].v = ptex->coords[p->texindex][3];
+ out[3].x = varray_vertex[12];
+ out[3].y = varray_vertex[13];
+ out[3].z = varray_vertex[14];
+
+ sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 4, 0, out);
+
+ sceGumPopMatrix ();
+ sceGumUpdateMatrix();
+ }
+ sceGuEnable (GU_CULL_FACE);
+ sceGuColor(GU_RGBA(0xff,0xff,0xff,0xff)); //return to normal color
+ }
+ break;
+
+ case pd_q3gunshot:
+ for (p = pt->start ; p ; p = p->next)
+ QMB_Q3Gunshot (p->org, (int)p->texindex, (float)p->color[3] / 255.0);
+ break;
+
+ case pd_q3teleport:
+ for (p = pt->start ; p ; p = p->next)
+ QMB_Q3Teleport (p->org, (float)p->color[3] / 255.0);
+ break;
+ default:
+ //assert (!"QMB_DrawParticles: unexpected drawtype");
+ break;
+ }
+ }
+
+ sceGuDepthMask (GU_FALSE);
+ sceGuDisable (GU_BLEND);
+ sceGuBlendFunc (GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+ sceGuShadeModel (GU_FLAT);
+}
+
+void QMB_Shockwave_Splash(vec3_t org, int radius)
+{
+ float theta;
+ vec3_t angle;
+
+ angle[2] = 0;
+
+ for (theta = 0; theta < 6.283185307179586476925286766559; theta += 0.069813170079773183076947630739545)
+ {
+ #ifdef PSP_VFPU
+ angle[0] = vfpu_cosf(theta) * radius;
+ angle[1] = vfpu_sinf(theta) * radius;
+ #else
+ angle[0] = cos(theta) * radius;
+ angle[1] = sin(theta) * radius;
+ #endif
+ AddParticle(p_shockwave, org, 1, 2, 0.625f, NULL, angle);
+ }
+}
+
+extern sfx_t *cl_sfx_thunder;
+void QMB_LetItRain(void)
+{
+ int i;
+ msurface_t *surf;
+ glpoly_t *p;
+ col_t color;
+ float distance[3], point[3];
+ float frametime = fabs(cl.ctime - cl.oldtime);
+
+ color[0] = color[1] = color[2] = + (rand() % 25 + 15);
+ color[2] += 5;
+
+ if (!qmb_initialized)
+ return;
+
+ if (!particle_mode)
+ return;
+
+ if (frametime)
+ {
+ for (i = 0, surf = cl.worldmodel->surfaces; i < cl.worldmodel->numsurfaces; i++, surf++)
+ {
+ if (!(surf->flags & SURF_DRAWSKY)) continue;
+
+ for (p = surf->polys; p; p = p->next)
+ {
+ VectorSubtract(r_refdef.vieworg,p->midpoint, distance);
+
+ if (VectorLength(distance) > r_farclip.value)
+ continue;
+
+ //R00k let's make some thunder...
+ /*if (cl.time > cl.thundertime)
+ {
+ S_StartSound (-1, 0, cl_sfx_thunder, p->midpoint, 1, 1);
+ cl.thundertime = cl.time + (rand()% 60 + 1);
+ }*/
+
+ point[0] = p->midpoint[0] + (rand() % 80 - 40);
+ point[1] = p->midpoint[1] + (rand() % 80 - 40);
+ point[2] = p->midpoint[2];
+
+ AddParticle(p_rain, point, 1, 0.75, 5, color, zerodir);
+ }
+ }
+ }
+};
+
+//R00k: revamped to coincide with classic particle style...
+
+void QMB_ParticleExplosion (vec3_t org)
+{
+ if (r_explosiontype.value == 2)//no explosion what so ever
+ return;
+
+ if (ISUNDERWATER(TruePointContents(org)))
+ {
+ AddParticle (p_bubble, org, 6, 3.0, 2.5, NULL, zerodir);
+ AddParticle (p_bubble, org, 4, 2.35, 2.5, NULL, zerodir);
+
+ if (r_part_explosions.value == 2)
+ {
+ AddParticle (p_q3explosion, org, 1, 36, 0.2, NULL, zerodir);
+ }
+ else
+ {
+ if (r_part_explosions.value == 2)
+ {
+ AddParticle (p_q3explosion, org, 1, 36, 0.2, NULL, zerodir);
+ }
+ else
+ {
+ AddParticle (p_fire, org, 16, 120, 1, NULL, zerodir);
+ if (r_explosiontype.value != 1)
+ {
+ AddParticle (p_spark, org, 50, 250, 0.925f, NULL, zerodir);
+ AddParticle (p_spark, org, 25, 150, 0.925f, NULL, zerodir);
+ }
+ }
+ }
+ }
+ else
+
+ {
+ /* original
+ if (r_explosiontype.value != 3)
+ {
+ if (r_flametype.value < 1)//R00k
+ {
+ AddParticle (p_fire2, org, 16, 18, 1, NULL, zerodir);
+ }
+ else
+ {
+ AddParticle (p_fire, org, 16, 18, 1, NULL, zerodir);
+ }
+ }
+
+ if ((r_explosiontype.value == 0) || (r_explosiontype.value == 1) || (r_explosiontype.value == 3))
+ {
+ AddParticle (p_spark, org, 50, 250, 0.925f, NULL, zerodir);
+ AddParticle (p_spark, org, 25, 150, 0.925f, NULL, zerodir);
+ }
+ */
+
+ //shpuld
+ AddParticle (p_fire, org, 10, 40, 0.5, NULL, zerodir);
+ AddParticle (p_fire2, org, 14, 36, 1.8, NULL, zerodir);
+ }
+}
+
+void d8to24col (col_t colourv, int colour)
+{
+ byte *colourByte;
+
+ colourByte = (byte *)&d_8to24table[colour];
+ colourv[0] = colourByte[0];
+ colourv[1] = colourByte[1];
+ colourv[2] = colourByte[2];
+}
+
+__inline static void AddColoredParticle (part_type_t type, vec3_t org, int count, float size, float time, int colorStart, int colorLength, vec3_t dir)
+{
+ col_t color;
+ int i, j, colorMod = 0;
+ float tempSize;
+ particle_t *p;
+ particle_type_t *pt;
+
+
+ if (!qmb_initialized)
+ Sys_Error ("QMB particle added without initialization");
+
+ //assert (size > 0 && time > 0);
+
+ if (type < 0 || type >= num_particletypes)
+ Sys_Error ("AddColoredParticle: Invalid type (%d)", type);
+
+ pt = &particle_types[particle_type_index[type]];
+
+ for (i=0 ; i < count && free_particles ; i++)
+ {
+ d8to24col (color, colorStart + (colorMod % colorLength));
+ colorMod++;
+ INIT_NEW_PARTICLE(pt, p, color, size, time);
+
+ switch (type)
+ {
+ case p_spark:
+ p->size = 1.175;
+ VectorCopy (org, p->org);
+ tempSize = size * 2;
+ p->vel[0] = (rand() % (int)tempSize) - ((int)tempSize / 4);
+ p->vel[1] = (rand() % (int)tempSize) - ((int)tempSize / 4);
+ p->vel[2] = (rand() % (int)tempSize) - ((int)tempSize / 6);
+ break;
+
+ case p_fire:
+ VectorCopy (org, p->org);
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = ((rand() % 160) - 80) * (size / 25.0);
+ break;
+
+ default:
+ //assert (!"AddColoredParticle: unexpected type");
+ break;
+ }
+ }
+}
+
+void QMB_ColorMappedExplosion (vec3_t org, int colorStart, int colorLength)
+{
+
+ if (ISUNDERWATER(TruePointContents(org)))
+ {
+ //AddColoredParticle (p_fire, org, 16, 18, 1, colorStart, colorLength, zerodir);
+ AddParticle (p_bubble, org, 6, 3.0, 2.5, NULL, zerodir);
+ AddParticle (p_bubble, org, 4, 2.35, 2.5, NULL, zerodir);
+ if (r_explosiontype.value != 2)
+ {
+ AddColoredParticle (p_spark, org, 50, 100, 0.5, colorStart, colorLength, zerodir);
+ AddColoredParticle (p_spark, org, 25, 60, 0.5, colorStart, colorLength, zerodir);
+ }
+ }
+ else
+ {
+ if (r_flametype.value < 1)//R00k
+ {
+ AddColoredParticle (p_fire2, org, 16, 18, 1, colorStart, colorLength, zerodir);
+ }
+ else
+ {
+ AddColoredParticle (p_fire, org, 16, 18, 1, colorStart, colorLength, zerodir);
+ }
+
+ if (r_explosiontype.value < 2)
+ {
+ AddColoredParticle (p_spark, org, 50, 250, 0.625f, colorStart, colorLength, zerodir);
+ if (r_explosiontype.value < 1)
+ AddColoredParticle (p_spark, org, 25, 150, 0.625f, colorStart, colorLength, zerodir);
+ }
+ }
+}
+
+/* Original
+
+void QMB_Blood_Splat(part_type_t type, vec3_t org) //R00k :)
+{
+ int j;
+ col_t color;
+ vec3_t neworg, angle;
+
+ VectorClear (angle);
+
+ color[0]=100;
+ color[1]=0;
+ color[2]=0;
+ color[3]=255;
+
+ AddParticle(p_bloodcloud, org, 1, 6, 0.5, color, zerodir);
+
+ for (j=0 ; j<4 ; j++)
+ {
+ AngleVectors (angle, NULLVEC, NULLVEC, neworg);
+ VectorMA (org, 70, neworg, neworg);
+
+ AddParticle (type, org, 5, 1, 2, color, neworg);
+ angle[1] += 360 / 4;
+ }
+}*/
+
+void QMB_Blood_Splat(part_type_t type, vec3_t org) //Shpuldified
+{
+ int j;
+ col_t color;
+ vec3_t neworg, angle;
+
+ VectorClear (angle);
+
+ color[0]=100;
+ color[1]=0;
+ color[2]=0;
+ color[3]=255;
+
+ if(type == p_blood1)
+ {
+ AddParticle(p_bloodcloud, org, 3, 5, 0.3, color, zerodir);
+ }
+
+ else if(type == p_blood2)
+ {
+ AddParticle(p_bloodcloud, org, 3, 7, 0.3, color, zerodir);
+ color[0] = 40;
+ AddParticle(p_blood2, org, 16, 3, 1.0, color, zerodir);
+ }
+
+ else //p_blood3, trail?
+ {
+ AddParticle(p_bloodcloud, org, 3, 5, 0.6, color, zerodir);
+ for (j=0 ; j<4 ; j++)
+ {
+ AngleVectors (angle, NULLVEC, NULLVEC, neworg);
+ VectorMA (org, 70, neworg, neworg);
+
+ AddParticle (type, org, 5, 1, 2, color, neworg);
+ angle[1] += 360 / 4;
+ }
+ }
+}
+
+void QMB_RunParticleEffect (vec3_t org, vec3_t dir, int col, int count)
+{
+ col_t color;
+ vec3_t neworg, newdir;
+ int i, j, particlecount;
+ int contents;//R00k Added
+
+ if (col == 73)
+ {
+ QMB_Blood_Splat(p_blood1, org);
+ return;
+ }
+ else if (col == 225)
+ {
+ QMB_Blood_Splat(p_blood2, org);
+ return;
+ }
+ else if (col == 20 && count == 30)
+ {
+ color[0] = color[2] = 51;
+ color[1] = 255;
+ AddParticle (p_chunk, org, 1, 1, 0.75, color, zerodir);
+ AddParticle (p_spark, org, 12, 75, 0.4, color, zerodir);
+ return;
+ }
+ else if (col == 226 && count == 20)
+ {
+ color[0] = 230;
+ color[1] = 204;
+ color[2] = 26;
+ AddParticle (p_chunk, org, 1, 1, 0.75, color, zerodir);
+ AddParticle (p_spark, org, 12, 75, 0.4, color, zerodir);
+ return;
+ }
+ else if(col == 111) //we will use this color for flames
+ {
+ color[0] = color[1] = color[2] = 255;
+ AddParticle (p_q3flame, org, 3, 3, 2, color, dir);
+ return;
+ }
+ else if(col == 112) //we will use this color for big flames
+ {
+ color[0] = color[1] = color[2] = 255;
+ AddParticle (p_q3flame, org, 3, 6, 2, color, dir);
+ return;
+ }
+
+ switch (count)
+ {
+ case 9:
+ case 10://nailgun
+ {
+ color[0] = 200; color[1] = 200; color[2] = 125;
+
+ if (r_part_spikes.value == 2)
+ AddParticle (p_q3gunshot, org, 1, 1, 0.3, NULL, zerodir);
+ else
+ AddParticle (p_spark, org, 6, 70, 0.6, NULL, zerodir);
+
+ AddParticle (p_chunk, org, 3, 1, 0.75, NULL, zerodir);
+
+ contents = TruePointContents (org);//R00k Added
+
+ if (ISUNDERWATER(contents))//R00k
+ {
+ AddParticle (p_bubble, org, 1, 2, 0.825f + ((rand() % 10) - 5) / 40.0, NULL, zerodir);
+ }
+ else
+
+ {
+ AddParticle (p_smoke, org, 1, 4, 0.825f + ((rand() % 10) - 5) / 40.0, NULL, zerodir);
+ }
+ }
+ break;
+
+ case 20://super nailgun
+ color[0] = 200; color[1] = 200; color[2] = 125;
+
+ if (r_part_spikes.value == 2)
+ AddParticle (p_q3gunshot, org, 1, 1, 0.3, NULL, zerodir);
+ else
+ //AddParticle (p_spark, org, 2, 85, 0.4, color, zerodir);
+ AddParticle (p_spark, org, 22, 100, 0.2, NULL, zerodir);
+
+ //AddParticle (p_chunk, org, 6, 2, 0.75, NULL, zerodir);
+
+ contents = TruePointContents (org);//R00k Added
+
+ if (ISUNDERWATER(contents))//R00k
+ {
+ AddParticle (p_bubble, org, 1, 2, 0.825f + ((rand() % 10) - 5) / 40.0, NULL, zerodir);
+ }
+ else
+
+ {
+ AddParticle (p_smoke, org, 3, 12, 1.225f + ((rand() % 10) - 5) / 40.0, NULL, zerodir);
+ }
+ break;
+
+ case 24:// gunshot
+ if (r_part_gunshots.value == 2)
+ {
+ AddParticle (p_q3gunshot, org, 1, 1, 0.3, NULL, zerodir);
+ }
+ else
+ {
+ particlecount = count >> 1;
+ AddParticle (p_gunblast, org, 1, 1.04, 0.2, NULL, zerodir);
+ for (i=0 ; i>3) ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ neworg[j] = org[j] + ((rand() % 24) - 12);
+ newdir[0] = dir[0] * (10 + (rand() % 5));
+ newdir[1] = dir[1] * (10 + (rand() % 5));
+ newdir[2] = dir[2] * 15;
+ d8to24col (color, (col & ~7) + (rand() & 7));
+ AddParticle (p_glow, neworg, 1, 3.5, 0.5 + 0.1 * (rand() % 3), color, newdir);
+ }
+ return;
+ }
+ }
+*/
+ particlecount = fmax(1, count>>1);
+ for (i=0 ; iv.view_ofs[2] >= 0)
+ org[2] -= 32 - sv_player->v.view_ofs[2];
+ else
+ org[2] -= 32 + abs(sv_player->v.view_ofs[2]);
+ /*if (sv_player->v.view_ofs[2] == 8) {
+ org[2] -= 24;
+ } else if (sv_player->v.view_ofs[2] == -10) {
+ org[2] -= 42;
+ }*/
+
+ float size;
+
+ if(!(ISUNDERWATER(TruePointContents(org))))
+ {
+ size = sv_player->v.Flash_Size;
+
+ if(size == 0 || cl.stats[STAT_ZOOM] == 2)
+ return;
+ switch(rand() % 3 + 1)
+ {
+ case 1:
+ AddParticle (p_muzzleflash, org, 1, size, 0.04 * frametime, color, zerodir);
+ break;
+ case 2:
+ AddParticle (p_muzzleflash2, org, 1, size, 0.04 * frametime, color, zerodir);
+ break;
+ case 3:
+ AddParticle (p_muzzleflash3, org, 1, size, 0.04 * frametime, color, zerodir);
+ break;
+ default:
+ AddParticle (p_muzzleflash, org, 1, size, 0.04 * frametime, color, zerodir);
+ break;
+ }
+ }
+}
+
+void QMB_MuzzleFlashLG(vec3_t org)
+{
+ float frametime = fabs(cl.ctime - cl.oldtime);
+ col_t color;
+
+ color[0] = color[1] = 20;
+ color[2] = 200;
+
+ if (!qmb_initialized)
+ return;
+
+ if (!particle_mode)
+ return;
+
+ if (!(ISUNDERWATER(TruePointContents(org))))
+ {
+ AddParticle (p_muzzleflash8, org, 1, 8, 0.4 * frametime, color, zerodir);
+ }
+}
+
+void QMB_RocketTrail (vec3_t start, vec3_t end, trail_type_t type)
+{
+ col_t color;
+
+ switch (type)
+ {
+ case GRENADE_TRAIL://r00K mODIFIED
+
+ if (ISUNDERWATER(TruePointContents(start)))
+ {
+ AddParticleTrail (p_bubble, start, end, 1, 0.30, NULL);
+ }
+ else
+
+ {
+ if (r_part_trails.value > 1)
+ {
+ color[0] = 15; color[1] = 15; color[2] = 10;
+ AddParticleTrail (p_alphatrail, start, end, 8, 1, color);
+ }
+ else
+ AddParticleTrail (p_smoke, start, end, 1.45, 0.825, NULL);
+ }
+ break;
+
+ case BLOOD_TRAIL:
+ case SLIGHT_BLOOD_TRAIL:
+ if (r_part_trails.value == 2)
+ AddParticleTrail (p_q3blood_trail, start, end, 15, 2, NULL);
+ else
+ AddParticleTrail (p_blood3, start, end, type == BLOOD_TRAIL ? 1.35 : 2.4, 2, NULL);
+ break;
+
+ case TRACER1_TRAIL:
+ color[0] = color[2] = 0;
+ color[1] = 124;
+ AddParticleTrail (p_trailpart, start, end, 3.75, 0.5, color);
+ break;
+
+ case TRACER2_TRAIL:
+ color[0] = 255;
+ color[1] = 77;
+ color[2] = 0;
+ AddParticleTrail (p_trailpart, start, end, 1.75, 0.2, color);
+ break;
+
+ case VOOR_TRAIL:
+ color[0] = 77;
+ color[1] = 0;
+ color[2] = 255;
+ AddParticleTrail (p_trailpart, start, end, 3.75, 0.5, color);
+ break;
+
+ case ALT_ROCKET_TRAIL:
+
+ if (ISUNDERWATER(TruePointContents(start)))
+ {
+ AddParticleTrail (p_bubble, start, end, 1, 2.5, NULL);
+ }
+ else
+
+ {
+ if (r_part_trails.value > 1)
+ {
+ color[0] = 15; color[1] = 15; color[2] = 10;
+ AddParticleTrail (p_alphatrail, start, end, 8, 1, color);
+ }
+ else
+ {
+ AddParticleTrail (p_dpfire, start, end, 3, 0.26, NULL);
+ AddParticleTrail (p_dpsmoke, start, end, 3, 0.825, NULL);
+ }
+ }
+ break;
+
+ case LAVA_TRAIL:
+ AddParticleTrail (p_lavatrail, start, end, 5, 0.25, NULL);
+ AddParticleTrail (p_dpsmoke, start, end, 5, 0.825, NULL);
+ break;
+
+ case BUBBLE_TRAIL:
+
+ if (ISUNDERWATER(TruePointContents(start)))
+ AddParticleTrail (p_bubble2, start, end, 1.5, 0.825, NULL);
+
+ break;
+
+ case NEHAHRA_SMOKE:
+ AddParticleTrail (p_smoke, start, end, 0.8, 0.825, NULL);
+ break;
+ case RAYGREEN_TRAIL:
+ color[0] = 0;
+ color[1] = 255;
+ color[2] = 0;
+ AddParticleTrail (p_alphatrail, start, end, 8, 0.6, color);
+ break;
+ case RAYRED_TRAIL:
+ color[0] = 255;
+ color[1] = 0;
+ color[2] = 0;
+ AddParticleTrail (p_alphatrail, start, end, 8, 0.6, color);
+ break;
+ case ROCKET_TRAIL:
+ default:
+ color[0] = 255;
+ color[1] = 56;
+ color[2] = 9;
+// AddParticleTrail (p_trailpart, start, end, 6.2, 0.31, color);
+ if (ISUNDERWATER(TruePointContents(start)))
+ {
+ AddParticleTrail (p_trailpart, start, end, 1, 0.30, color);
+ AddParticleTrail (p_bubble, start, end, 1, 2.5, NULL);
+ }
+ else
+
+ {
+ if (r_part_trails.value > 1)
+ {
+ color[0] = 15; color[1] = 15; color[2] = 10;
+ AddParticleTrail (p_alphatrail, start, end, 8, 3, color);
+ }
+ else
+ {
+ AddParticleTrail (p_trailpart, start, end, 6.2, 0.31, color);
+ AddParticleTrail (p_smoke, start, end, 1.8, 0.825, NULL);
+ }
+ }
+ break;
+ case NAIL_TRAIL://R00k added
+
+ if (ISUNDERWATER(TruePointContents(start)))
+ {
+ AddParticleTrail (p_bubble, start, end, 0.25, 0.50, NULL);
+ }
+ else
+
+ {
+ color[0] = 15; color[1] = 15; color[2] = 15;
+ AddParticleTrail (p_alphatrail, start, end, 1, 0.25, color);
+ }
+ break;
+ }
+}
+/*
+void QMB_AxeSwoosh(vec3_t start)
+{
+ col_t color;
+ vec3_t end, mid;
+
+ mid[0] = 15;mid[1] = 15;mid[2] = 15;
+ VectorSubtract(start,mid,end);
+
+ if (ISUNDERWATER(TruePointContents(start)))
+ {
+ AddParticleTrail (p_bubble, start, end, 0.25, 0.50, NULL);
+ }
+ else
+ {
+ color[0] = 255; color[1] = 255; color[2] = 255;
+ AddParticleTrail (p_alphatrail, start, end, 50, 0.5, color);
+ // AddParticle (p_spark, start, 50, 250, 0.925f, NULL, zerodir);
+ }
+};
+*/
+
+void QMB_BlobExplosion (vec3_t org)
+{
+ float theta;
+ col_t color;
+ vec3_t neworg, vel;
+
+ color[0] = 60;
+ color[1] = 100;
+ color[2] = 240;
+ AddParticle (p_spark, org, 44, 250, 1.15, color, zerodir);
+
+ color[0] = 90;
+ color[1] = 47;
+ color[2] = 207;
+ AddParticle (p_fire, org, 15, 30, 1.4, color, zerodir);
+
+ vel[2] = 0;
+ for (theta = 0 ; theta < 6.28318530717958647692528676655901 ; theta += 0.0897597901025655210989326680937001)
+ {
+ color[0] = (60 + (rand() & 15));
+ color[1] = (65 + (rand() & 15));
+ color[2] = (200 + (rand() & 15));
+
+ #ifdef PSP_VFPU
+ vel[0] = vfpu_cosf(theta) * 125;
+ vel[1] = vfpu_sinf(theta) * 125;
+ neworg[0] = org[0] + vfpu_cosf(theta) * 6;
+ neworg[1] = org[1] + vfpu_sinf(theta) * 6;
+ #else
+ vel[0] = cos(theta) * 125;
+ vel[1] = sin(theta) * 125;
+ neworg[0] = org[0] + cos(theta) * 6;
+ neworg[1] = org[1] + sin(theta) * 6;
+ #endif
+ neworg[2] = org[2] + 0 - 10;
+ AddParticle (p_shockwave, neworg, 1, 4, 0.8, color, vel);
+ neworg[2] = org[2] + 0 + 10;
+ AddParticle (p_shockwave, neworg, 1, 4, 0.8, color, vel);
+
+ vel[0] *= 1.15;
+ vel[1] *= 1.15;
+ #ifdef PSP_VFPU
+ neworg[0] = org[0] + vfpu_cosf(theta) * 13;
+ neworg[1] = org[1] + vfpu_sinf(theta) * 13;
+ #else
+ neworg[0] = org[0] + cos(theta) * 13;
+ neworg[1] = org[1] + sin(theta) * 13;
+ #endif
+ neworg[2] = org[2] + 0;
+ AddParticle (p_shockwave, neworg, 1, 6, 1.0, color, vel);
+ }
+}
+
+void QMB_LavaSplash (vec3_t org)
+{
+ int i, j;
+ float vel;
+ vec3_t dir, neworg;
+
+ for (i=-16 ; i<16; i++)
+ {
+ for (j=-16 ; j<16 ; j++)
+ {
+ dir[0] = j * 8 + (rand() & 7);
+ dir[1] = i * 8 + (rand() & 7);
+ dir[2] = 256;
+
+ neworg[0] = org[0] + dir[0];
+ neworg[1] = org[1] + dir[1];
+ neworg[2] = org[2] + (rand() & 63);
+
+ VectorNormalizeFast (dir);
+ vel = 50 + (rand() & 63);
+ VectorScale (dir, vel, dir);
+
+ AddParticle (p_lavasplash, neworg, 1, 4.5, 2.6 + (rand() & 31) * 0.02, NULL, dir);
+ }
+ }
+}
+
+void QMB_TeleportSplash (vec3_t org)
+{
+ int i, j, k;
+ vec3_t neworg, angle;
+ col_t color;
+
+ if (r_part_telesplash.value == 2)
+ {
+ AddParticle (p_q3teleport, org, 1, 1, 1.0, NULL, zerodir);
+ return;
+ }
+
+ //QMB_Shockwave_Splash(org, 120);
+ for (i=-12 ; i<=12 ; i+=6)
+ {
+ for (j=-12 ; j<=12 ; j+=6)
+ {
+ for (k=-24 ; k<=32 ; k+=8)
+ {
+ neworg[0] = org[0] + i + (rand() & 3) - 1;
+ neworg[1] = org[1] + j + (rand() & 3) - 1;
+ neworg[2] = org[2] + k + (rand() & 3) - 1;
+ angle[0] = (rand() & 15) - 7;
+ angle[1] = (rand() & 15) - 7;
+ angle[2] = (rand() % 160) - 80;
+ AddParticle (p_teleflare, neworg, 1, 1.8, 0.30 + (rand() & 7) * 0.02, NULL, angle);
+ }
+ }
+ }
+
+ VectorSet (color, 140, 140, 255);
+ VectorClear (angle);
+ for (i=0 ; i<5 ; i++)
+ {
+ angle[2] = 0;
+ for (j=0 ; j<5 ; j++)
+ {
+ AngleVectors (angle, NULLVEC, NULLVEC, neworg);
+ VectorMA (org, 70, neworg, neworg);
+ AddParticle (p_sparkray, org, 1, 6 + (i & 3), 5, color, neworg);
+ angle[2] += 360 / 5;
+ }
+ angle[0] += 180 / 5;
+ }
+}
+
+
+void QMB_InfernoFlame (vec3_t org)
+{
+ float frametime = fabs(cl.ctime - cl.oldtime);
+
+ if (ISUNDERWATER(TruePointContents(org)))
+ return;
+
+ if (frametime)
+ {
+ if (r_flametype.value < 1)
+ {
+ AddParticle (p_torch_flame, org, 1, 5, 0.5, NULL, zerodir);//R00k
+ }
+ else
+ {
+ AddParticle (p_inferno_flame, org, 1, 30, 13.125 * frametime, NULL, zerodir);
+ AddParticle (p_inferno_trail, org, 2, 1.75, 45.0 * frametime, NULL, zerodir);
+ AddParticle (p_inferno_trail, org, 2, 1.0, 52.5 * frametime, NULL, zerodir);
+ }
+ }
+}
+
+void QMB_StaticBubble (entity_t *ent)
+{
+ AddParticle (p_staticbubble, ent->origin, 1, ent->frame == 1 ? 1.85 : 2.9, 0.001, NULL, zerodir);
+}
+
+void QMB_TorchFlame (vec3_t org)
+{
+ if (fabs(cl.ctime - cl.oldtime))
+ AddParticle (p_torch_flame, org, 2, 2.5, 0.5, NULL, zerodir);
+}
+
+void QMB_FlameGt (vec3_t org, float size, float time)
+{
+ if (fabs(cl.ctime - cl.oldtime))
+ AddParticle (p_flame, org, 1, size, time, NULL, zerodir);
+}
+
+void QMB_BigTorchFlame (vec3_t org)
+{
+ if (fabs(cl.ctime - cl.oldtime))
+ AddParticle (p_torch_flame, org, 2, 7, 0.5, NULL, zerodir);
+}
+
+void QMB_Q3TorchFlame (vec3_t org, float size)
+{
+ static double flametime = 0;
+
+ if (flametime + 0.125 < cl.time || flametime >= cl.time)
+ flametime = cl.time;
+ else
+ return;
+
+ if (fabs(cl.ctime - cl.oldtime))
+ AddParticle (p_q3flame, org, 1, size, 0.25, NULL, zerodir);
+}
+
+void QMB_ShamblerCharge (vec3_t org)
+{
+ vec3_t pos, vec, dir;
+ col_t col = {60, 100, 240, 128};
+ float time, len;
+ int i;
+
+ for (i=0 ; i<5 ; i++)
+ {
+ VectorClear(vec);
+ VectorClear(dir);
+
+ VectorCopy(org, pos);
+ pos[0] += (rand() % 200) - 100;
+ pos[1] += (rand() % 200) - 100;
+ pos[2] += (rand() % 200) - 100;
+
+ VectorSubtract(pos, org, vec);
+ len = VectorLength (vec);
+ VectorNormalize (vec);
+ VectorMA(dir, -200, vec, dir);
+ time = len / 200;
+
+ AddParticle (p_streakwave, pos, 1, 3, time, col, dir);
+ }
+}
+
+void QMB_LaserSight (void)
+{
+ float frametime = fabs(cl.time - cl.oldtime);
+ col_t color;
+ int c;
+
+ extern cvar_t r_laserpoint;
+ extern cvar_t scr_ofsx;
+ //extern cvar_t cl_gun_offset;
+
+ //blubs-- Issue with this is that there's no particle texture assigned, need to either create, or fix rendering of null particle texture.
+ vec3_t dest, start, stop, forward, right,up;
+ trace_t trace;
+
+ if (!particle_mode)
+ return;
+
+ if (frametime)
+ {
+ if (qmb_initialized)
+ {
+ VectorClear(stop);
+ AngleVectors (r_refdef.viewangles, forward, right, up);
+ VectorCopy(cl_entities[cl.viewentity].origin, start);
+
+ start[2] += 16;
+ start[2] += cl.crouch + bound(-7, scr_ofsx.value, 4);
+
+ VectorMA (start, 0, right, start);
+ VectorMA (start, 4096, forward, dest);
+
+ c = lt_default;
+
+ switch ((int)r_laserpoint.value)
+ {
+ case 1:
+ color[0] = color[0] = 000;color[1] = 000;color[2] = 255;color[3] = 50;//B
+ c = lt_blue;
+ break;
+ case 2:
+ color[0] = 255;color[1] = 000;color[2] = 000;color[3] = 50;//R
+ c = lt_red;
+ break;
+ case 3:
+ color[0] = 255;color[1] = 255;color[2] = 000;color[3] = 50;//Y
+ //c = lt_yellow;
+ c = lt_red;
+ break;
+ case 4:
+ color[0] = 000;color[1] = 255;color[2] = 000;color[3] = 50;//G
+ c = lt_green;
+ break;
+ }
+
+ memset (&trace, 0, sizeof(trace_t));
+ trace.fraction = 1;
+ SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, start, dest, &trace);
+
+ start[2]+=cl.crouch;
+ AddParticle (p_streaktrail, start, 1, 2, 0.02, color, trace.endpos);// draw the line
+ //the 2 value above is size
+
+ if (trace.fraction != 1)
+ {
+ color[3] = 200;
+ AddParticle (p_dot, trace.endpos, 1, 4, 0.01, color, zerodir);//pinpoint on wall
+ if ((cl.maxclients < 2) && (cl.time > cl.laser_point_time))
+ {
+ CL_NewDlight (0, trace.endpos, (rand() % 10 + 30), 0.02, c);
+ cl.laser_point_time = cl.time + 0.02;
+ }
+ }
+ }
+ }
+}
+
+void QMB_Lightning_Splash(vec3_t org)
+{
+ int i, j;
+ vec3_t neworg, angle;
+ col_t color;
+ col_t col2 = {200, 100, 100, 255};
+
+ VectorSet (color, 40, 40, 128);
+ VectorClear (angle);
+
+ for (i=0 ; i<5 ; i++)
+ {
+ angle[2] = 0;
+ for (j=0 ; j<5 ; j++)
+ {
+ AngleVectors (angle, NULLVEC, NULLVEC, neworg);
+ VectorMA (org, 20, neworg, neworg);
+ AddParticle (p_spark, org, 2, 85, 0.05f, NULL, zerodir);
+ AddParticle (p_spark, org, 2, 100, 0.1f, col2, neworg);
+ angle[2] += 360 / 5;
+ }
+ angle[0] += 180 / 5;
+ }
+ color[0] = 224 + (rand() & 31);
+ color[1] = 100 + (rand() & 31);
+ color[2] = 0;
+
+ AddParticle (p_spark, org, 1, 70, 0.2, color, zerodir);
+}
+
+void QMB_LightningBeam (vec3_t start, vec3_t end)
+{
+ float frametime = fabs(cl.time - cl.oldtime);
+ col_t color = {255,255,255,255};
+ trace_t trace;
+
+ if (frametime)
+ {
+ if (qmb_initialized && r_part_lightning.value )
+ {
+ if (qmb_initialized && r_part_sparks.value)
+ {
+ memset (&trace, 0, sizeof(trace_t));
+ if (!SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, start, end, &trace))
+ {
+ if (trace.fraction < 1)
+ {
+ VectorCopy (trace.endpos, end);
+ if ((r_decal_sparks.value) && (particle_mode) && (decals_enabled))
+ {
+ R_SpawnDecalStatic(end, decal_glow, 10);
+ }
+ QMB_Lightning_Splash (end);
+ }
+ }
+ }
+
+ //R00k v1.84 moved down here
+ AddParticle(p_lightningbeam, start, 1, 80, host_frametime * 2, color, end);
+ }
+ }
+}
+
+void R_DrawQ3Model (entity_t *ent);
+
+void QMB_Q3Gunshot (vec3_t org, int skinnum, float alpha)
+{
+ vec3_t neworg, normal, v, newend;
+ entity_t *ent;
+ extern model_t *cl_q3gunshot_mod;
+
+ if (!(ent = CL_NewTempEntity()))
+ return;
+
+ VectorCopy (org, ent->origin);
+ ent->model = cl_q3gunshot_mod;
+
+ VectorCopy (cl_entities[cl.viewentity].origin, neworg);
+ VectorSubtract (ent->origin, neworg, v);
+ VectorScale (v, 2, v);
+ VectorAdd (neworg, v, newend);
+
+ if (TraceLineN(neworg, newend, newend, normal))
+ vectoangles (normal, ent->angles);
+
+ ent->skinnum = skinnum;
+ ent->rendermode = TEX_ADDITIVE;
+ ent->renderamt = alpha;
+
+ R_DrawQ3Model (ent);
+}
+
+void QMB_Q3Teleport (vec3_t org, float alpha)
+{
+ entity_t *ent;
+ extern model_t *cl_q3teleport_mod;
+
+ if (!(ent = CL_NewTempEntity()))
+ return;
+
+ VectorCopy (org, ent->origin);
+ ent->model = cl_q3teleport_mod;
+ ent->rendermode = TEX_ADDITIVE;
+ ent->renderamt = alpha;
+
+ R_DrawQ3Model (ent);
+}
+
+#define NUMVERTEXNORMALS 162
+
+extern float r_avertexnormals[NUMVERTEXNORMALS][3];
+extern vec3_t avelocities[NUMVERTEXNORMALS];
+
+/*
+===============
+R_EntityParticles
+===============
+*/
+void QMB_EntityParticles (entity_t *ent)
+{
+ int i;
+ float angle, dist, sr, sp, sy, cr, cp, cy;
+ vec3_t forward, org;
+ col_t color = {255,255,0,100};
+
+ dist = 64;
+
+ if (!avelocities[0][0])
+ for (i=0 ; iorigin[0] + r_avertexnormals[i][0]*dist + forward[0]*16;
+ org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*16;
+ org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*16;
+ AddParticle (p_flare, org, 1, 2,0.005, color, forward);
+ }
+}
+
+//Modified from Quake2
+void QMB_FlyParticles (vec3_t origin, int count)
+{
+ float frametime = fabs(cl.time - cl.oldtime);
+ int i;
+ float angle, sr, sp, sy, cr, cp, cy;
+ vec3_t forward, org;
+ float dist = 64;
+ col_t color = {255,255,255,100};
+
+ if (frametime)
+ {
+ if (count > NUMVERTEXNORMALS)
+ count = NUMVERTEXNORMALS;
+
+ if (!avelocities[0][0])
+ {
+ for (i=0 ; i
+#include
+
+#include "clipping.hpp"
+
+
+using namespace std;
+using namespace quake;
+
+list TempDecalTextureList;
+
+#ifdef SLIM
+#define DEFAULT_NUM_DECALS 1024 //*4
+#else
+#define DEFAULT_NUM_DECALS 256
+#endif
+
+#define ABSOLUTE_MIN_DECALS 256
+#define ABSOLUTE_MAX_DECALS 32768
+#define MAX_DECAL_VERTICES 128
+#define MAX_DECAL_TRIANGLES 64
+
+typedef struct decal_s
+{
+ vec3_t origin;
+ vec3_t normal;
+ vec3_t tangent;
+ float radius;
+ int bspdecal;
+
+ struct decal_s *next;
+ float die;
+ float starttime;
+
+ int srcblend;
+ int dstblend;
+
+ int texture;
+
+ // geometry of decal
+ int vertexCount, triangleCount;
+ vec3_t vertexArray[MAX_DECAL_VERTICES];
+ float texcoordArray[MAX_DECAL_VERTICES][2];
+ int triangleArray[MAX_DECAL_TRIANGLES][3];
+} decal_t;
+
+extern int decal_blood1, decal_blood2, decal_blood3, decal_q3blood, decal_burn, decal_mark, decal_glow;
+
+static decal_t *decals, *active_decals, *free_decals;
+static int r_numdecals;
+
+static plane_t leftPlane, rightPlane, bottomPlane, topPlane, backPlane, frontPlane;
+
+cvar_t r_decaltime = {"r_decaltime", "12"};
+cvar_t r_decal_viewdistance = {"r_decal_viewdistance", "1024"};
+
+void DecalClipLeaf (decal_t *dec, mleaf_t *leaf);
+void DecalWalkBsp_R (decal_t *dec, mnode_t *node);
+int DecalClipPolygonAgainstPlane (plane_t *plane, int vertexCount, vec3_t *vertex, vec3_t *newVertex);
+int DecalClipPolygon (int vertexCount, vec3_t *vertices, vec3_t *newVertex);
+/*
+#define lhrandom(MIN, MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
+
+#define VectorRandom(v) \
+{ \
+ do { \
+ (v)[0] = lhrandom(-1, 1); \
+ (v)[1] = lhrandom(-1, 1); \
+ (v)[2] = lhrandom(-1, 1); \
+ } while (DotProduct(v, v) > 1); \
+}
+*/
+float RandomMinMax (float min, float max)
+{
+ return min + ((rand() % 10000) / 10000.0) * (max - min);
+}
+
+/*
+===============
+R_InitDecals
+===============
+*/
+void R_InitDecals (void)
+{
+ int i;
+
+ Cvar_RegisterVariable (&r_decaltime);
+ Cvar_RegisterVariable (&r_decal_viewdistance);
+
+ if (!qmb_initialized)
+ return;
+/*
+ decal_blood1 = loadtextureimage("textures/decals/blood_splat01", 0, 0, qfalse, GU_LINEAR);
+ decal_blood2 = loadtextureimage("textures/decals/blood_splat02", 0, 0, qfalse, GU_LINEAR);
+ decal_blood3 = loadtextureimage("textures/decals/blood_splat03", 0, 0, qfalse, GU_LINEAR);
+ decal_q3blood = loadtextureimage("textures/decals/blood_stain", 0, 0, qfalse, GU_LINEAR);
+ decal_burn = loadtextureimage("textures/decals/explo_burn01", 0, 0, qfalse, GU_LINEAR);
+ decal_mark = loadtextureimage("textures/decals/particle_burn01", 0, 0, qfalse, GU_LINEAR);
+ decal_glow = loadtextureimage("textures/decals/glow2", 0, 0, qfalse, GU_LINEAR);
+
+ decal_blood1 = GL_LoadTextureImage ("textures/decals/blood_splat01", "decals:blood_splat01", 128, 128, TEX_ALPHA | TEX_COMPLAIN);
+ decal_blood2 = GL_LoadTextureImage ("textures/decals/blood_splat02", "decals:blood_splat02", 128, 128, TEX_ALPHA | TEX_COMPLAIN);
+ decal_blood3 = GL_LoadTextureImage ("textures/decals/blood_splat03", "decals:blood_splat03", 128, 128, TEX_ALPHA | TEX_COMPLAIN);
+ decal_q3blood = GL_LoadTextureImage ("textures/decals/blood_stain", "decals:blood_stain", 64, 64, TEX_ALPHA | TEX_COMPLAIN);
+ decal_burn = GL_LoadTextureImage ("textures/decals/explo_burn01", "decals:explo_burn01", 128, 128, TEX_ALPHA | TEX_COMPLAIN);
+ decal_mark = GL_LoadTextureImage ("textures/decals/particle_burn01", "decals:particle_burn01", 64, 64, TEX_ALPHA | TEX_COMPLAIN);
+ decal_glow = GL_LoadTextureImage ("textures/decals/glow2", "decals:glow2", 64, 64, TEX_ALPHA | TEX_COMPLAIN);
+*/
+ if ((i = COM_CheckParm("-decals")) && i + 1 < com_argc)
+ {
+ r_numdecals = Q_atoi(com_argv[i+1]);
+ r_numdecals = bound(ABSOLUTE_MIN_DECALS, r_numdecals, ABSOLUTE_MAX_DECALS);
+ }
+ else
+ {
+ r_numdecals = DEFAULT_NUM_DECALS;
+ }
+
+ decals = (decal_t*)Hunk_AllocName (r_numdecals * sizeof(decal_t), "decals");
+}
+
+/*
+===============
+R_ClearDecals
+===============
+*/
+static int wadreload = 1;
+void R_ClearDecals (void)
+{
+ int i;
+
+ if (!qmb_initialized)
+ return;
+
+ memset (decals, 0, r_numdecals * sizeof(decal_t));
+ free_decals = &decals[0];
+ active_decals = NULL;
+
+ for (i = 0 ; i < r_numdecals ; i++)
+ decals[i].next = &decals[i+1];
+
+ decals[r_numdecals-1].next = NULL;
+
+ while (TempDecalTextureList.size() > 0)
+ {
+ int index = TempDecalTextureList.front();
+ TempDecalTextureList.pop_front();
+ GL_UnloadTexture(index);
+ }
+ wadreload = 1;
+}
+
+void R_SpawnDecal (vec3_t center, vec3_t normal, vec3_t tangent, int tex, int size, int isbsp)
+{
+ int a;
+ float width, height, depth, d, one_over_w, one_over_h;
+ vec3_t binormal, test = {0.5, 0.5, 0.5};
+ decal_t *dec;
+
+ if (!qmb_initialized)
+ return;
+
+ // allocate decal
+ if (!free_decals)
+ return;
+
+ dec = free_decals;
+ free_decals = dec->next;
+ dec->next = active_decals;
+ active_decals = dec;
+
+ VectorNormalize (test);
+ CrossProduct (normal, test, tangent);
+
+ VectorCopy (center, dec->origin);
+ VectorCopy (tangent, dec->tangent);
+ VectorCopy (normal, dec->normal);
+ VectorNormalize (tangent);
+ VectorNormalize (normal);
+ CrossProduct (normal, tangent, binormal);
+ VectorNormalize (binormal);
+
+ width = RandomMinMax (size * 0.5, size);
+ height = width;
+ depth = width * 0.5;
+ dec->radius = fmax(fmax(width, height), depth);
+ dec->starttime = cl.time;
+ dec->bspdecal = isbsp;
+ dec->die = (isbsp ? 0 : cl.time + r_decaltime.value);
+ dec->texture = tex;
+
+ // Calculate boundary planes
+ d = DotProduct (center, tangent);
+ VectorCopy (tangent, leftPlane.normal);
+ leftPlane.dist = -(width * 0.5 - d);
+ VectorNegate (tangent, tangent);
+ VectorCopy (tangent, rightPlane.normal);
+ VectorNegate (tangent, tangent);
+ rightPlane.dist = -(width * 0.5 + d);
+
+ d = DotProduct (center, binormal);
+ VectorCopy (binormal, bottomPlane.normal);
+ bottomPlane.dist = -(height * 0.5 - d);
+ VectorNegate (binormal, binormal);
+ VectorCopy (binormal, topPlane.normal);
+ VectorNegate (binormal, binormal);
+ topPlane.dist = -(height * 0.5 + d);
+
+ d = DotProduct (center, normal);
+ VectorCopy (normal, backPlane.normal);
+ backPlane.dist = -(depth - d);
+ VectorNegate (normal, normal);
+ VectorCopy (normal, frontPlane.normal);
+ VectorNegate (normal, normal);
+ frontPlane.dist = -(depth + d);
+
+ // Begin with empty mesh
+ dec->vertexCount = 0;
+ dec->triangleCount = 0;
+
+ // Clip decal to bsp
+ DecalWalkBsp_R (dec, cl.worldmodel->nodes);
+
+ // This happens when a decal is to far from any surface or the surface is to steeply sloped
+ if (dec->triangleCount == 0)
+ { // deallocate decal
+ active_decals = dec->next;
+ dec->next = free_decals;
+ free_decals = dec;
+ return;
+ }
+
+ // Assign texture mapping coordinates
+ one_over_w = 1.0F / width;
+ one_over_h = 1.0F / height;
+ for (a = 0 ; a < dec->vertexCount ; a++)
+ {
+ float s, t;
+ vec3_t v;
+
+ VectorSubtract (dec->vertexArray[a], center, v);
+ s = DotProduct (v, tangent) * one_over_w + 0.5F;
+ t = DotProduct (v, binormal) * one_over_h + 0.5F;
+ dec->texcoordArray[a][0] = s;
+ dec->texcoordArray[a][1] = t;
+ }
+}
+/*
+//back up, pre-blubs
+void _R_SpawnDecalStatic (vec3_t org, int tex, int size)
+{
+ int i;
+ float frac, bestfrac;
+ vec3_t tangent, v, bestorg, normal, bestnormal, org2;
+
+ if (!qmb_initialized)
+ return;
+
+ VectorClear (bestorg);
+ VectorClear (bestnormal);
+
+ bestfrac = 10;
+ for (i = 0 ; i < 14 ; i++)
+ {
+ VectorRandom (org2);
+ VectorMA (org, 20, org2, org2);
+ TraceLineN (org, org2, v, normal);
+ frac = 0.5;
+
+ if (bestfrac > frac)
+ {
+ bestfrac = frac;
+ VectorCopy (v, bestorg);
+ VectorCopy (normal, bestnormal);
+ CrossProduct (normal, bestnormal, tangent);
+ }
+
+ if (bestnormal && bestorg)
+ {
+ continue;
+ }
+ else
+ {
+ VectorNegate (org2, org2);
+ VectorMA (org, 20, org2, org2);
+ TraceLineN (org, org2, v, normal);
+ VectorCopy (v, bestorg);
+ VectorCopy (normal, bestnormal);
+ CrossProduct (normal, bestnormal, tangent);
+ }
+
+ if (bestnormal && bestorg)
+ continue;
+
+ }
+
+ if (bestfrac < 1)
+ R_SpawnDecal (bestorg, bestnormal, tangent, tex, size, 0);
+}*/
+
+//Revamped by blubs
+void R_SpawnDecalStatic (vec3_t org, int tex, int size)
+{
+ int i;
+ float frac, bestfrac;
+ vec3_t tangent, v, bestorg, normal, bestnormal, org2;
+ vec3_t tempVec;
+
+ if (!qmb_initialized)
+ return;
+
+ VectorClear (bestorg);
+ VectorClear (bestnormal);
+ VectorClear(tempVec);
+
+ bestfrac = 10;
+ for (i = 0 ; i < 26 ; i++)
+ {
+ //Reference: i = 0: check straight up, i = 1: check straight down
+ //1 < i < 10: Check sideways in increments of 45 degrees
+ //9 < i < 18: Check angled 45 degrees down in increments of 45 degrees
+ //17 < i : Check angled 45 degrees up in increments of 45 degrees
+ org2[0] = (((((i - 2) % 8) < 2) || (((i - 2) % 8) == 7)) ? 1 : 0 ) + ((((i - 2) % 8) > 2 && ((i - 2) % 8) < 6) ? -1 : 0 );
+ org2[1] = ((((i - 2) % 8) > 0 && ((i - 2) % 8) < 4) ? 1 : 0 ) + ((((i - 2) % 8) > 4 && ((i - 2) % 8) < 7) ? -1 : 0 );
+ org2[2] = ((i == 0) ? 1 : 0) + ((i == 1) ? -1 : 0) + (((i > 9) && (i < 18)) ? 1 : 0) + ((i > 17) ? -1 : 0);
+
+ VectorCopy(org,tempVec);
+ VectorMA(tempVec, -0.1,org2,tempVec);
+
+ VectorMA (org, 20, org2, org2);
+ TraceLineN (tempVec, org2, v, normal);
+
+ VectorSubtract(org2,tempVec,org2);//goal
+ VectorSubtract(v,tempVec,tempVec);//collision
+
+ if(VectorLength(org2) == 0)
+ return;
+
+ frac = VectorLength(tempVec) / VectorLength(org2);
+
+ if(frac < 1 && frac < bestfrac)
+ {
+ bestfrac = frac;
+ VectorCopy(v,bestorg);
+ VectorCopy(normal, bestnormal);
+ CrossProduct(normal,bestnormal,tangent);
+ }
+ }
+
+ if (bestfrac < 1)
+ R_SpawnDecal (bestorg, bestnormal, tangent, tex, size, 0);
+}
+
+
+//Crow_bar.
+void R_SpawnDecalBSP (vec3_t org, char *texname, int size)
+{
+ int i;
+ float frac, bestfrac;
+ vec3_t tangent, v, bestorg, normal, bestnormal, org2;
+ vec3_t tempVec;
+
+ if (!qmb_initialized)
+ return;
+
+ int tex = loadtextureimage(va("decals/%s",texname), 0, 0, qfalse, GU_LINEAR);
+ if(!tex) //find in decals.wad
+ {
+ if(wadreload)
+ {
+ WAD3_LoadTextureWadFile ("decals.wad");
+ wadreload = 0;
+ }
+ tex = WAD3_LoadTextureName(texname);
+ if(!tex)
+ {
+ return;
+ }
+ }
+
+ //don't free static decals
+ if((tex != decal_q3blood) &&
+ (tex != decal_blood1) &&
+ (tex != decal_blood2) &&
+ (tex != decal_blood3) &&
+ (tex != decal_q3blood) &&
+ (tex != decal_burn) &&
+ (tex != decal_mark) &&
+ (tex != decal_glow))
+ {
+ TempDecalTextureList.push_back(tex); //write to list ,for unload
+ }
+
+ VectorClear (bestorg);
+ VectorClear (bestnormal);
+ VectorClear(tempVec);
+
+ bestfrac = 10;
+ for (i = 0 ; i < 26 ; i++)
+ {
+ //Reference: i = 0: check straight up, i = 1: check straight down
+ //1 < i < 10: Check sideways in increments of 45 degrees
+ //9 < i < 18: Check angled 45 degrees down in increments of 45 degrees
+ //17 < i : Check angled 45 degrees up in increments of 45 degrees
+ org2[0] = (((((i - 2) % 8) < 2) || (((i - 2) % 8) == 7)) ? 1 : 0 ) + ((((i - 2) % 8) > 2 && ((i - 2) % 8) < 6) ? -1 : 0 );
+ org2[1] = ((((i - 2) % 8) > 0 && ((i - 2) % 8) < 4) ? 1 : 0 ) + ((((i - 2) % 8) > 4 && ((i - 2) % 8) < 7) ? -1 : 0 );
+ org2[2] = ((i == 0) ? 1 : 0) + ((i == 1) ? -1 : 0) + (((i > 9) && (i < 18)) ? 1 : 0) + ((i > 17) ? -1 : 0);
+
+ VectorCopy(org,tempVec);
+ VectorMA(tempVec, -0.1,org2,tempVec);
+
+ VectorMA (org, 20, org2, org2);
+ TraceLineN (tempVec, org2, v, normal);
+
+ VectorSubtract(org2,tempVec,org2);//goal
+ VectorSubtract(v,tempVec,tempVec);//collision
+
+ if(VectorLength(org2) == 0)
+ return;
+
+ frac = VectorLength(tempVec) / VectorLength(org2);
+
+ if(frac < 1 && frac < bestfrac)
+ {
+ bestfrac = frac;
+ VectorCopy(v,bestorg);
+ VectorCopy(normal, bestnormal);
+ CrossProduct(normal,bestnormal,tangent);
+ }
+ }
+
+ if (bestfrac < 1)
+ R_SpawnDecal (bestorg, bestnormal, tangent, tex, size, 1);
+}
+
+void DecalWalkBsp_R (decal_t *dec, mnode_t *node)
+{
+ float dist;
+ mplane_t *plane;
+ mleaf_t *leaf;
+
+ if (node->contents < 0)
+ { //we are in a leaf
+ leaf = (mleaf_t *)node;
+ DecalClipLeaf (dec, leaf);
+ return;
+ }
+
+ plane = node->plane;
+ dist = DotProduct (dec->origin, plane->normal) - plane->dist;
+
+ if (dist > dec->radius)
+ {
+ DecalWalkBsp_R (dec, node->children[0]);
+ return;
+ }
+ if (dist < -dec->radius)
+ {
+ DecalWalkBsp_R (dec, node->children[1]);
+ return;
+ }
+
+ DecalWalkBsp_R (dec, node->children[0]);
+ DecalWalkBsp_R (dec, node->children[1]);
+}
+
+qboolean DecalAddPolygon (decal_t *dec, int vertcount, vec3_t *vertices)
+{
+ int a, b, count, *triangle;
+
+ count = dec->vertexCount;
+ if (count + vertcount >= MAX_DECAL_VERTICES)
+ return qfalse;
+
+ if (dec->triangleCount + vertcount - 2 >= MAX_DECAL_TRIANGLES)
+ return qfalse;
+
+ // Add polygon as a triangle fan
+ triangle = &dec->triangleArray[dec->triangleCount][0];
+ for (a = 2 ; a < vertcount ; a++)
+ {
+ dec->triangleArray[dec->triangleCount][0] = count;
+ dec->triangleArray[dec->triangleCount][1] = (count + a - 1);
+ dec->triangleArray[dec->triangleCount][2] = (count + a );
+ dec->triangleCount++;
+ }
+
+ // Assign vertex colors
+ for (b = 0 ; b < vertcount ; b++)
+ {
+ VectorCopy(vertices[b], dec->vertexArray[count]);
+ count++;
+ }
+
+ dec->vertexCount = count;
+ return qtrue;
+}
+
+const double decalEpsilon = 0.001;
+
+void DecalClipLeaf (decal_t *dec, mleaf_t *leaf)
+{
+ int c;
+ vec3_t newVertex[64], t3;
+ msurface_t **surf;
+
+ c = leaf->nummarksurfaces;
+ surf = leaf->firstmarksurface;
+
+ // for all surfaces in the leaf
+ for (c = 0 ; c < leaf->nummarksurfaces ; c++, surf++)
+ {
+ int i, count;
+ glpoly_t *poly;
+
+ poly = (*surf)->polys;
+ for (i = 0 ; i < poly->numverts ; i++)
+ {
+ newVertex[i][0] = poly->verts[i].xyz[0];
+ newVertex[i][1] = poly->verts[i].xyz[1];
+ newVertex[i][2] = poly->verts[i].xyz[2];
+ }
+
+ VectorCopy ((*surf)->plane->normal, t3);
+
+ if ((*surf)->flags & SURF_PLANEBACK)
+ VectorNegate (t3, t3);
+
+ // avoid backfacing and ortogonal facing faces to recieve decal parts
+ if (DotProduct(dec->normal, t3) > decalEpsilon)
+ {
+ count = DecalClipPolygon (poly->numverts, newVertex, newVertex);
+ if (count != 0 && !DecalAddPolygon(dec, count, newVertex))
+ break;
+ }
+ }
+}
+
+int DecalClipPolygon (int vertexCount, vec3_t *vertices, vec3_t *newVertex)
+{
+ vec3_t tempVertex[64];
+
+ // Clip against all six planes
+ int count = DecalClipPolygonAgainstPlane (&leftPlane, vertexCount, vertices, tempVertex);
+ if (count != 0)
+ {
+ count = DecalClipPolygonAgainstPlane (&rightPlane, count, tempVertex, newVertex);
+ if (count != 0)
+ {
+ count = DecalClipPolygonAgainstPlane (&bottomPlane, count, newVertex, tempVertex);
+ if (count != 0)
+ {
+ count = DecalClipPolygonAgainstPlane (&topPlane, count, tempVertex, newVertex);
+ if (count != 0)
+ {
+ count = DecalClipPolygonAgainstPlane (&backPlane, count, newVertex, tempVertex);
+ if (count != 0)
+ {
+ count = DecalClipPolygonAgainstPlane (&frontPlane, count, tempVertex, newVertex);
+ }
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+int DecalClipPolygonAgainstPlane (plane_t *plane, int vertexCount, vec3_t *vertex, vec3_t *newVertex)
+{
+ int a, b, c, count, negativeCount = 0;
+ float t;
+ bool negative[65];
+ vec3_t v1, v2;
+
+ // Classify vertices
+ for (a = 0 ; a < vertexCount ; a++)
+ {
+ bool neg = ((DotProduct(plane->normal, vertex[a]) - plane->dist) < 0.0);
+ negative[a] = neg;
+ negativeCount += neg;
+ }
+
+ // Discard this polygon if it's completely culled
+ if (negativeCount == vertexCount)
+ return 0;
+
+ count = 0;
+ for (b = 0 ; b < vertexCount ; b++)
+ {
+ // c is the index of the previous vertex
+ c = (b != 0) ? b - 1 : vertexCount - 1;
+
+ if (negative[b])
+ {
+ if (!negative[c])
+ {
+ // Current vertex is on negative side of plane, but previous vertex is on positive side.
+ VectorCopy (vertex[c], v1);
+ VectorCopy (vertex[b], v2);
+
+ t = (DotProduct(plane->normal, v1) - plane->dist) /
+ (plane->normal[0] * (v1[0] - v2[0])
+ + plane->normal[1] * (v1[1] - v2[1])
+ + plane->normal[2] * (v1[2] - v2[2]));
+
+ VectorScale (v1, (1.0 - t), newVertex[count]);
+ VectorMA (newVertex[count], t, v2, newVertex[count]);
+
+ count++;
+ }
+ }
+ else
+ {
+ if (negative[c])
+ {
+ // Current vertex is on positive side of plane, but previous vertex is on negative side.
+ VectorCopy (vertex[b], v1);
+ VectorCopy (vertex[c], v2);
+
+ t = (DotProduct(plane->normal, v1) - plane->dist) /
+ (plane->normal[0] * (v1[0] - v2[0])
+ + plane->normal[1] * (v1[1] - v2[1])
+ + plane->normal[2] * (v1[2] - v2[2]));
+
+ VectorScale (v1, (1.0 - t), newVertex[count]);
+ VectorMA (newVertex[count], t, v2, newVertex[count]);
+
+ count++;
+ }
+
+ // Include current vertex
+ VectorCopy (vertex[b], newVertex[count]);
+ count++;
+ }
+ }
+
+ // Return number of vertices in clipped polygon
+ return count;
+}
+
+/*
+===============
+R_DrawDecals
+===============
+*/
+void R_DrawDecals (void)
+{
+ int i;
+ float dcolor;
+ vec3_t decaldist;
+ decal_t *p, *kill;
+ float *point_tex, *point_xyz;
+
+ if (!qmb_initialized)
+ return;
+
+
+ sceGuEnable (GU_BLEND);
+ sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
+ sceGuDepthMask (GU_TRUE);
+ sceGuShadeModel (GU_SMOOTH);
+ sceGuDepthOffset(-256);
+
+
+ for ( ; ; )
+ {
+ kill = active_decals;
+
+ if (kill && (kill->die < cl.time) && (!kill->bspdecal))
+ {
+ active_decals = kill->next;
+ kill->next = free_decals;
+ free_decals = kill;
+ continue;
+ }
+ break;
+ }
+
+ for (p = active_decals ; p ; p = p->next)
+ {
+
+ for ( ; ; )
+ {
+ kill = p->next;
+
+ if (kill && (kill->die < cl.time) && (!kill->bspdecal))
+ {
+ p->next = kill->next;
+ kill->next = free_decals;
+ free_decals = kill;
+ continue;
+ }
+ break;
+ }
+ VectorSubtract (r_refdef.vieworg, p->origin, decaldist);
+
+ if (VectorLength(decaldist) > r_decal_viewdistance.value)
+ continue;
+
+ if (p->texture == decal_q3blood)
+ sceGuBlendFunc (GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
+ else
+ sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
+
+
+ GL_Bind (p->texture);
+
+ dcolor = 1;
+ if (((p->die - cl.time) < 0.5) && (!p->bspdecal))
+ {
+ float scale = 2 * (p->die - cl.time);
+ sceGuColor(GU_COLOR(dcolor * scale, dcolor * scale, dcolor * scale, scale));
+ }
+ else
+ {
+ dcolor = (1 - (VectorLength(decaldist) / r_decal_viewdistance.value));
+ sceGuColor(GU_COLOR(dcolor, dcolor, dcolor, dcolor));
+ }
+
+ for (i = 0 ; i < p->triangleCount ; i++)
+ {
+ // Allocate memory for this polygon.
+ const int unclipped_vertex_count = 3;
+ glvert_t* const unclipped_vertices = static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count));
+ for(int v = 0; v < unclipped_vertex_count ; v++)
+ {
+ point_tex = &p->texcoordArray[p->triangleArray[i][v]][0];
+ point_xyz = &p->vertexArray [p->triangleArray[i][v]][0];
+ unclipped_vertices[v].st[0] = point_tex[0];
+ unclipped_vertices[v].st[1] = point_tex[1];
+ unclipped_vertices[v].xyz[0] = point_xyz[0];
+ unclipped_vertices[v].xyz[1] = point_xyz[1];
+ unclipped_vertices[v].xyz[2] = point_xyz[2];
+ }
+
+ if (clipping::is_clipping_required(
+ unclipped_vertices,
+ unclipped_vertex_count))
+ {
+ // Clip the polygon.
+ const glvert_t* clipped_vertices;
+ std::size_t clipped_vertex_count;
+ clipping::clip(
+ unclipped_vertices,
+ unclipped_vertex_count,
+ &clipped_vertices,
+ &clipped_vertex_count);
+
+ // Did we have any vertices left?
+ if (clipped_vertex_count)
+ {
+ // Copy the vertices to the display list.
+ const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t);
+ glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size));
+ memcpy(display_list_vertices, clipped_vertices, buffer_size);
+
+ // Draw the clipped vertices.
+ sceGuDrawArray(
+ GU_TRIANGLE_FAN,
+ GU_TEXTURE_32BITF | GU_VERTEX_32BITF,
+ clipped_vertex_count, 0, display_list_vertices);
+ }
+ }
+ else
+ {
+ // Draw the poly directly.
+ sceGuDrawArray(
+ GU_TRIANGLE_FAN,
+ GU_TEXTURE_32BITF | GU_VERTEX_32BITF,
+ unclipped_vertex_count, 0, unclipped_vertices);
+ }
+ }
+ }
+
+ sceGuDepthOffset(0);
+
+ sceGuDisable (GU_BLEND);
+ sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
+ sceGuDepthMask (GU_FALSE);
+
+ sceGuColor(GU_COLOR(1,1,1,1));
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+ sceGuShadeModel (GU_FLAT);
+}
+
diff --git a/source/psp/video_hardware_draw.cpp b/source/psp/video_hardware_draw.cpp
new file mode 100644
index 0000000..e0c7a1b
--- /dev/null
+++ b/source/psp/video_hardware_draw.cpp
@@ -0,0 +1,3472 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+Copyright (C) 2008-2009 Crow_bar.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+// draw.c -- this is the only file outside the refresh that touches the
+// vid buffer
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+
+#include "video_hardware_dxtn.h"
+#include "video_hardware_resample.h"
+
+#include "vram.hpp"
+#include
+
+byte *draw_chars; // 8*8 graphic characters
+qpic_t *sniper_scope;
+
+int translate_texture;
+int char_texture;
+int detail_texture;
+int underwater_texture;
+//int zombie_skins[3][8];
+int zombie_skins[2][2];
+int ref_texture;
+int nonetexture;
+typedef byte texel;
+
+bool tex_scale_down = true;
+
+//Loading Fill by Crow_bar
+float loading_cur_step;
+char loading_name[32];
+float loading_num_step;
+
+#define CLUT4
+
+typedef struct
+{
+ // Source.
+ char identifier[64];
+ int original_width;
+ int original_height;
+ bool stretch_to_power_of_two;
+
+ // Texture description.
+ int format;
+ int filter;
+ int width;
+ int height;
+ int mipmaps;
+ int bpp;
+ int swizzle;
+ qboolean islmp;
+#ifdef CLUT4
+ unsigned char *palette;
+ ScePspRGBA8888 *palette_2;
+ #else
+ ScePspRGBA8888 *palette;
+#endif
+ qboolean palette_active;
+
+ // Buffers.
+ texel* ram;
+ texel* vram;
+} gltexture_t;
+
+int loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, int filter);
+void VID_SetPalette4(unsigned char* clut4pal);
+int vid_palmode;
+
+#define MAX_GLTEXTURES 1024
+gltexture_t gltextures[MAX_GLTEXTURES];
+bool gltextures_used[MAX_GLTEXTURES];
+int numgltextures;
+
+typedef struct
+{
+ int index; // index into gltextures[].
+} glpic_t;
+
+//cvar_t png_compression_level = {"png_compression_level", "1"};
+//cvar_t jpeg_compression_level = {"jpeg_compression_level", "75"};
+
+void GL_Bind4Part(int texture_index)
+{
+ const gltexture_t& texture = gltextures[texture_index];
+ VID_SetPalette4(texture.palette);
+ vid_palmode = GU_PSM_T4;
+
+ sceGuTexMode(texture.format, 0, 0, GU_FALSE);
+ sceGuTexFilter(texture.filter, texture.filter);
+
+ const void* const texture_memory = texture.vram ? texture.vram : texture.ram;
+ sceGuTexImage(0, texture.width, texture.height, texture.width, texture_memory);
+}
+
+void GL_InitTextureUsage ()
+{
+ for (int i=0; i 0 && r_mipmaps.value > 0)
+ {
+ //Con_Printf("asd\n");
+ float slope = 0.4f;
+ sceGuTexSlope(slope); // the near from 0 slope is the lower (=best detailed) mipmap it uses
+ sceGuTexFilter(GU_LINEAR_MIPMAP_LINEAR, GU_LINEAR_MIPMAP_LINEAR);
+ sceGuTexLevelMode(int(r_mipmaps_func.value), r_mipmaps_bias.value);
+ }
+ else {
+ // dr_mabuse1981: "retro filter" start.
+ if (r_retro.value)
+ sceGuTexFilter(GU_NEAREST, GU_NEAREST);
+ else
+ sceGuTexFilter(texture.filter, texture.filter);
+ // dr_mabuse1981: "retro filter" end.
+ }
+
+ // Set the texture image.
+ const void* const texture_memory = texture.vram ? texture.vram : texture.ram;
+ sceGuTexImage(0, texture.width, texture.height, texture.width, texture_memory);
+
+ if (texture.mipmaps > 0 && r_mipmaps.value > 0) {
+ int size = (texture.width * texture.height);
+ int offset = size;
+ int div = 2;
+
+ for (int i = 1; i <= texture.mipmaps; i++) {
+ void* const texture_memory2 = ((byte*)texture_memory) + offset;
+ sceGuTexImage(i, texture.width / div, texture.height / div, texture.width / div, texture_memory2);
+ offset += size / (div*div);
+ div *= 2;
+ }
+ }
+ }
+
+
+}
+
+void GL_BindLM (int texture_index)
+{
+ // Binding the currently bound texture?
+ if (currenttexture == texture_index)
+ {
+ // Don't bother.
+ return;
+ }
+
+ // Remember the current texture.
+ currenttexture = texture_index;
+
+ // Which texture is it?
+ const gltexture_t& texture = gltextures[texture_index];
+
+ // Set the texture mode.
+ sceGuTexMode(texture.format, 0, 0, texture.swizzle);
+ sceGuTexFilter(texture.filter, texture.filter);
+
+ // Set the texture image.
+ const void* const texture_memory = texture.vram ? texture.vram : texture.ram;
+ sceGuTexImage(0, texture.width, texture.height, texture.width, texture_memory);
+}
+
+void GL_BindDET (int texture_index)
+{
+ int tex_m = (int)r_detail_mipmaps_func.value;
+ // Binding the currently bound texture?
+ if (currenttexture == texture_index)
+ {
+ // Don't bother.
+ return;
+ }
+
+ // Remember the current texture.
+ currenttexture = texture_index;
+
+ // Which texture is it?
+ const gltexture_t& texture = gltextures[texture_index];
+
+ // Set the texture mode.
+ sceGuTexMode(texture.format, texture.mipmaps , 0, texture.swizzle);
+
+ if (texture.mipmaps > 0 && r_detail_mipmaps.value > 0)
+ {
+ float slope = 0.4f;
+ sceGuTexSlope(slope); // the near from 0 slope is the lower (=best detailed) mipmap it uses
+ sceGuTexFilter(GU_LINEAR_MIPMAP_LINEAR, GU_LINEAR_MIPMAP_LINEAR);
+ sceGuTexLevelMode(tex_m, r_detail_mipmaps_bias.value); // manual slope setting
+ }
+ else
+ sceGuTexFilter(texture.filter, texture.filter);
+
+
+ //sceGuTexLevelMode(1, 1.0f); // manual slope setting
+ //sceGuTexSlope(0.2f); // the near from 0 slope is the lower (=best detailed) mipmap it uses
+
+ // Set the texture image.
+ const void* const texture_memory = texture.vram ? texture.vram : texture.ram;
+ sceGuTexImage(0, texture.width, texture.height, texture.width, texture_memory);
+
+
+ if (texture.mipmaps > 0 && r_detail_mipmaps.value > 0)
+ {
+ int size = (texture.width * texture.height * texture.bpp);
+ int offset = size;
+ int div = 2;
+
+ for (int i = 1; i <= texture.mipmaps; i++)
+ {
+ void* const texture_memory2 = ((byte*) texture_memory)+offset;
+ sceGuTexImage(i, texture.width/div, texture.height/div, texture.width/div, texture_memory2);
+ offset += size/(div*div);
+ div *=2;
+ }
+ }
+
+}
+
+//=============================================================================
+/* Support Routines */
+
+typedef struct cachepic_s
+{
+ char name[MAX_QPATH];
+ qpic_t pic;
+ byte padding[32]; // for appended glpic
+} cachepic_t;
+
+#define MAX_CACHED_PICS 128
+cachepic_t menu_cachepics[MAX_CACHED_PICS];
+int menu_numcachepics;
+
+byte menuplyr_pixels[4096];
+
+static int GL_LoadPicTexture (qpic_t *pic)
+{
+ return GL_LoadTexture ("", pic->width, pic->height, pic->data, qfalse, GU_NEAREST, 0);
+}
+
+qpic_t *Draw_PicFromWad (char *name)
+{
+ qpic_t *p;
+ glpic_t *gl;
+
+ p = static_cast(W_GetLumpName (name));
+ gl = (glpic_t *)p->data;
+ gl->index = GL_LoadPicTexture (p);
+
+ return p;
+}
+
+
+/*
+================
+Draw_CachePic
+================
+*/
+qpic_t *Draw_CachePic (char *path)
+{
+ cachepic_t *pic;
+ int i;
+ qpic_t *dat;
+ glpic_t *gl;
+ char str[128];
+
+ strcpy (str, path);
+ for (pic=menu_cachepics, i=0 ; iname))
+ return &pic->pic;
+
+ if (menu_numcachepics == MAX_CACHED_PICS)
+ Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");
+ menu_numcachepics++;
+ strcpy (pic->name, str);
+
+//
+// load the pic from disk
+//
+
+ int index = loadtextureimage (str, 0, 0, qfalse, GU_LINEAR);
+ if(index)
+ {
+ pic->pic.width = gltextures[index].original_width;
+ pic->pic.height = gltextures[index].original_height;
+
+ gltextures[index].islmp = qfalse;
+ gl = (glpic_t *)pic->pic.data;
+ gl->index = index;
+
+ return &pic->pic;
+ }
+
+ dat = (qpic_t *)COM_LoadTempFile (str);
+ if (!dat)
+ {
+ strcat (str, ".lmp");
+ dat = (qpic_t *)COM_LoadTempFile (str);
+ if (!dat)
+ {
+ Con_Printf ("Draw_CachePic: failed to load file %s\n", str);
+ return NULL;
+ }
+ }
+ SwapPic (dat);
+
+
+ pic->pic.width = dat->width;
+ pic->pic.height = dat->height;
+
+ gl = (glpic_t *)pic->pic.data;
+ gl->index = GL_LoadPicTexture (dat);
+
+ gltextures[gl->index].islmp = qtrue;
+ return &pic->pic;
+}
+
+/*
+================
+Draw_CachePic
+================
+*/
+qpic_t *Draw_CacheImg (char *path)
+{
+ cachepic_t *pic;
+ int i;
+ qpic_t *dat;
+ glpic_t *gl;
+
+ for (pic=menu_cachepics, i=0 ; iname))
+ return &pic->pic;
+
+ if (menu_numcachepics == MAX_CACHED_PICS)
+ Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");
+ menu_numcachepics++;
+ strcpy (pic->name, path);
+
+//
+// load the pic from disk
+//
+
+ int index = loadtextureimage (path, 0, 0, qfalse, GU_LINEAR);
+ if(index != -1)
+ {
+ pic->pic.width = gltextures[index].original_width;
+ pic->pic.height = gltextures[index].original_height;
+
+ gl = (glpic_t *)pic->pic.data;
+ gl->index = index;
+
+ return &pic->pic;
+ }
+
+ dat = (qpic_t *)COM_LoadTempFile (path);
+ if (!dat)
+ dat = (qpic_t *)COM_LoadTempFile ("gfx/error.lmp");
+ SwapPic (dat);
+
+ // HACK HACK HACK --- we need to keep the bytes for
+ // the translatable player picture just for the menu
+ // configuration dialog
+ if (!strcmp (path, "gfx/menuplyr.lmp"))
+ memcpy (menuplyr_pixels, dat->data, dat->width*dat->height);
+
+ pic->pic.width = dat->width;
+ pic->pic.height = dat->height;
+
+ gl = (glpic_t *)pic->pic.data;
+ gl->index = GL_LoadPicTexture (dat);
+
+ return &pic->pic;
+}
+
+
+byte nontexdt[8][8] =
+{
+ {0x43,0x43,0x43,0x43, 0x53,0x53,0x53,0x53},
+ {0x43,0x43,0x43,0x43, 0x53,0x53,0x53,0x53},
+ {0x43,0x43,0x43,0x43, 0x53,0x53,0x53,0x53},
+ {0x43,0x43,0x43,0x43, 0x53,0x53,0x53,0x53},
+
+ {0x53,0x53,0x53,0x53, 0x43,0x43,0x43,0x43},
+ {0x53,0x53,0x53,0x53, 0x43,0x43,0x43,0x43},
+ {0x53,0x53,0x53,0x53, 0x43,0x43,0x43,0x43},
+ {0x53,0x53,0x53,0x53, 0x43,0x43,0x43,0x43},
+
+};
+
+/*
+================
+R_CreateDlightImage
+from Quake3
+================
+*/
+#define DLIGHT_SIZE 16
+static void R_CreateDlightImage( void )
+{
+ int x,y;
+ byte data[DLIGHT_SIZE][DLIGHT_SIZE];
+ int b;
+
+ // make a centered inverse-square falloff blob for dynamic lighting
+ for (x=0 ; x 255)
+ {
+
+ b = 255;
+ }
+ else if ( b < 75 )
+ {
+ b = 0;
+ }
+
+ data[y][x] = b;
+ }
+ }
+ ref_texture = GL_LoadTexture ("reftexture", DLIGHT_SIZE, DLIGHT_SIZE, (byte*)data, qfalse, GU_LINEAR, 0);
+}
+
+/*
+===============
+Draw_Init
+===============
+*/
+void Draw_Init (void)
+{
+ byte *detail, *uw;
+ int i;
+
+ // load the console background and the charset
+ // by hand, because we need to write the version
+ // string into the background before turning
+ // it into a texture
+
+ nonetexture = GL_LoadTexture ("nonetexture", 8, 8, (byte*)nontexdt, qfalse, GU_NEAREST, 0);
+ R_CreateDlightImage();
+
+ // now turn them into textures
+ char_texture = loadtextureimage ("gfx/charset", 0, 0, qfalse, GU_NEAREST);
+ if (char_texture == 0)// did not find a matching TGA...
+ Sys_Error ("Could not load charset, make sure you have every folder and file installed properly\nDouble check that all of your files are in their correct places\nAnd that you have installed the game properly.\nRefer to the readme.txt file for help\n");
+
+ detail_texture = loadtextureimage ("gfx/detail", 0, 0, qfalse, GU_LINEAR);
+
+ if (detail_texture == 0)// did not find a matching TGA...
+ {
+ detail = static_cast(COM_LoadTempFile ("gfx/detail.lmp"));
+ if (!detail)
+ {
+ //Con_Printf ("Couldn't load gfx/detail \n"); FIXME no point?
+ detail_texture = nonetexture;
+ }
+ else
+ detail_texture = GL_LoadTexture ("Detail", 256, 256, detail, qfalse, GU_LINEAR, 3);
+ }
+
+ underwater_texture = loadtextureimage ("gfx/uwater", 0, 0, qfalse, GU_LINEAR);
+ if (underwater_texture == 0)// did not find a matching TGA...
+ {
+
+ uw = static_cast(COM_LoadTempFile ("gfx/uwater.lmp"));
+ if (!uw)
+ {
+ Con_Printf ("Couldn't load gfx/uwater \n");
+ underwater_texture = nonetexture;
+ }
+ else
+ underwater_texture = GL_LoadTexture ("Underwater", 256, 256, uw, qfalse, GU_LINEAR, 0);
+ }
+
+ sniper_scope = Draw_CachePic ("gfx/hud/scope");
+
+ zombie_skins[0][0] = loadtextureimage("/textures/ai/z0",0,0,qfalse,GU_LINEAR);
+ zombie_skins[0][1] = loadtextureimage("/textures/ai/z0",0,0,qfalse,GU_LINEAR);
+ zombie_skins[1][0] = loadtextureimage("/textures/ai/z0",0,0,qfalse,GU_LINEAR);
+ zombie_skins[1][1] = loadtextureimage("/textures/ai/z0",0,0,qfalse,GU_LINEAR);
+
+ Clear_LoadingFill ();
+}
+
+
+
+/*
+================
+Draw_Character
+
+Draws one 8*8 graphics character with 0 being transparent.
+It can be clipped to the top of the screen to allow the console to be
+smoothly scrolled off.
+================
+*/
+void Draw_Character (int x, int y, int num)
+{
+ int row, col;
+
+ if (num == 32)
+ return; // space
+
+ num &= 255;
+
+ if (y <= -8)
+ return; // totally off screen
+
+ row = num>>4;
+ col = num&15;
+
+ GL_Bind (char_texture);
+
+ struct vertex
+ {
+ short u, v;
+ short x, y, z;
+ };
+
+ vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2));
+
+ vertices[0].u = col * 8;
+ vertices[0].v = row * 8;
+ vertices[0].x = x;
+ vertices[0].y = y;
+ vertices[0].z = 0;
+
+ vertices[1].u = (col + 1) * 8;
+ vertices[1].v = (row + 1) * 8;
+ vertices[1].x = x + 8;
+ vertices[1].y = y + 8;
+ vertices[1].z = 0;
+
+ sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices);
+}
+
+
+/*
+================
+Draw_CharacterRGBA
+
+This is the same as Draw_Character, but with RGBA color codes.
+- MotoLegacy
+================
+*/
+extern cvar_t scr_coloredtext;
+void Draw_CharacterRGBA(int x, int y, int num, float r, float g, float b, float a)
+{
+ int row, col;
+
+ if (num == 32)
+ return; // space
+
+ num &= 255;
+
+ if (y <= -8)
+ return; // totally off screen
+
+ row = num>>4;
+ col = num&15;
+
+ GL_Bind (char_texture);
+
+ if (scr_coloredtext.value)
+ sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA);
+
+ struct vertex
+ {
+ short u, v;
+ short x, y, z;
+ };
+
+ vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2));
+
+ vertices[0].u = col * 8;
+ vertices[0].v = row * 8;
+ vertices[0].x = x;
+ vertices[0].y = y;
+ vertices[0].z = 0;
+
+ vertices[1].u = (col + 1) * 8;
+ vertices[1].v = (row + 1) * 8;
+ vertices[1].x = x + 8;
+ vertices[1].y = y + 8;
+ vertices[1].z = 0;
+
+ sceGuColor(GU_COLOR(r/255, g/255, b/255, a/255));
+ sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices);
+
+ if (scr_coloredtext.value)
+ sceGuTexFunc(GU_TFX_REPLACE , GU_TCC_RGBA);
+}
+
+/*
+================
+Draw_String
+================
+*/
+void Draw_String (int x, int y, char *str)
+{
+ while (*str)
+ {
+ Draw_Character (x, y, *str);
+ str++;
+ x += 8;
+ }
+}
+
+/*
+=============
+Draw_AlphaPic
+=============
+*/
+void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha)
+{
+ if (alpha != 1.0f)
+ {
+ sceGuTexFunc(GU_TFX_DECAL, GU_TCC_RGBA);
+ }
+
+ glpic_t *gl;
+
+ gl = (glpic_t *)pic->data;
+ if (!gl->index)
+ GL_Bind (nonetexture);
+ else
+ GL_Bind (gl->index);
+
+ struct vertex
+ {
+ short u, v;
+ short x, y, z;
+ };
+
+ vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2));
+
+ vertices[0].u = 0;
+ vertices[0].v = 0;
+ vertices[0].x = x;
+ vertices[0].y = y;
+ vertices[0].z = 0;
+
+ const gltexture_t& glt = gltextures[gl->index];
+ if (gltextures[gl->index].islmp)
+ {
+ vertices[1].u = glt.original_width;
+ vertices[1].v = glt.original_height;
+ }
+ else
+ {
+ vertices[1].u = glt.width;
+ vertices[1].v = glt.height;
+ }
+ vertices[1].x = x + pic->width;
+ vertices[1].y = y + pic->height;
+ vertices[1].z = 0;
+
+ sceGuColor(GU_RGBA(0xff, 0xff, 0xff, static_cast(alpha * 255.0f)));
+
+ sceGuDrawArray(
+ GU_SPRITES,
+ GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D,
+ 2, 0, vertices);
+
+ sceGuColor(0xffffffff);
+
+ if (alpha != 1.0f)
+ {
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+ }
+}
+
+
+/*
+=============
+Draw_ColorPic
+=============
+*/
+void Draw_ColorPic (int x, int y, qpic_t *pic, float r, float g , float b, float a)
+{
+ sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
+
+ glpic_t *gl;
+
+ gl = (glpic_t *)pic->data;
+ if (!gl->index)
+ GL_Bind (nonetexture);
+ else
+ GL_Bind (gl->index);
+
+ struct vertex
+ {
+ short u, v;
+ short x, y, z;
+ };
+
+ vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2));
+
+ vertices[0].u = 0;
+ vertices[0].v = 0;
+
+ vertices[0].x = x;
+ vertices[0].y = y;
+ vertices[0].z = 0;
+
+ const gltexture_t& glt = gltextures[gl->index];
+ if (gltextures[gl->index].islmp)
+ {
+ vertices[1].u = glt.original_width;
+ vertices[1].v = glt.original_height;
+ }
+ else
+ {
+ vertices[1].u = glt.width;
+ vertices[1].v = glt.height;
+ }
+
+ vertices[1].x = x + pic->width;
+ vertices[1].y = y + pic->height;
+ vertices[1].z = 0;
+
+ sceGuColor(GU_RGBA(
+ static_cast(r),
+ static_cast(g),
+ static_cast(b),
+ static_cast(a)));
+
+ sceGuDrawArray(
+ GU_SPRITES,
+ GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D,
+ 2, 0, vertices);
+
+ sceGuColor(0xffffffff);
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+}
+
+/*
+=============
+Draw_Pic
+=============
+*/
+void Draw_Pic (int x, int y, qpic_t *pic)
+{
+ Draw_AlphaPic(x, y, pic, 1.0f);
+}
+
+/*
+=============
+Draw_StretchPic
+=============
+*/
+void Draw_StretchPic (int x, int y, qpic_t *pic, int x_value, int y_value)
+{
+
+ glpic_t *gl;
+
+ gl = (glpic_t *)pic->data;
+ if (!gl->index)
+ GL_Bind (nonetexture);
+ else
+ GL_Bind (gl->index);
+
+ struct vertex
+ {
+ unsigned short u, v;
+ short x, y, z;
+ };
+
+
+ vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2));
+ const gltexture_t& glt = gltextures[gl->index];
+ vertices[0].u = 0;
+ vertices[0].v = 0;
+ vertices[0].x = x;
+ vertices[0].y = y;
+ vertices[0].z = 0;
+
+ if (gltextures[gl->index].islmp)
+ {
+ vertices[1].u = glt.original_width;
+ vertices[1].v = glt.original_height;
+ }
+ else
+ {
+ vertices[1].u = glt.width;
+ vertices[1].v = glt.height;
+ }
+ vertices[1].x = x + x_value;
+ vertices[1].y = y + y_value;
+ vertices[1].z = 0;
+
+ sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices);
+
+}
+/*
+=============
+Draw_TransPic
+=============
+*/
+void Draw_TransPic (int x, int y, qpic_t *pic)
+{
+
+ if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 ||
+ (unsigned)(y + pic->height) > vid.height)
+ {
+ Sys_Error ("Draw_TransPic: bad coordinates");
+ }
+
+ Draw_Pic (x, y, pic);
+}
+
+/*
+=============
+Draw_TransPicTranslate
+
+Only used for the player color selection menu
+=============
+*/
+void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation)
+{
+ int v, c;
+ unsigned trans[64*64];
+ int p;
+
+ c = pic->width * pic->height;
+
+ for(v = 0; v < c ; v++)
+ {
+ p = menuplyr_pixels[v];
+ if (p == 255)
+ trans[v] = p;
+ else
+ trans[v] = translation[p];
+ }
+ int translate_texture = GL_LoadTexture ("Player_Trl", pic->width, pic->height, (const byte*)trans, qtrue, GU_LINEAR, 0);
+
+ GL_Bind (translate_texture);
+
+ struct vertex
+ {
+ short u, v;
+ short x, y, z;
+ };
+
+ vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2));
+
+ vertices[0].u = 0;
+ vertices[0].v = 0;
+ vertices[0].x = x;
+ vertices[0].y = y;
+ vertices[0].z = 0;
+
+ vertices[1].u = 64;
+ vertices[1].v = 64;
+ vertices[1].x = x + pic->width;
+ vertices[1].y = y + pic->height;
+ vertices[1].z = 0;
+
+ sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices);
+
+ GL_UnloadTexture(translate_texture);
+}
+
+
+/*
+================
+Draw_ConsoleBackground
+
+================
+*/
+void Draw_ConsoleBackground (int lines)
+{
+ //int y = (vid.height * 3) >> 2;
+
+ if (con_forcedup)
+ Draw_Fill(0, 0, vid.width, lines, 0);
+ else
+ Draw_Fill(0, 0, vid.width, lines, 0);
+
+
+
+}
+/*
+================
+Draw_LoadingFill
+By Crow_bar
+================
+*/
+void Draw_LoadingFill(void)
+{
+ if(!loading_num_step)
+ return;
+
+ int size = 8;
+ int max_step = 350;
+ int x = (vid.width / 2) - (max_step / 2);
+ int y = vid.height - (size/ 2) - 10;
+ int l;
+ char str[64];
+
+
+ if(loading_cur_step > loading_num_step)
+ loading_cur_step = loading_num_step;
+
+ float loadsize = loading_cur_step * (max_step / loading_num_step);
+ Draw_FillByColor (x - 2, y - 2, max_step + 4, size + 4, GU_RGBA(255, 255, 255, 200));
+ Draw_FillByColor (x, y, loadsize, size, GU_RGBA(0, 0, 0, 200));
+
+ strcpy (str, "Loading: ");
+ strcat (str, loading_name);
+ l = strlen (str);
+ Draw_String((vid.width - l*8)/2, y, str);
+}
+
+void Clear_LoadingFill (void)
+{
+ //it is end loading
+ loading_cur_step = 0;
+ loading_num_step = 0;
+ memset(loading_name, 0, sizeof(loading_name));
+}
+
+/*
+=============
+Draw_FillByColor
+
+Fills a box of pixels with a single color
+=============
+*/
+void Draw_FillByColor (int x, int y, int w, int h, unsigned int c)
+{
+ struct vertex
+ {
+ short x, y, z;
+ };
+
+ vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2));
+
+ vertices[0].x = x;
+ vertices[0].y = y;
+ vertices[0].z = 0;
+
+ vertices[1].x = x + w;
+ vertices[1].y = y + h;
+ vertices[1].z = 0;
+
+ sceGuDisable(GU_TEXTURE_2D);
+ sceGuColor(c);
+ sceGuDrawArray(GU_SPRITES, GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices);
+ sceGuColor(0xffffffff);
+ sceGuEnable(GU_TEXTURE_2D);
+}
+
+void Draw_Fill (int x, int y, int w, int h, int c)
+{
+ struct vertex
+ {
+ short x, y, z;
+ };
+
+ vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2));
+
+ vertices[0].x = x;
+ vertices[0].y = y;
+ vertices[0].z = 0;
+
+ vertices[1].x = x + w;
+ vertices[1].y = y + h;
+ vertices[1].z = 0;
+
+ sceGuDisable(GU_TEXTURE_2D);
+ sceGuColor(GU_RGBA(host_basepal[c*3], host_basepal[c*3+1], host_basepal[c*3+2], 0xff));
+ sceGuDrawArray(GU_SPRITES, GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices);
+ sceGuColor(0xffffffff);
+ sceGuEnable(GU_TEXTURE_2D);
+}
+
+byte *StringToRGB (char *s)
+{
+ byte *col;
+ static byte rgb[4];
+
+ Cmd_TokenizeString (s);
+ if (Cmd_Argc() == 3)
+ {
+ rgb[0] = (byte)Q_atoi(Cmd_Argv(0));
+ rgb[1] = (byte)Q_atoi(Cmd_Argv(1));
+ rgb[2] = (byte)Q_atoi(Cmd_Argv(2));
+ }
+ else
+ {
+ col = (byte *)&d_8to24table[(byte)Q_atoi(s)];
+ rgb[0] = col[0];
+ rgb[1] = col[1];
+ rgb[2] = col[2];
+ }
+ rgb[3] = 255;
+
+ return rgb;
+}
+
+extern "C" cvar_t crosshair;
+extern cvar_t crosshair;
+extern qboolean croshhairmoving;
+//extern cvar_t cl_zoom;
+extern qpic_t *hitmark;
+double Hitmark_Time, crosshair_spread_time;
+float cur_spread;
+float crosshair_offset_step;
+
+int CrossHairWeapon (void)
+{
+ int i;
+ switch(cl.stats[STAT_ACTIVEWEAPON])
+ {
+ case W_COLT:
+ case W_BIATCH:
+ i = 15;
+ break;
+ case W_KAR:
+ case W_ARMAGEDDON:
+ i = 50;
+ break;
+ case W_THOMPSON:
+ case W_GIBS:
+ i = 10;
+ break;
+ case W_357:
+ case W_KILLU:
+ i = 10;
+ break;
+ case W_BAR:
+ case W_WIDOW:
+ i = 10;
+ break;
+ case W_BROWNING:
+ case W_ACCELERATOR:
+ i = 20;
+ break;
+ case W_DB:
+ case W_BORE:
+ i = 25;
+ break;
+ case W_FG:
+ case W_IMPELLER:
+ i = 10;
+ break;
+ case W_GEWEHR:
+ case W_COMPRESSOR:
+ i = 10;
+ break;
+ case W_KAR_SCOPE:
+ case W_HEADCRACKER:
+ i = 50;
+ break;
+ case W_M1:
+ case W_M1000:
+ i = 10;
+ break;
+ case W_M1A1:
+ case W_WIDDER:
+ i = 10;
+ break;
+ case W_MP40:
+ case W_AFTERBURNER:
+ i = 10;
+ break;
+ case W_MG:
+ case W_BARRACUDA:
+ i = 20;
+ break;
+ case W_PANZER:
+ case W_LONGINUS:
+ i = 0;
+ break;
+ case W_PPSH:
+ case W_REAPER:
+ i = 10;
+ break;
+ case W_PTRS:
+ case W_PENETRATOR:
+ i = 50;
+ break;
+ case W_RAY:
+ case W_PORTER:
+ i = 10;
+ break;
+ case W_SAWNOFF:
+ case W_SNUFF:
+ i = 30;
+ break;
+ case W_STG:
+ case W_SPATZ:
+ i = 10;
+ break;
+ case W_TRENCH:
+ case W_GUT:
+ i = 25;
+ break;
+ case W_TYPE:
+ case W_SAMURAI:
+ i = 10;
+ break;
+ case W_MP5:
+ i = 10;
+ break;
+ case W_TESLA:
+ i = 0;
+ break;
+ default:
+ i = 0;
+ break;
+ }
+
+ if (cl.perks & 64)
+ i *= 0.65;
+
+ return i;
+}
+int CrossHairMaxSpread (void)
+{
+ int i;
+ switch(cl.stats[STAT_ACTIVEWEAPON])
+ {
+ case W_COLT:
+ case W_BIATCH:
+ i = 30;
+ break;
+ case W_KAR:
+ case W_ARMAGEDDON:
+ i = 75;
+ break;
+ case W_THOMPSON:
+ case W_GIBS:
+ i = 25;
+ break;
+ case W_357:
+ case W_KILLU:
+ i = 20;
+ break;
+ case W_BAR:
+ case W_WIDOW:
+ i = 35;
+ break;
+ case W_BROWNING:
+ case W_ACCELERATOR:
+ i = 50;
+ break;
+ case W_DB:
+ case W_BORE:
+ i = 25;
+ break;
+ case W_FG:
+ case W_IMPELLER:
+ i = 40;
+ break;
+ case W_GEWEHR:
+ case W_COMPRESSOR:
+ i = 35;
+ break;
+ case W_KAR_SCOPE:
+ case W_HEADCRACKER:
+ i = 75;
+ break;
+ case W_M1:
+ case W_M1000:
+ i = 35;
+ break;
+ case W_M1A1:
+ case W_WIDDER:
+ i = 35;
+ break;
+ case W_MP40:
+ case W_AFTERBURNER:
+ i = 25;
+ break;
+ case W_MG:
+ case W_BARRACUDA:
+ i = 50;
+ break;
+ case W_PANZER:
+ case W_LONGINUS:
+ i = 0;
+ break;
+ case W_PPSH:
+ case W_REAPER:
+ i = 25;
+ break;
+ case W_PTRS:
+ case W_PENETRATOR:
+ i = 75;
+ break;
+ case W_RAY:
+ case W_PORTER:
+ i = 20;
+ break;
+ case W_SAWNOFF:
+ case W_SNUFF:
+ i = 30;
+ break;
+ case W_STG:
+ case W_SPATZ:
+ i = 35;
+ break;
+ case W_TRENCH:
+ case W_GUT:
+ i = 25;
+ break;
+ case W_TYPE:
+ case W_SAMURAI:
+ i = 25;
+ break;
+ case W_MP5:
+ i = 25;
+ break;
+ case W_TESLA:
+ i = 0;
+ break;
+ default:
+ i = 0;
+ break;
+ }
+
+ if (cl.perks & 64)
+ i *= 0.65;
+
+ return i;
+}
+
+/*
+================
+Draw_Crosshair
+================
+*/
+
+extern float crosshair_opacity;
+void Draw_Crosshair (void)
+{
+ if (cl.stats[STAT_HEALTH] < 20)
+ return;
+
+ if (!crosshair_opacity)
+ crosshair_opacity = 255;
+
+ float col;
+
+ if (sv_player->v.facingenemy == 1) {
+ col = 0;
+ } else {
+ col = 255;
+ }
+
+ // crosshair moving
+ if (crosshair_spread_time > sv.time && crosshair_spread_time)
+ {
+ cur_spread = cur_spread + 10;
+ crosshair_opacity = 128;
+
+ if (cur_spread >= CrossHairMaxSpread())
+ cur_spread = CrossHairMaxSpread();
+ }
+ // crosshair not moving
+ else if (crosshair_spread_time < sv.time && crosshair_spread_time)
+ {
+ cur_spread = cur_spread - 4;
+ crosshair_opacity = 255;
+
+ if (cur_spread <= 0) {
+ cur_spread = 0;
+ crosshair_spread_time = 0;
+ }
+ }
+
+ if (cl.stats[STAT_ACTIVEWEAPON] == W_M2 || cl.stats[STAT_ACTIVEWEAPON] == W_TESLA || cl.stats[STAT_ACTIVEWEAPON] == W_DG3)
+ {
+ Draw_CharacterRGBA((vid.width)/2-4, (vid.height)/2, 'O', 255, col, col, crosshair_opacity);
+ }
+ else if (crosshair.value == 1 && cl.stats[STAT_ZOOM] != 1 && cl.stats[STAT_ZOOM] != 2 && cl.stats[STAT_ACTIVEWEAPON] != W_PANZER)
+ {
+ int x_value, y_value;
+ int crosshair_offset = CrossHairWeapon() + cur_spread;
+ if (CrossHairMaxSpread() < crosshair_offset || croshhairmoving)
+ crosshair_offset = CrossHairMaxSpread();
+
+ if (sv_player->v.view_ofs[2] == 8) {
+ crosshair_offset *= 0.80;
+ } else if (sv_player->v.view_ofs[2] == -10) {
+ crosshair_offset *= 0.65;
+ }
+
+ crosshair_offset_step += (crosshair_offset - crosshair_offset_step) * 0.5;
+
+ x_value = (vid.width - 8)/2 - crosshair_offset_step;
+ y_value = (vid.height - 8)/2;
+ Draw_CharacterRGBA(x_value, y_value, 158, 255, col, col, crosshair_opacity);
+
+ x_value = (vid.width - 8)/2 + crosshair_offset_step;
+ y_value = (vid.height - 8)/2;
+ Draw_CharacterRGBA(x_value, y_value, 158, 255, col, col, crosshair_opacity);
+
+ x_value = (vid.width - 8)/2;
+ y_value = (vid.height - 8)/2 - crosshair_offset_step;
+ Draw_CharacterRGBA(x_value, y_value, 157, 255, col, col, crosshair_opacity);
+
+ x_value = (vid.width - 8)/2;
+ y_value = (vid.height - 8)/2 + crosshair_offset_step;
+ Draw_CharacterRGBA(x_value, y_value, 157, 255, col, col, crosshair_opacity);
+ }
+ else if (crosshair.value && cl.stats[STAT_ZOOM] != 1 && cl.stats[STAT_ZOOM] != 2)
+ Draw_CharacterRGBA((vid.width - 8)/2, (vid.height - 8)/2, '.', 255, col, col, crosshair_opacity);
+ if (cl.stats[STAT_ZOOM] == 2)
+ Draw_Pic (0, 0, sniper_scope);
+ if (Hitmark_Time > sv.time)
+ Draw_Pic ((vid.width - hitmark->width)/2,(vid.height - hitmark->height)/2, hitmark);
+}
+
+/*
+================
+Draw_ColoredString
+================
+*/
+
+static int HexToInt(char c)
+{
+ if (isdigit(c))
+ return c - '0';
+ else if (c >= 'a' && c <= 'f')
+ return 10 + c - 'a';
+ else if (c >= 'A' && c <= 'F')
+ return 10 + c - 'A';
+ else
+ return -1;
+}
+
+// Creds to UP Team for scale code - Moto
+void Draw_ColoredString(int x, int y, char *text, float r, float g, float b, float a, int scale)
+{
+ int num;
+ qboolean white = qtrue;
+
+ if (y <= -8)
+ return; // totally off screen
+
+ if (!*text)
+ return;
+
+ GL_Bind (char_texture);
+
+
+
+ if (scr_coloredtext.value)
+ sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA);
+
+ for ( ; *text; text++)
+ {
+
+ // MotoLegacy - Added 0-255 RGBA support (5/26/2020)
+ sceGuColor(GU_COLOR(r/255, g/255, b/255, a/255));
+
+ num = *text & 255;
+
+ if (num != 32 && num != (32 | 128))
+ {
+ float frow, fcol;
+
+ frow = num>>4;
+ fcol = num&15;
+
+ struct vertex
+ {
+ short u, v;
+ short x, y, z;
+ };
+
+ vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2));
+
+ vertices[0].u = fcol * 8;
+ vertices[0].v = frow * 8;
+ vertices[0].x = x;
+ vertices[0].y = y;
+ vertices[0].z = 0;
+
+ vertices[1].u = (fcol + 1) * 8;
+ vertices[1].v = (frow + 1) * 8;
+ vertices[1].x = x + (8*scale);
+ vertices[1].y = y + (8*scale);
+ vertices[1].z = 0;
+
+ sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices);
+ }
+ x += 8*scale;
+ }
+
+ if (!white)
+ sceGuColor(GU_COLOR(1,1,1,1));
+
+ if (scr_coloredtext.value)
+ sceGuTexFunc(GU_TFX_REPLACE , GU_TCC_RGBA);
+}
+
+//=============================================================================
+#ifdef NAA
+//font.c : font.raw
+// bin2c font.raw font.c font
+//font.c
+extern"C"
+{
+ #include "font.c"
+}
+,.,.,
+static int fontwidthtab[128] =
+{
+ 10, 10, 10, 10,
+ 10, 10, 10, 10,
+ 10, 10, 10, 10,
+ 10, 10, 10, 10,
+
+ 10, 10, 10, 10,
+ 10, 10, 10, 10,
+ 10, 10, 10, 10,
+ 10, 10, 10, 10,
+
+ 10, 6, 8, 10, // ! " #
+ 10, 10, 10, 6, // $ % & '
+ 10, 10, 10, 10, // ( ) * +
+ 6, 10, 6, 10, // , - . /
+
+ 10, 10, 10, 10, // 0 1 2 3
+ 10, 10, 10, 10, // 6 5 8 7
+ 10, 10, 6, 6, // 10 9 : ;
+ 10, 10, 10, 10, // < = > ?
+
+ 16, 10, 10, 10, // @ A B C
+ 10, 10, 10, 10, // D E F G
+ 10, 6, 8, 10, // H I J K
+ 8, 10, 10, 10, // L M N O
+
+ 10, 10, 10, 10, // P Q R S
+ 10, 10, 10, 12, // T U V W
+ 10, 10, 10, 10, // X Y Z [
+ 10, 10, 8, 10, // \ ] ^ _
+
+ 6, 8, 8, 8, // ` a b c
+ 8, 8, 6, 8, // d e f g
+ 8, 6, 6, 8, // h i j k
+ 6, 10, 8, 8, // l m n o
+
+ 8, 8, 8, 8, // p q r s
+ 8, 8, 8, 12, // t u v w
+ 8, 8, 8, 10, // x y z {
+ 8, 10, 8, 12 // | } ~
+};
+
+void Draw_FrontText(const char* text, int x, int y, unsigned int color, int fw) //Crow_bar
+{
+ int len = (int)strlen(text);
+
+ if(!len)
+ {
+ return;
+ }
+
+ // Set the texture mode.
+ sceGuTexMode(GU_PSM_8888, 0, 0, 0);
+ sceGuTexImage(0, 256, 128, 256, font);
+ sceGuTexFilter(GU_NEAREST, GU_NEAREST);
+
+ sceGuShadeModel(GU_SMOOTH);
+
+ sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
+ sceGuDepthMask(GU_TRUE);
+
+ typedef struct
+ {
+ float s, t;
+ unsigned int c;
+ float x, y, z;
+ } vertex;
+
+ vertex* const v = static_cast(sceGuGetMemory(sizeof(vertex) * 2 * len));
+
+ int i;
+ for(i = 0; i < len; i++)
+ {
+ unsigned char c = (unsigned char)text[i];
+ if(c < 32)
+ {
+ c = 0;
+ }
+ else if(c >= 128)
+ {
+ c = 0;
+ }
+
+ int tx = (c & 0x0F) << 4;
+ int ty = (c & 0xF0);
+
+ vertex* v0 = &v[i*2+0];
+ vertex* v1 = &v[i*2+1];
+
+ v0->s = (float)(tx + (fw ? ((16 - fw) >> 1) : ((16 - fontwidthtab[c]) >> 1)));
+ v0->t = (float)(ty);
+ v0->c = color;
+ v0->x = (float)(x);
+ v0->y = (float)(y);
+ v0->z = 0.0f;
+
+ v1->s = (float)(tx + 16 - (fw ? ((16 - fw) >> 1) : ((16 - fontwidthtab[c]) >> 1)));
+ v1->t = (float)(ty + 16);
+ v1->c = color;
+ v1->x = (float)(x + (fw ? fw : fontwidthtab[c]));
+ v1->y = (float)(y + 16);
+ v1->z = 0.0f;
+
+ x += (fw ? fw : fontwidthtab[c]);
+ }
+ sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_2D, len * 2, 0, v);
+
+ sceGuShadeModel(GU_FLAT);
+
+ sceGuDepthMask(GU_FALSE);
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+}
+
+void Draw_FrontTest (void)
+{
+ Draw_FrontText("red", 0, 0, 0xFF0000FF, 0);
+ Draw_FrontText("green", 0, 16, 0xFF00FF00, 0);
+ Draw_FrontText("blue", 0, 32, 0xFFFF0000, 0);
+
+ Draw_FrontText("free char width", 0, 64, 0xFFFFFFFF, 0);
+ Draw_FrontText("block char width 10", 0, 80, 0xFFFFFFFF, 10);
+ Draw_FrontText("block char width 16", 0, 96, 0xFFFFFFFF, 16);
+
+ Draw_FrontText("opacity 100%", 0, 128, 0xFFFFFFFF, 0);
+ Draw_FrontText("opacity 50%", 0, 144, 0x7FFFFFFF, 0);
+ Draw_FrontText("opacity 10%", 0, 160, 0x18FFFFFF, 0);
+
+ Draw_FrontText("I'm Crow_bar", 2, 194, 0x40FFFFFF, 0);
+ Draw_FrontText("I'm Crow_bar", 0, 192, 0xFFFFFFFF, 0);
+
+ static float t = 0.0f;
+ t += 0.1f;
+
+ unsigned int c = 0xFF000000 |
+ (unsigned int)((sinf(t * 0.393f + 0.086f) / 2.0f + 0.5f) * 255.0f) << 16 |
+ (unsigned int)((sinf(t * 0.444f + 0.854f) / 2.0f + 0.5f) * 255.0f) << 8 |
+ (unsigned int)((sinf(t * 0.117f + 1.337f) / 2.0f + 0.5f) * 255.0f) << 0;
+
+ Draw_FrontText("Hello World from pspdev", 0, 224, c, 0);
+}
+#endif
+//=============================================================================
+
+/*
+================
+Draw_FadeScreen
+
+================
+*/
+void Draw_FadeScreen (void)
+{
+ struct vertex
+ {
+ short x, y, z;
+ };
+
+ vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2));
+
+ vertices[0].x = 0;
+ vertices[0].y = 0;
+ vertices[0].z = 0;
+ vertices[1].x = vid.width;
+ vertices[1].y = vid.height;
+ vertices[1].z = 0;
+
+ sceGuDisable(GU_TEXTURE_2D);
+
+ sceGuColor(GU_RGBA(0, 0, 0, 0x80));
+ sceGuDrawArray(
+ GU_SPRITES,
+ GU_VERTEX_16BIT | GU_TRANSFORM_2D,
+ 2, 0, vertices);
+
+ sceGuEnable(GU_TEXTURE_2D);
+}
+
+//=============================================================================
+
+/*
+================
+GL_Set2D
+
+Setup as if the screen was 320*200
+================
+*/
+void GL_Set2D (void)
+{
+ sceGuViewport (glx, gly, glwidth, glheight);
+ sceGuScissor(0, 0, glwidth, glheight);
+
+ sceGuEnable(GU_BLEND);
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+}
+
+
+/*
+================
+swizzle_slow
+================
+*/
+void swizzle(u8* out, const u8* in, unsigned int width, unsigned int height)
+{
+ unsigned int i,j;
+ unsigned int rowblocks = (width / 16);
+
+ for (j = 0; j < height; ++j)
+ {
+ for (i = 0; i < width; ++i)
+ {
+ unsigned int blockx = i / 16;
+ unsigned int blocky = j / 8;
+
+ unsigned int x = (i - blockx*16);
+ unsigned int y = (j - blocky*8);
+ unsigned int block_index = blockx + ((blocky) * rowblocks);
+ unsigned int block_address = block_index * 16 * 8;
+
+ out[block_address + x + y * 16] = in[i+j*width];
+ }
+ }
+}
+
+/*
+================
+swizzle_fast
+================
+*/
+static void swizzle_fast(u8* out, const u8* in, unsigned int width, unsigned int height)
+{
+ unsigned int blockx, blocky;
+ unsigned int j;
+
+ unsigned int width_blocks = (width / 16);
+ unsigned int height_blocks = (height / 8);
+
+ unsigned int src_pitch = (width-16)/4;
+ unsigned int src_row = width * 8;
+
+ const u8* ysrc = in;
+ u32* dst = (u32*)out;
+
+ for (blocky = 0; blocky < height_blocks; ++blocky)
+ {
+ const u8* xsrc = ysrc;
+ for (blockx = 0; blockx < width_blocks; ++blockx)
+ {
+ const u32* src = (u32*)xsrc;
+ for (j = 0; j < 8; ++j)
+ {
+ *(dst++) = *(src++);
+ *(dst++) = *(src++);
+ *(dst++) = *(src++);
+ *(dst++) = *(src++);
+ src += src_pitch;
+ }
+ xsrc += 16;
+ }
+ ysrc += src_row;
+ }
+}
+
+
+
+/*
+================
+GL_ResampleTexture for 8 bit
+================
+*/
+void GL_ResampleTexture(const byte *in, int inwidth, int inheight, unsigned char *out, int outwidth, int outheight)
+{
+ const unsigned int fracstep = inwidth * 0x10000 / outwidth;
+ for (int i = 0; i < outheight ; ++i, out += outwidth)
+ {
+ const byte* inrow = in + inwidth * (i * inheight / outheight);
+ unsigned int frac = fracstep >> 1;
+ for (int j = 0; j < outwidth; ++j, frac += fracstep)
+ {
+ out[j] = inrow[frac >> 16];
+ }
+ }
+}
+
+/*
+================
+GL_Upload8
+================
+*/
+void GL_Upload8(int texture_index, const byte *data, int width, int height)
+{
+ if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false)
+ {
+ Sys_Error("Invalid texture index %d", texture_index);
+ }
+
+ const gltexture_t& texture = gltextures[texture_index];
+
+ // Check that the texture matches.
+ if ((width != texture.original_width) != (height != texture.original_height))
+ {
+ Sys_Error("Attempting to upload a texture which doesn't match the destination");
+ }
+
+ // Create a temporary buffer to use as a source for swizzling.
+ std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0);
+ std::vector unswizzled(buffer_size);
+
+
+ if (texture.mipmaps > 0) {
+ int size_incr = buffer_size/4;
+ for (int i= 1;i <= texture.mipmaps;i++) {
+ buffer_size += size_incr;
+ size_incr = size_incr/4;
+ }
+ }
+
+ // Do we need to resize?
+ if (texture.stretch_to_power_of_two)
+ {
+ // Resize.
+ GL_ResampleTexture(data, width, height, &unswizzled[0], texture.width, texture.height);
+ }
+ else
+ {
+ // Straight copy.
+ for (int y = 0; y < height; ++y)
+ {
+ const byte* const src = data + (y * width);
+ byte* const dst = &unswizzled[y * texture.width];
+ memcpy(dst, src, width);
+ }
+ }
+
+ // Swizzle to system RAM.
+ swizzle_fast(texture.ram, &unswizzled[0], texture.width, texture.height);
+
+ if (texture.mipmaps > 0)
+ {
+ int size = (texture.width * texture.height);
+ int offset = size;
+ int div = 2;
+
+ for (int i = 1; i <= texture.mipmaps;i++)
+ {
+ GL_ResampleTexture(data, width, height, &unswizzled[0], texture.width/div, texture.height/div);
+ swizzle_fast(texture.ram+offset, &unswizzled[0], texture.width/div, texture.height/div);
+ offset += size/(div*div);
+ div *=2;
+ }
+ }
+
+ unswizzled.clear();
+
+ // Copy to VRAM?
+ if (texture.vram)
+ {
+ // Copy.
+ memcpy(texture.vram, texture.ram, buffer_size);
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.vram, buffer_size);
+ }
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.ram, buffer_size);
+}
+
+/*
+================
+GL_Upload8_A
+================
+*/
+void GL_Upload8_A(int texture_index, const byte *data, int width, int height)
+{
+ if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false)
+ {
+ Sys_Error("Invalid texture index %d", texture_index);
+ }
+
+ const gltexture_t& texture = gltextures[texture_index];
+
+ // Check that the texture matches.
+ if ((width != texture.original_width) != (height != texture.original_height))
+ {
+ Sys_Error("Attempting to upload a texture which doesn't match the destination");
+ }
+
+ // Create a temporary buffer to use as a source for swizzling.
+ const std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0);
+ memcpy((void *) texture.ram, (void *) data, buffer_size);
+
+
+ // Copy to VRAM?
+ if (texture.vram)
+ {
+ // Copy.
+ memcpy(texture.vram, texture.ram, buffer_size);
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.vram, buffer_size);
+ }
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.ram, buffer_size);
+}
+/*
+================
+GL_Upload16_A
+================
+*/
+void GL_Upload16_A(int texture_index, const byte *data, int width, int height)
+{
+ if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false)
+ {
+ Sys_Error("Invalid texture index %d", texture_index);
+ }
+
+ const gltexture_t& texture = gltextures[texture_index];
+
+ // Check that the texture matches.
+ if ((width != texture.original_width) != (height != texture.original_height))
+ {
+ Sys_Error("Attempting to upload a texture which doesn't match the destination");
+ }
+
+ // Create a temporary buffer to use as a source for swizzling.
+ const std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0);
+ memcpy((void *) texture.ram, (void *) data, buffer_size);
+
+ // Copy to VRAM?
+ if (texture.vram)
+ {
+ // Copy.
+ memcpy(texture.vram, texture.ram, buffer_size);
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.vram, buffer_size);
+ }
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.ram, buffer_size);
+}
+
+/*
+=======================================
+TextureConvector
+=======================================
+From DE2 by Christoph Arnold "charnold"
+modify by Crow_bar
+=======================================
+*/
+void TextureConvector(unsigned char *in32, unsigned char *out16, int w, int h, int format)
+{
+ int texel;
+
+ int size = w * h;
+
+ for (texel = 0; texel < size; texel++)
+ {
+ if (format == GU_PSM_4444)
+ {
+ *(out16) = (*in32>>4) & 0x0f; in32++; // r
+ *(out16++) |= (*in32) & 0xf0; in32++; // g
+ *(out16) = (*in32>>4) & 0x0f; in32++; // b
+ *(out16++) |= (*in32) & 0xf0; in32++; // a
+ }
+ else if (format == GU_PSM_5650)
+ {
+ unsigned char r,g,b;
+
+ r = (*in32>>3) & 0x1f; in32++; // r = 5 bit
+ g = (*in32>>2) & 0x3f; in32++; // g = 6 bit
+ b = (*in32>>3) & 0x1f; in32++; // b = 5 bit
+ in32++; // a = 0 bit
+
+ *(out16) = r; // alle 5 bits von r auf lower 5 bits von out16
+ *(out16++) |= (g<<5) & 0xe0; // lower 3 bits von g auf higher 3 bits von out16
+ *(out16) = (g>>3) & 0x07; // higher 3 bits von g auf lower 3 bits von out16
+ *(out16++) |= (b<<3) & 0xf8; // alle 5 bits von b auf higher 5 bits von out16
+
+ }
+ else if (format == GU_PSM_5551)
+ {
+ unsigned char r,g,b,a;
+
+ r = (*in32>>3) & 0x1f; in32++; // r = 5 bit
+ g = (*in32>>3) & 0x1f; in32++; // g = 5 bit
+ b = (*in32>>3) & 0x1f; in32++; // b = 5 bit
+ a = (*in32>>7) & 0x01; in32++; // a = 1 bit
+
+ *(out16) = r; // alle 5 bits von r auf lower 5 bits von out16
+ *(out16++) |= (g<<5) & 0xe0; // lower 3 bits von g auf higher 3 bits von out16
+ *(out16) = (g>>3) & 0x03; // higher 2 bits von g auf lower 2 bits von out16
+ *(out16) |= (b<<2) & 0x7c; // alle 5 bits von b auf bits 3-7 von out16
+ *(out16++) |= (a<<7) & 0x80; // 1 bit von a auf bit 8 von out16
+ }
+ }
+}
+
+/*
+================
+GL_Upload16_B
+================
+*/
+void GL_Upload32_16(int texture_index, const byte *data, int width, int height)
+{
+ if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false)
+ {
+ Sys_Error("Invalid texture index %d", texture_index);
+ }
+
+ const gltexture_t& texture = gltextures[texture_index];
+
+ // Check that the texture matches.
+ if ((width != texture.original_width) != (height != texture.original_height))
+ {
+ Sys_Error("Attempting to upload a texture which doesn't match the destination");
+ }
+
+ // Create a temporary buffer to use as a source for swizzling.
+ std::size_t buffer_sizesrc = GL_GetTexSize(-1, texture.width, texture.height, 4);
+ std::vector unswizzled(buffer_sizesrc);
+
+ std::size_t buffer_sizedst = GL_GetTexSize(texture.format, texture.width, texture.height, 0);
+ std::vector bpp(buffer_sizesrc);
+
+
+ if (texture.stretch_to_power_of_two)
+ {
+ // Resize.
+ Image_Resample ((void*)data, width, height, &unswizzled[0], texture.width, texture.height, texture.bpp, int(r_restexf.value));
+ }
+ else
+ {
+ // Straight copy.
+ for (int y = 0; y < height; ++y)
+ {
+ const byte* const src = data + (y * width * texture.bpp);
+ byte* const dst = &unswizzled[y * texture.width * texture.bpp];
+ memcpy(dst, src, width * texture.bpp);
+ }
+ }
+
+ TextureConvector((unsigned char*)&unswizzled[0], (unsigned char*)&bpp[0], texture.width, texture.height, texture.format);
+ unswizzled.clear();
+
+ // Swizzle to system RAM.
+ swizzle_fast(texture.ram, &bpp[0], texture.width * 2, texture.height);
+ bpp.clear();
+
+
+ // Copy to VRAM?
+ if (texture.vram)
+ {
+ // Copy.
+ memcpy(texture.vram, texture.ram, buffer_sizedst);
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.vram, buffer_sizedst);
+ }
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.ram, buffer_sizedst);
+}
+
+
+/*
+================
+GL_Upload16
+================
+*/
+void GL_Upload16(int texture_index, const byte *data, int width, int height)
+{
+ if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false)
+ {
+ Sys_Error("Invalid texture index %d", texture_index);
+ }
+
+ const gltexture_t& texture = gltextures[texture_index];
+
+ // Check that the texture matches.
+ if ((width != texture.original_width) != (height != texture.original_height))
+ {
+ Sys_Error("Attempting to upload a texture which doesn't match the destination");
+ }
+
+ // Create a temporary buffer to use as a source for swizzling.
+ const std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0);
+ std::vector unswizzled(buffer_size);
+
+
+ // Straight copy.
+ for (int y = 0; y < height; ++y)
+ {
+ const byte* const src = data + (y * width * texture.bpp);
+ byte* const dst = &unswizzled[y * texture.width * texture.bpp];
+ memcpy(dst, src, width * texture.bpp);
+ }
+
+ // Swizzle to system RAM.
+ swizzle_fast(texture.ram, &unswizzled[0], texture.width * texture.bpp, texture.height);
+ unswizzled.clear();
+
+
+ // Copy to VRAM?
+ if (texture.vram)
+ {
+ // Copy.
+ memcpy(texture.vram, texture.ram, buffer_size);
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.vram, buffer_size);
+ }
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.ram, buffer_size);
+}
+
+/*
+===============
+GL_Upload24_A
+===============
+*/
+void GL_Upload24_A(int texture_index, const byte *data, int width, int height)
+{
+ if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false)
+ {
+ Sys_Error("Invalid texture index %d", texture_index);
+ }
+
+ const gltexture_t& texture = gltextures[texture_index];
+
+ // Check that the texture matches.
+ if ((width != texture.original_width) != (height != texture.original_height))
+ {
+ Sys_Error("Attempting to upload a texture which doesn't match the destination");
+ }
+
+ // Create a temporary buffer to use as a source for swizzling.
+ const std::size_t buffer_size = GL_GetTexSize(-1, texture.width, texture.height, texture.bpp);
+
+ //resize Crow_bar PSP port 24BIT RESAMPLER
+ if (texture.stretch_to_power_of_two)
+ {
+ Image_Resample ((void*)data, width, height, (void *) texture.ram, texture.width, texture.height, texture.bpp, int(r_restexf.value));
+ }
+ else
+ {
+ memcpy((void *) texture.ram, (void *) data, buffer_size);
+ }
+
+
+ // Copy to VRAM?
+ if (texture.vram)
+ {
+ // Copy.
+ memcpy(texture.vram, texture.ram, buffer_size);
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.vram, buffer_size);
+ }
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.ram, buffer_size);
+}
+
+/*
+================
+GL_Upload24
+================
+*/
+void GL_Upload24(int texture_index, const byte *data, int width, int height)
+{
+ if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false)
+ {
+ Sys_Error("Invalid texture index %d", texture_index);
+ }
+
+ const gltexture_t& texture = gltextures[texture_index];
+
+ // Check that the texture matches.
+ if ((width != texture.original_width) != (height != texture.original_height))
+ {
+ Sys_Error("Attempting to upload a texture which doesn't match the destination");
+ }
+
+ // Create a temporary buffer to use as a source for swizzling.
+ std::size_t buffer_size = GL_GetTexSize(-1, texture.width, texture.height, texture.bpp);
+ std::vector unswizzled(buffer_size);
+
+ if (texture.mipmaps > 0)
+ {
+ int size_incr = buffer_size/4;
+
+ for (int i= 1;i <= texture.mipmaps;i++)
+ {
+ buffer_size += size_incr;
+ size_incr = size_incr/4;
+ }
+ }
+
+ //32BIT resize and swizzler by Crow_bar PSP port
+ // Do we need to resize?
+ if (texture.stretch_to_power_of_two)
+ {
+ // Resize.
+ Image_Resample ((void*)data, width, height, &unswizzled[0], texture.width, texture.height, texture.bpp, int(r_restexf.value));
+ }
+ else
+ {
+ // Straight copy.
+ for (int y = 0; y < height; ++y)
+ {
+ const byte* const src = data + (y * width * texture.bpp);
+ byte* const dst = &unswizzled[y * texture.width * texture.bpp];
+ memcpy(dst, src, width * texture.bpp);
+ }
+ }
+
+ // Swizzle to system RAM.
+ swizzle_fast(texture.ram, &unswizzled[0], texture.width * texture.bpp, texture.height);
+
+ if (texture.mipmaps > 0)
+ {
+ int size = GL_GetTexSize(-1, texture.width, texture.height, texture.bpp);
+ int offset = size;
+ int div = 2;
+
+ for (int i = 1; i <= texture.mipmaps;i++)
+ {
+ Image_Resample((void*)data, width, height, &unswizzled[0],
+ texture.width/div, texture.height/div, texture.bpp, int(r_restexf.value));
+ swizzle_fast(texture.ram+offset, &unswizzled[0], (texture.width/div) * texture.bpp, texture.height/div);
+ offset += size/(div*div);
+ div *=2;
+ }
+ }
+
+ unswizzled.clear();
+
+ // Copy to VRAM?
+ if (texture.vram)
+ {
+ // Copy.
+ memcpy(texture.vram, texture.ram, buffer_size);
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.vram, buffer_size);
+ }
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.ram, buffer_size);
+}
+
+/*
+===============
+GL_Upload32_A
+===============
+*/
+void GL_Upload32_A(int texture_index, const byte *data, int width, int height)
+{
+ if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false)
+ {
+ Sys_Error("Invalid texture index %d", texture_index);
+ }
+
+ gltexture_t& texture = gltextures[texture_index];
+
+ // Check that the texture matches.
+ if ((width != texture.original_width) != (height != texture.original_height))
+ {
+ Sys_Error("Attempting to upload a texture which doesn't match the destination");
+ }
+
+ // Create a temporary buffer to use as a source for swizzling.
+ std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0);
+
+ //resize Crow_bar PSP port 32BIT RESAMPLER
+ if (texture.stretch_to_power_of_two)
+ {
+ Image_Resample ((void*)data, width, height, (void *) texture.ram, texture.width, texture.height, texture.bpp, int(r_restexf.value));
+ }
+ else
+ {
+ memcpy((void *) texture.ram, (void *) data, buffer_size);
+ }
+
+ // Copy to VRAM?
+ if (texture.vram)
+ {
+ // Copy.
+ memcpy(texture.vram, texture.ram, buffer_size);
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.vram, buffer_size);
+ }
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.ram, buffer_size);
+}
+
+/*
+================
+GL_Upload32
+================
+*/
+void GL_Upload32(int texture_index, const byte *data, int width, int height)
+{
+ if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false)
+ {
+ Sys_Error("Invalid texture index %d", texture_index);
+ }
+
+ //const
+ gltexture_t& texture = gltextures[texture_index];
+
+ // Check that the texture matches.
+ if ((width != texture.original_width) != (height != texture.original_height))
+ {
+ Sys_Error("Attempting to upload a texture which doesn't match the destination");
+ }
+
+ // Create a temporary buffer to use as a source for swizzling.
+ std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0);
+ std::vector unswizzled(buffer_size);
+
+ if (texture.mipmaps > 0)
+ {
+ int size_incr = buffer_size/4;
+
+ for (int i= 1;i <= texture.mipmaps;i++)
+ {
+ buffer_size += size_incr;
+ size_incr = size_incr/4;
+ }
+ }
+
+ //32BIT resize and swizzler by Crow_bar PSP port
+ // Do we need to resize?
+ if (texture.stretch_to_power_of_two)
+ {
+ // Resize.
+ Image_Resample ((void*)data, width, height, &unswizzled[0], texture.width, texture.height, texture.bpp, int(r_restexf.value));
+ }
+ else
+ {
+ // Straight copy.
+ for (int y = 0; y < height; ++y)
+ {
+ const byte* const src = data + (y * width * texture.bpp);
+ byte* const dst = &unswizzled[y * texture.width * texture.bpp];
+ memcpy(dst, src, width * texture.bpp);
+ }
+ }
+
+ // Swizzle to system RAM.
+ swizzle_fast(texture.ram, &unswizzled[0], texture.width * texture.bpp, texture.height);
+
+
+ if (texture.mipmaps > 0)
+ {
+ int size = GL_GetTexSize(texture.format, texture.width, texture.height, 0);
+ int offset = size;
+ int div = 2;
+
+ for (int i = 1; i <= texture.mipmaps;i++)
+ {
+ Image_Resample((void*)data, width, height, &unswizzled[0],
+ texture.width/div, texture.height/div, texture.bpp, int(r_restexf.value));
+ swizzle_fast(texture.ram+offset, &unswizzled[0], (texture.width/div) * texture.bpp, texture.height/div);
+ offset += size/(div*div);
+ div *=2;
+ }
+ }
+
+ unswizzled.clear();
+
+ // Copy to VRAM?
+ if (texture.vram)
+ {
+ // Copy.
+ memcpy(texture.vram, texture.ram, buffer_size);
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.vram, buffer_size);
+ }
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.ram, buffer_size);
+}
+
+/*
+================
+GL_UploadDXT
+================
+*/
+void GL_UploadDXT(int texture_index, const byte *data, int width, int height)
+{
+ if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false)
+ {
+ Sys_Error("Invalid texture index %d", texture_index);
+ }
+
+ const gltexture_t& texture = gltextures[texture_index];
+
+ // Check that the texture matches.
+ if ((width != texture.original_width) != (height != texture.original_height))
+ {
+ Sys_Error("Attempting to upload a texture which doesn't match the destination");
+ }
+
+ // Create a temporary buffer to use as a source for swizzling.
+ std::size_t buffer_sizesrc = GL_GetTexSize(-1, texture.width, texture.height, texture.bpp);
+ std::vector unswizzled(buffer_sizesrc);
+
+ // New compressed texture
+ std::size_t buffer_sizedst = GL_GetTexSize(texture.format, texture.width, texture.height, 0);
+
+ if (texture.stretch_to_power_of_two)
+ {
+ // Resize.
+ Image_Resample ((void*)data, width, height, &unswizzled[0], texture.width, texture.height, texture.bpp, int(r_restexf.value));
+ }
+ else
+ {
+ // Straight copy.
+ for (int y = 0; y < height; ++y)
+ {
+ const byte* const src = data + (y * width * texture.bpp);
+ byte* const dst = &unswizzled[y * texture.width * texture.bpp];
+ memcpy(dst, src, width * texture.bpp);
+ }
+ }
+
+ tx_compress_dxtn(texture.bpp, texture.width, texture.height,(const unsigned char *)&unswizzled[0], texture.format, (unsigned char *)texture.ram);
+ unswizzled.clear();
+ // Copy to VRAM?
+ if (texture.vram)
+ {
+ // Copy.
+ memcpy(texture.vram, texture.ram, buffer_sizedst);
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.vram, buffer_sizedst);
+ }
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.ram, buffer_sizedst);
+}
+
+static std::size_t round_up(std::size_t size)
+{
+ static const float denom = 1.0f / logf(2.0f);
+ const float logged = logf(size) * denom;
+ const float ceiling = ceilf(logged);
+ return 1 << static_cast(ceiling);
+}
+
+
+static std::size_t round_down(std::size_t size)
+{
+ static const float denom = 1.0f / logf(2.0f);
+ const float logged = logf(size) * denom;
+ const float floor = floorf(logged);
+ return 1 << static_cast(floor);
+}
+
+/*
+================
+GL_UnloadTexture
+================
+*/
+void GL_UnloadTexture(int texture_index)
+{
+
+ if (gltextures_used[texture_index] == true)
+ {
+ gltexture_t& texture = gltextures[texture_index];
+
+ Con_DPrintf("Unloading: %s,%d\n",texture.identifier, texture.bpp);
+ // Source.
+ strcpy(texture.identifier,"");
+ texture.original_width = 0;
+ texture.original_height = 0;
+ texture.stretch_to_power_of_two = qfalse;
+
+ // Fill in the texture description.
+ texture.format = GU_PSM_T8;
+ texture.bpp = 0;
+ texture.filter = GU_LINEAR;
+ texture.width = 0;
+ texture.height = 0;
+ texture.mipmaps = 0;
+ texture.swizzle = 0;
+#ifdef STATIC_PAL
+ memset(texture.palette, 0, sizeof(texture.palette));
+#else
+ if (texture.palette != NULL)
+ {
+ free(texture.palette);
+ texture.palette = NULL;
+ }
+ if (texture.palette_2 != NULL)
+ {
+ free(texture.palette_2);
+ texture.palette_2 = NULL;
+ }
+#endif
+ texture.palette_active = qfalse;
+ if (texture.palette != NULL)
+ {
+ free(texture.palette);
+ texture.palette = NULL;
+ }
+ if (texture.palette_2 != NULL)
+ {
+ free(texture.palette_2);
+ texture.palette_2 = NULL;
+ }
+
+ // Buffers.
+ if (texture.ram != NULL)
+ {
+ free(texture.ram);
+ texture.ram = NULL;
+ }
+ /*if (texture.vram != NULL)
+ {
+ vfree(texture.vram);
+ texture.vram = NULL;
+ } */
+
+ }
+
+ gltextures_used[texture_index] = false;
+ numgltextures--;
+
+}
+
+/*
+================
+GL_LoadTexture
+================
+*/
+int GL_LoadTexture (const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level)
+{
+ int texture_index = -1;
+
+ tex_scale_down = r_tex_scale_down.value == qtrue;
+ // See if the texture is already present.
+ if (identifier[0])
+ {
+ for (int i = 0; i < MAX_GLTEXTURES; ++i)
+ {
+ if (gltextures_used[i] == true)
+ {
+ const gltexture_t& texture = gltextures[i];
+ if (!strcmp (identifier, texture.identifier))
+ {
+ return i;
+ }
+ }
+ }
+ }
+
+ // Out of textures?
+ if (numgltextures == MAX_GLTEXTURES)
+ {
+ Sys_Error("Out of OpenGL textures");
+ }
+
+ // Use the next available texture.
+ numgltextures++;
+ texture_index = numgltextures;
+
+ for (int i = 0; i < MAX_GLTEXTURES; ++i)
+ {
+ if (gltextures_used[i] == false) {
+ texture_index = i;
+ break;
+ }
+ }
+ gltexture_t& texture = gltextures[texture_index];
+ gltextures_used[texture_index] = true;
+
+ // Fill in the source data.
+ strcpy(texture.identifier, identifier);
+ texture.original_width = width;
+ texture.original_height = height;
+ texture.stretch_to_power_of_two = stretch_to_power_of_two != qfalse;
+
+ // Fill in the texture description.
+ texture.format = GU_PSM_T8;
+ texture.filter = filter;
+ texture.mipmaps = mipmap_level;
+ texture.swizzle = GU_TRUE;
+ texture.bpp = 1;
+#ifdef STATIC_PAL
+ memset(texture.palette, 0, sizeof(texture.palette));
+#endif
+ texture.palette_active = qfalse;
+
+ if (tex_scale_down == true && texture.stretch_to_power_of_two == true)
+ {
+ texture.width = std::max(round_down(width), 32U);
+ texture.height = std::max(round_down(height),32U);
+ }
+ else
+ {
+ texture.width = std::max(round_up(width), 32U);
+ texture.height = std::max(round_up(height),32U);
+ }
+
+ for (int i=0; i <= mipmap_level;i++)
+ {
+ int div = (int) powf(2,i);
+ if ((texture.width / div) > 16 && (texture.height / div) > 16 ) {
+ texture.mipmaps = i;
+ }
+ }
+
+ // Do we really need to resize the texture?
+ if (texture.stretch_to_power_of_two)
+ {
+ // Not if the size hasn't changed.
+ texture.stretch_to_power_of_two = (texture.width != width) || (texture.height != height);
+ }
+
+ Con_DPrintf("Loading: %s [%dx%d](%0.2f KB)\n",texture.identifier,texture.width,texture.height, (float) (texture.width*texture.height)/1024);
+
+ // Allocate the RAM.
+ std::size_t buffer_size = texture.width * texture.height;
+
+ if (texture.mipmaps > 0) {
+ int size_incr = buffer_size/4;
+ for (int i= 1;i <= texture.mipmaps;i++) {
+ buffer_size += size_incr;
+ size_incr = size_incr/4;
+ }
+ }
+
+ texture.ram = static_cast(memalign(16, buffer_size));
+
+ if (!texture.ram)
+ {
+ Sys_Error("Out of RAM for textures.");
+ }
+
+ // Allocate the VRAM.
+ texture.vram = static_cast(valloc(buffer_size));
+
+ // Upload the texture.
+ GL_Upload8(texture_index, data, width, height);
+
+ if (texture.vram && texture.ram)
+ {
+ free(texture.ram);
+ texture.ram = NULL;
+ }
+ // Done.
+ return texture_index;
+}
+
+/*
+================
+GL_LoadPalTex
+================
+*/
+int GL_LoadPalTex (const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level, byte *palette, int paltype)
+{
+
+ int texture_index = -1;
+
+ tex_scale_down = r_tex_scale_down.value == qtrue;
+ // See if the texture is already present.
+ if (identifier[0])
+ {
+ for (int i = 0; i < MAX_GLTEXTURES; ++i)
+ {
+ if (gltextures_used[i] == true)
+ {
+ const gltexture_t& texture = gltextures[i];
+ if (!strcmp (identifier, texture.identifier))
+ {
+ return i;
+ }
+ }
+ }
+ }
+
+ // Out of textures?
+ if (numgltextures == MAX_GLTEXTURES)
+ {
+ Sys_Error("Out of OpenGL textures");
+ }
+
+ // Use the next available texture.
+ numgltextures++;
+ texture_index = numgltextures;
+
+ for (int i = 0; i < MAX_GLTEXTURES; ++i)
+ {
+ if (gltextures_used[i] == false)
+ {
+ texture_index = i;
+ break;
+ }
+ }
+ gltexture_t& texture = gltextures[texture_index];
+ gltextures_used[texture_index] = true;
+
+ // Fill in the source data.
+ strcpy(texture.identifier, identifier);
+ texture.original_width = width;
+ texture.original_height = height;
+ texture.stretch_to_power_of_two = stretch_to_power_of_two != qfalse;
+
+ // Fill in the texture description.
+ texture.format = GU_PSM_T8;
+ texture.filter = filter;
+ texture.mipmaps = mipmap_level;
+ texture.swizzle = GU_TRUE;
+ texture.bpp = 1;
+
+ // Upload the Palette
+ if((paltype == PAL_RGB && palette) ||
+ (paltype == PAL_RGBA && palette) ||
+ (paltype == PAL_Q2 && palette == NULL) ||
+ (paltype == PAL_H2 && palette == NULL) )
+ {
+#ifndef STATIC_PAL
+ if(paltype == PAL_Q2)
+ {
+ texture.palette_2 = d_8to24tableQ2; //hard coded palette
+ }
+ else if(paltype == PAL_H2)
+ {
+ texture.palette_2 = d_8to24tableH2; //hard coded palette
+ }
+ else
+ {
+ texture.palette_2 = static_cast(memalign(16, sizeof(ScePspRGBA8888) * 256));
+ if(!texture.palette_2)
+ {
+ Sys_Error("Out of RAM for palettes.");
+ }
+#endif
+ if(paltype == PAL_RGBA)
+ {
+ // Convert the palette to PSP format.
+ for (ScePspRGBA8888* color = &texture.palette_2[0]; color < &texture.palette_2[256]; ++color)
+ {
+ const unsigned int r = gammatable[*palette++];
+ const unsigned int g = gammatable[*palette++];
+ const unsigned int b = gammatable[*palette++];
+ const unsigned int a = gammatable[*palette++];
+ *color = GU_RGBA(r, g, b, a);
+ }
+ }
+ else if(paltype == PAL_RGB)
+ {
+ // Convert the palette to PSP format.
+ for (ScePspRGBA8888* color = &texture.palette_2[0]; color < &texture.palette_2[256]; ++color)
+ {
+ const unsigned int r = gammatable[*palette++];
+ const unsigned int g = gammatable[*palette++];
+ const unsigned int b = gammatable[*palette++];
+ *color = GU_RGBA(r, g, b, 0xff);
+ }
+ }
+#ifndef STATIC_PAL
+ }
+#endif
+ texture.palette_2[255] = 0; //alpha color
+ texture.palette_active = qtrue;
+ }
+ else
+ {
+ Sys_Error("GL_LoadPalTex: Unknow palette type");
+ }
+
+
+ if (tex_scale_down == true && texture.stretch_to_power_of_two == true)
+ {
+ texture.width = std::max(round_down(width), 32U);
+ texture.height = std::max(round_down(height),32U);
+ }
+ else
+ {
+ texture.width = std::max(round_up(width), 32U);
+ texture.height = std::max(round_up(height),32U);
+ }
+
+ for (int i=0; i <= mipmap_level;i++)
+ {
+ int div = (int) powf(2,i);
+ if ((texture.width / div) > 16 && (texture.height / div) > 16 )
+ {
+ texture.mipmaps = i;
+ }
+ }
+
+ // Do we really need to resize the texture?
+ if (texture.stretch_to_power_of_two)
+ {
+ // Not if the size hasn't changed.
+ texture.stretch_to_power_of_two = (texture.width != width) || (texture.height != height);
+ }
+
+ Con_DPrintf("Loading TEX_PAL: %s [%dx%d](%0.2f KB)\n",texture.identifier,texture.width,texture.height, (float) ((texture.width*texture.height)/1024) + 256);
+
+ // Allocate the RAM.
+ std::size_t buffer_size = texture.width * texture.height;
+
+ if (texture.mipmaps > 0)
+ {
+ int size_incr = buffer_size/4;
+ for (int i= 1;i <= texture.mipmaps;i++)
+ {
+ buffer_size += size_incr;
+ size_incr = size_incr/4;
+ }
+ }
+
+ texture.ram = static_cast(memalign(16, buffer_size));
+
+ if (!texture.ram)
+ {
+ Sys_Error("Out of RAM for textures.");
+ }
+
+ // Allocate the VRAM.
+ texture.vram = static_cast(valloc(buffer_size));
+
+ // Upload the texture.
+ GL_Upload8(texture_index, data, width, height);
+
+ if (texture.vram && texture.ram)
+ {
+ free(texture.ram);
+ texture.ram = NULL;
+ }
+ // Done.
+ return texture_index;
+}
+
+
+/*
+================
+GL_LoadTextureLM
+================
+*/
+int GL_LoadTextureLM (const char *identifier, int width, int height, const byte *data, int bpp, int filter, qboolean update, int forcopy)
+{
+ tex_scale_down = r_tex_scale_down.value == qtrue;
+ int texture_index = -1;
+ // See if the texture is already present.
+ if (identifier[0])
+ {
+ for (int i = 0; i < MAX_GLTEXTURES; ++i)
+ {
+ if (gltextures_used[i] == true)
+ {
+ const gltexture_t& texture = gltextures[i];
+ if (!strcmp (identifier, texture.identifier))
+ {
+ if (update == qfalse)
+ {
+ return i;
+ }
+ else
+ {
+ texture_index = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (update == qfalse || texture_index == -1)
+ {
+ // Out of textures?
+ if (numgltextures == MAX_GLTEXTURES)
+ {
+ Sys_Error("Out of OpenGL textures");
+ }
+
+ // Use the next available texture.
+ numgltextures++;
+ texture_index = numgltextures;
+
+ for (int i = 0; i < MAX_GLTEXTURES; ++i)
+ {
+ if (gltextures_used[i] == false)
+ {
+ texture_index = i;
+ break;
+ }
+ }
+ gltexture_t& texture = gltextures[texture_index];
+ gltextures_used[texture_index] = true;
+
+ // Fill in the source data.
+ strcpy(texture.identifier, identifier);
+ texture.original_width = width;
+ texture.original_height = height;
+ texture.stretch_to_power_of_two = false;
+ texture.bpp = bpp;
+
+#ifdef STATIC_PAL
+ memset(texture.palette, 0, sizeof(texture.palette));
+#endif
+ texture.palette_active = qfalse;
+
+ // Fill in the texture description.
+ switch(texture.bpp)
+ {
+ case 1:
+ texture.format = GU_PSM_T8;
+ break;
+ case 2:
+ texture.format = GU_PSM_4444;
+ break;
+ case 3:
+ texture.format = GU_PSM_8888;
+ break;
+ case 4:
+ texture.format = GU_PSM_8888;
+ break;
+ default:
+ Sys_Error("Failed bpp!");
+ }
+
+ texture.filter = filter;
+ texture.mipmaps = 0;
+
+ if(forcopy)
+ {
+ texture.swizzle = GU_FALSE;
+ }
+ else
+ {
+ texture.swizzle = GU_TRUE;
+ }
+
+ if (tex_scale_down == true && texture.stretch_to_power_of_two == true)
+ {
+ texture.width = std::max(round_down(width), 16U);
+ texture.height = std::max(round_down(height), 16U);
+ }
+ else
+ {
+ texture.width = std::max(round_up(width), 16U);
+ texture.height = std::max(round_up(height), 16U);
+ }
+
+ // Allocate the RAM.
+ const std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0);
+ texture.ram = static_cast(memalign(16, buffer_size));
+ if (!texture.ram)
+ {
+ Sys_Error("Out of RAM for lightmap textures.");
+ }
+
+ // Allocate the VRAM.
+ texture.vram = static_cast(valloc(buffer_size));
+
+ // Upload the texture.
+ if(!texture.swizzle)
+ {
+ switch(texture.bpp)
+ {
+ case 1:
+ GL_Upload8_A(texture_index, data, width, height);
+ break;
+ case 2:
+ GL_Upload16_A(texture_index, data, width, height);
+ break;
+ case 3:
+ GL_Upload24_A(texture_index, data, width, height);
+ break;
+ case 4:
+ GL_Upload32_A(texture_index, data, width, height);
+ break;
+ default:
+ Sys_Error("Failed bpp!");
+ }
+ }
+ else
+ {
+ switch(texture.bpp)
+ {
+ case 1:
+ GL_Upload8(texture_index, data, width, height);
+ break;
+ case 2:
+ GL_Upload16(texture_index, data, width, height);
+ break;
+ case 3:
+ GL_Upload24(texture_index, data, width, height);
+ break;
+ case 4:
+ GL_Upload32(texture_index, data, width, height);
+ break;
+ default:
+ Sys_Error("Failed bpp!");
+ }
+ }
+
+ if (texture.vram && texture.ram)
+ {
+ free(texture.ram);
+ texture.ram = NULL;
+ }
+ }
+ else
+ {
+ gltexture_t& texture = gltextures[texture_index];
+
+ if ((width == texture.original_width) && (height == texture.original_height))
+ {
+
+ // Upload the texture.
+ if(!texture.swizzle)
+ {
+ switch(texture.bpp)
+ {
+ case 1:
+ GL_Upload8_A(texture_index, data, width, height);
+ break;
+ case 2:
+ GL_Upload16_A(texture_index, data, width, height);
+ break;
+ case 3:
+ GL_Upload24_A(texture_index, data, width, height);
+ break;
+ case 4:
+ GL_Upload32_A(texture_index, data, width, height);
+ break;
+ default:
+ Sys_Error("Failed bpp!");
+ }
+ }
+ else
+ {
+ switch(texture.bpp)
+ {
+ case 1:
+ GL_Upload8(texture_index, data, width, height);
+ break;
+ case 2:
+ GL_Upload16(texture_index, data, width, height);
+ break;
+ case 3:
+ GL_Upload24(texture_index, data, width, height);
+ break;
+ case 4:
+ GL_Upload32(texture_index, data, width, height);
+ break;
+ default:
+ Sys_Error("Failed bpp!");
+ }
+ }
+ }
+ if (texture.vram && texture.ram)
+ {
+ free(texture.ram);
+ texture.ram = NULL;
+ }
+ }
+ // Done.
+ return texture_index;
+}
+
+/*
+================
+GL_LoadImages
+================
+*/
+int GL_LoadImages (const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level, int bpp)
+{
+ int texture_index = -1;
+
+ tex_scale_down = r_tex_scale_down.value == qtrue;
+
+ // See if the texture is already present.
+ if (identifier[0])
+ {
+ for (int i = 0; i < MAX_GLTEXTURES; ++i)
+ {
+ if (gltextures_used[i] == true)
+ {
+ const gltexture_t& texture = gltextures[i];
+ if (!strcmp (identifier, texture.identifier))
+ {
+ return i;
+ }
+ }
+ }
+ }
+
+ // Out of textures?
+ if (numgltextures == MAX_GLTEXTURES)
+ { Sys_Error("Out of OpenGL textures");
+ }
+
+ // Use the next available texture.
+ numgltextures++;
+ texture_index = numgltextures;
+
+ for (int i = 0; i < MAX_GLTEXTURES; ++i)
+ {
+ if (gltextures_used[i] == false)
+ {
+ texture_index = i;
+ break;
+ }
+ }
+
+ gltexture_t& texture = gltextures[texture_index];
+ gltextures_used[texture_index] = true;
+
+ // Fill in the source data.
+ strcpy(texture.identifier, identifier);
+ texture.original_width = width;
+ texture.original_height = height;
+ texture.stretch_to_power_of_two = stretch_to_power_of_two != qfalse;
+ texture.bpp = bpp;
+#ifdef STATIC_PAL
+ memset(texture.palette, 0, sizeof(texture.palette));
+#endif
+ texture.palette_active = qfalse;
+
+ // Fill in the texture description.
+ switch(texture.bpp)
+ {
+ case 1:
+ texture.format = GU_PSM_T8;
+ break;
+ case 2:
+ texture.format = GU_PSM_4444; //5650, 5551, T16(pal)
+ break;
+ case 4:
+ switch((int)r_texcompr.value)
+ {
+ // case 32:
+ case 0:
+ texture.format = GU_PSM_8888; //T32(pal)
+ break;
+ case 1:
+ texture.format = GU_PSM_DXT1;
+ break;
+ case 3:
+ texture.format = GU_PSM_DXT3;
+ break;
+ case 16:
+ texture.format = GU_PSM_4444;
+ break;
+ case 5:
+ default:
+ texture.format = GU_PSM_DXT5;
+ break;
+ }
+ break;
+ }
+
+ texture.filter = filter;
+ if(texture.format < GU_PSM_DXT1)
+ {
+ texture.mipmaps = mipmap_level;
+ }
+ else
+ {
+ texture.mipmaps = 0;
+ }
+
+ switch(texture.format)
+ {
+ case GU_PSM_T8:
+ case GU_PSM_4444:
+ case GU_PSM_8888:
+ texture.swizzle = GU_TRUE;
+ break;
+ case GU_PSM_DXT1:
+ case GU_PSM_DXT3:
+ case GU_PSM_DXT5:
+ texture.swizzle = GU_FALSE;
+ break;
+ }
+
+ if (tex_scale_down == true && texture.stretch_to_power_of_two == true)
+ {
+ texture.width = std::max(round_down(width), 32U);
+ texture.height = std::max(round_down(height),32U);
+ }
+ else
+ {
+ texture.width = std::max(round_up(width), 32U);
+ texture.height = std::max(round_up(height),32U);
+ }
+
+ if(texture.format < GU_PSM_DXT1)
+ {
+ for (int i=0; i <= mipmap_level;i++)
+ {
+ int div = (int) powf(2,i);
+ if((texture.width / div) > 16 && (texture.height / div) > 16 )
+ {
+ texture.mipmaps = i;
+ }
+ }
+ }
+
+ // Do we really need to resize the texture?
+ if (texture.stretch_to_power_of_two)
+ {
+ // Not if the size hasn't changed.
+ texture.stretch_to_power_of_two = (texture.width != width) || (texture.height != height);
+ }
+
+ // Allocate the RAM.
+ std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0);
+
+ Con_DPrintf("Loading: %s [%dx%d](%0.2f KB)\n",texture.identifier,texture.width,texture.height, (float) buffer_size/1024);
+
+ //Con_Printf("Loading: %s [%dx%d](%d Bytes) (Format: %d, BPP: %d)\n",texture.identifier,texture.width,texture.height, buffer_size,texture.format,bpp);
+
+ if (texture.mipmaps > 0 && texture.format < GU_PSM_DXT1)
+ {
+ int size_incr = buffer_size/4;
+ for (int i= 1;i <= texture.mipmaps;i++)
+ {
+ buffer_size += size_incr;
+ size_incr = size_incr/4;
+ }
+ }
+
+ texture.ram = static_cast(memalign(16, buffer_size));
+
+ if (!texture.ram)
+ {
+ Sys_Error("Out of RAM for images.");
+ }
+
+ // Allocate the VRAM.
+ texture.vram = static_cast(valloc(buffer_size));
+ // Upload the texture.
+ switch(texture.format)
+ {
+ case GU_PSM_T8:
+ GL_Upload8(texture_index, data, width, height);
+ break;
+ case GU_PSM_4444:
+ GL_Upload16(texture_index, data, width, height);
+ break;
+ case GU_PSM_8888:
+ GL_Upload32(texture_index, data, width, height);
+ break;
+ case GU_PSM_DXT1:
+ case GU_PSM_DXT3:
+ case GU_PSM_DXT5:
+ GL_UploadDXT(texture_index, data, width, height);
+ break;
+ }
+ if (texture.vram && texture.ram)
+ {
+ free(texture.ram);
+ texture.ram = NULL;
+ }
+ // Done.
+ return texture_index;
+}
+
+int GL_LoadTexture8Pal32 (char *identifier, int width, int height, byte *data, byte *pal)
+{
+ int i, s;
+ s = width*height;
+
+ if (s&3)
+ Sys_Error ("GL_Upload8: s&3");
+
+ byte *trans = (byte*)memalign(16, s*4);
+ for (i=0 ; i= MAX_GLTEXTURES) || gltextures_used[texture_index] == false)
+ {
+ Sys_Error("Invalid texture index %d", texture_index);
+ }
+
+ const gltexture_t& texture = gltextures[texture_index];
+
+ // Check that the texture matches.
+ if ((width != texture.original_width) != (height != texture.original_height))
+ {
+ Sys_Error("Attempting to upload a texture which doesn't match the destination");
+ }
+
+ // Create a temporary buffer to use as a source for swizzling.
+ std::size_t buffer_size = (width * height) / 2;
+
+ memcpy(texture.ram, data, buffer_size);
+ memcpy(texture.palette, data + buffer_size, 16 * 4);
+ int i;
+
+ // Copy to VRAM?
+ /*if (texture.vram)
+ {
+ // Copy.
+ memcpy(texture.vram, texture.ram, buffer_size);
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.vram, buffer_size);
+ }*/
+
+ // Flush the data cache.
+ sceKernelDcacheWritebackRange(texture.ram, buffer_size);
+}
+
+int GL_LoadTexture4(const char *identifier, unsigned int width, unsigned int height, const byte *data, int filter)
+{
+ int texture_index = -1;
+
+ tex_scale_down = r_tex_scale_down.value == qtrue;
+
+ if (identifier[0])
+ {
+ for (int i = 0; i < MAX_GLTEXTURES; ++i)
+ {
+ if (gltextures_used[i] == true)
+ {
+ const gltexture_t& texture = gltextures[i];
+ if (!strcmp(identifier, texture.identifier))
+ {
+ return i;
+ }
+ }
+ }
+ }
+
+ // Out of textures?
+ if (numgltextures == MAX_GLTEXTURES)
+ {
+ Sys_Error("Out of OpenGL textures");
+ }
+
+ // Use the next available texture.
+ numgltextures++;
+ texture_index = numgltextures;
+
+ for (int i = 0; i < MAX_GLTEXTURES; ++i)
+ {
+ if (gltextures_used[i] == false) {
+ texture_index = i;
+ break;
+ }
+ }
+ gltexture_t& texture = gltextures[texture_index];
+ gltextures_used[texture_index] = true;
+
+ // Fill in the source data.
+ strcpy(texture.identifier, identifier);
+ texture.original_width = texture.width = width;
+ texture.original_height = texture.height = height;
+ texture.stretch_to_power_of_two = qfalse;
+
+ // Fill in the texture description.
+ texture.format = GU_PSM_T4;
+ texture.filter = filter;
+ texture.mipmaps = 0; // mipmap_level;
+
+
+ Con_DPrintf("Loading: %s [%dx%d](%0.2f KB)\n", texture.identifier, texture.width, texture.height, (float)(texture.width*texture.height) / 1024);
+
+ // Allocate the RAM.
+ std::size_t buffer_size = (texture.width * texture.height) / 2; // 4bpp
+
+ texture.ram = static_cast(memalign(16, buffer_size));
+ texture.palette = static_cast(memalign(16, 16 * 4));
+
+ if (!texture.ram || !texture.palette)
+ {
+ Sys_Error("Out of RAM for textures.");
+ }
+
+ // Allocate the VRAM.
+ //texture.vram = static_cast(quake::vram::allocate(buffer_size));
+
+ // Upload the texture.
+ GL_Upload4(texture_index, data, width, height);
+
+ if (texture.vram && texture.ram) {
+ free(texture.ram);
+ texture.ram = NULL;
+ }
+ // Done.
+ return texture_index;
+}
diff --git a/source/psp/video_hardware_dxtn.cpp b/source/psp/video_hardware_dxtn.cpp
new file mode 100644
index 0000000..bc3bfb6
--- /dev/null
+++ b/source/psp/video_hardware_dxtn.cpp
@@ -0,0 +1,842 @@
+/*
+ * libtxc_dxtn
+ * Version: 0.1b
+ *
+ * Fixed some bugs with dxt1 compression
+ *
+ * Copyright (C) 2004 Roland Scheidegger
+ * All Rights Reserved.
+ * Copyright (C) 2006-2008 Franck Charlet
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and / or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include
+#include
+
+#include
+
+#include "video_hardware_dxtn.h"
+
+
+/* weights used for error function, basically weights (unsquared 2/4/1) according to rgb->luminance conversion
+ not sure if this really reflects visual perception */
+#define REDWEIGHT 4
+#define GREENWEIGHT 16
+#define BLUEWEIGHT 1
+
+#define ALPHACUT 127
+
+// ----------------------------------------------------
+// Convert the DXTx buffers for the PSP
+
+static void Convert_DXT5(unsigned char *data, unsigned int size)
+{
+ unsigned short *src = (unsigned short *) data;
+ int i;
+ int j;
+ unsigned short converted[8];
+
+ for(j = 0; size >= 16; size -= 16, j++) {
+ converted[4] = src[1];
+ converted[5] = src[2];
+ converted[6] = src[3];
+ converted[7] = src[0];
+
+ converted[0] = src[6];
+ converted[1] = src[7];
+ converted[2] = src[4];
+ converted[3] = src[5];
+ for(i = 0; i < 8; i++) src[i] = converted[i];
+ src += 8;
+ }
+}
+
+static void Convert_DXT3(unsigned char *data, unsigned int size)
+{
+ unsigned short *src = (unsigned short *) data;
+ int i;
+ int j;
+ unsigned short converted[8];
+
+ for(j = 0; size >= 16; size -= 16, j++) {
+ converted[4] = src[0];
+ converted[5] = src[1];
+ converted[6] = src[2];
+ converted[7] = src[3];
+
+ converted[0] = src[6];
+ converted[1] = src[7];
+ converted[2] = src[4];
+ converted[3] = src[5];
+ for(i = 0; i < 8; i++) src[i] = converted[i];
+ src += 8;
+ }
+}
+
+static void Convert_DXT1(unsigned char *data, unsigned int size)
+{
+ unsigned short *src = (unsigned short *) data;
+ int i;
+ int j;
+ unsigned short converted[4];
+
+ for(j = 0; size >= 8; size -= 8, j++) {
+ converted[0] = src[2];
+ converted[1] = src[3];
+ converted[2] = src[0];
+ converted[3] = src[1];
+ for(i = 0; i < 4; i++) src[i] = converted[i];
+ src += 4;
+ }
+}
+
+// ----------------------------------------------------
+
+static void fancybasecolorsearch( unsigned char *blkaddr, unsigned char srccolors[4][4][4], unsigned char *bestcolor[2], int numxpixels, int numypixels, int type, int haveAlpha) {
+ /* use same luminance-weighted distance metric to determine encoding as for finding the base colors */
+
+ int i, j, colors, z;
+ unsigned int pixerror, pixerrorred, pixerrorgreen, pixerrorblue, pixerrorbest;
+ int colordist, blockerrlin[2][3];
+ unsigned char nrcolor[2];
+ int pixerrorcolorbest[3];
+ unsigned char enc = 0;
+ unsigned char cv[4][4];
+ unsigned char testcolor[2][3];
+
+ if(((bestcolor[0][0] & 0xf8) << 8 | (bestcolor[0][1] & 0xfc) << 3 | bestcolor[0][2] >> 3) <
+ ((bestcolor[1][0] & 0xf8) << 8 | (bestcolor[1][1] & 0xfc) << 3 | bestcolor[1][2] >> 3)) {
+ testcolor[0][0] = bestcolor[0][0];
+ testcolor[0][1] = bestcolor[0][1];
+ testcolor[0][2] = bestcolor[0][2];
+ testcolor[1][0] = bestcolor[1][0];
+ testcolor[1][1] = bestcolor[1][1];
+ testcolor[1][2] = bestcolor[1][2];
+ }
+ else {
+ testcolor[1][0] = bestcolor[0][0];
+ testcolor[1][1] = bestcolor[0][1];
+ testcolor[1][2] = bestcolor[0][2];
+ testcolor[0][0] = bestcolor[1][0];
+ testcolor[0][1] = bestcolor[1][1];
+ testcolor[0][2] = bestcolor[1][2];
+ }
+
+ for(i = 0; i < 3; i ++) {
+ cv[0][i] = testcolor[0][i];
+ cv[1][i] = testcolor[1][i];
+ cv[2][i] = (testcolor[0][i] * 2 + testcolor[1][i]) / 3;
+ cv[3][i] = (testcolor[0][i] + testcolor[1][i] * 2) / 3;
+ }
+
+ blockerrlin[0][0] = 0;
+ blockerrlin[0][1] = 0;
+ blockerrlin[0][2] = 0;
+ blockerrlin[1][0] = 0;
+ blockerrlin[1][1] = 0;
+ blockerrlin[1][2] = 0;
+
+ nrcolor[0] = 0;
+ nrcolor[1] = 0;
+
+ for(j = 0; j < numypixels; j++) {
+ for(i = 0; i < numxpixels; i++) {
+ pixerrorbest = 0xffffffff;
+ for(colors = 0; colors < 4; colors++) {
+ colordist = srccolors[j][i][0] - (cv[colors][0]);
+ pixerror = colordist * colordist * REDWEIGHT;
+ pixerrorred = colordist;
+ colordist = srccolors[j][i][1] - (cv[colors][1]);
+ pixerror += colordist * colordist * GREENWEIGHT;
+ pixerrorgreen = colordist;
+ colordist = srccolors[j][i][2] - (cv[colors][2]);
+ pixerror += colordist * colordist * BLUEWEIGHT;
+ pixerrorblue = colordist;
+ if(pixerror < pixerrorbest) {
+ enc = colors;
+ pixerrorbest = pixerror;
+ pixerrorcolorbest[0] = pixerrorred;
+ pixerrorcolorbest[1] = pixerrorgreen;
+ pixerrorcolorbest[2] = pixerrorblue;
+ }
+ }
+ if(enc == 0) {
+ for(z = 0; z < 3; z++) {
+ blockerrlin[0][z] += 3 * pixerrorcolorbest[z];
+ }
+ nrcolor[0] += 3;
+ }
+ else if(enc == 2) {
+ for(z = 0; z < 3; z++) {
+ blockerrlin[0][z] += 2 * pixerrorcolorbest[z];
+ }
+ nrcolor[0] += 2;
+ for(z = 0; z < 3; z++) {
+ blockerrlin[1][z] += 1 * pixerrorcolorbest[z];
+ }
+ nrcolor[1] += 1;
+ }
+ else if(enc == 3) {
+ for(z = 0; z < 3; z++) {
+ blockerrlin[0][z] += 1 * pixerrorcolorbest[z];
+ }
+ nrcolor[0] += 1;
+ for(z = 0; z < 3; z++) {
+ blockerrlin[1][z] += 2 * pixerrorcolorbest[z];
+ }
+ nrcolor[1] += 2;
+ }
+ else if(enc == 1) {
+ for(z = 0; z < 3; z++) {
+ blockerrlin[1][z] += 3 * pixerrorcolorbest[z];
+ }
+ nrcolor[1] += 3;
+ }
+ }
+ }
+ if(nrcolor[0] == 0) nrcolor[0] = 1;
+ if(nrcolor[1] == 0) nrcolor[1] = 1;
+ for(j = 0; j < 2; j++) {
+ for(i = 0; i < 3; i++) {
+ int newvalue = testcolor[j][i] + blockerrlin[j][i] / nrcolor[j];
+ if(newvalue <= 0) testcolor[j][i] = 0;
+ else if(newvalue >= 255) testcolor[j][i] = 255;
+ else testcolor[j][i] = newvalue;
+ }
+ }
+
+ if ((abs(testcolor[0][0] - testcolor[1][0]) < 8) &&
+ (abs(testcolor[0][1] - testcolor[1][1]) < 4) &&
+ (abs(testcolor[0][2] - testcolor[1][2]) < 8)) {
+ /* both colors are so close they might get encoded as the same 16bit values */
+ unsigned char coldiffred, coldiffgreen, coldiffblue, coldiffmax, factor, ind0, ind1;
+
+ coldiffred = abs(testcolor[0][0] - testcolor[1][0]);
+ coldiffgreen = 2 * abs(testcolor[0][1] - testcolor[1][1]);
+ coldiffblue = abs(testcolor[0][2] - testcolor[1][2]);
+ coldiffmax = coldiffred;
+ if(coldiffmax < coldiffgreen) coldiffmax = coldiffgreen;
+ if(coldiffmax < coldiffblue) coldiffmax = coldiffblue;
+ if(coldiffmax > 0) {
+ if(coldiffmax > 4) factor = 2;
+ else if(coldiffmax > 2) factor = 3;
+ else factor = 4;
+ /* Won't do much if the color value is near 255... */
+ /* argh so many ifs */
+ if(testcolor[1][1] >= testcolor[0][1]) {
+ ind1 = 1; ind0 = 0;
+ }
+ else {
+ ind1 = 0; ind0 = 1;
+ }
+ if((testcolor[ind1][1] + factor * coldiffgreen) <= 255)
+ testcolor[ind1][1] += factor * coldiffgreen;
+ else testcolor[ind1][1] = 255;
+ if((testcolor[ind1][0] - testcolor[ind0][1]) > 0) {
+ if((testcolor[ind1][0] + factor * coldiffred) <= 255)
+ testcolor[ind1][0] += factor * coldiffred;
+ else testcolor[ind1][0] = 255;
+ }
+ else {
+ if((testcolor[ind0][0] + factor * coldiffred) <= 255)
+ testcolor[ind0][0] += factor * coldiffred;
+ else testcolor[ind0][0] = 255;
+ }
+ if((testcolor[ind1][2] - testcolor[ind0][2]) > 0) {
+ if((testcolor[ind1][2] + factor * coldiffblue) <= 255)
+ testcolor[ind1][2] += factor * coldiffblue;
+ else testcolor[ind1][2] = 255;
+ }
+ else {
+ if((testcolor[ind0][2] + factor * coldiffblue) <= 255)
+ testcolor[ind0][2] += factor * coldiffblue;
+ else testcolor[ind0][2] = 255;
+ }
+ }
+ }
+
+ if(((testcolor[0][0] & 0xf8) << 8 | (testcolor[0][1] & 0xfc) << 3 | testcolor[0][2] >> 3) <
+ ((testcolor[1][0] & 0xf8) << 8 | (testcolor[1][1] & 0xfc) << 3 | testcolor[1][2]) >> 3) {
+ for(i = 0; i < 3; i++) {
+ bestcolor[0][i] = testcolor[0][i];
+ bestcolor[1][i] = testcolor[1][i];
+ }
+ }
+ else {
+ for(i = 0; i < 3; i++) {
+ bestcolor[0][i] = testcolor[1][i];
+ bestcolor[1][i] = testcolor[0][i];
+ }
+ }
+}
+
+static void storedxtencodedblock( unsigned char *blkaddr, unsigned char srccolors[4][4][4], unsigned char *bestcolor[2], int numxpixels, int numypixels, int type, int haveAlpha) {
+ /* use same luminance-weighted distance metric to determine encoding as for finding the base colors */
+
+ int i, j, colors;
+ unsigned int testerror, testerror2, pixerror, pixerrorbest;
+ int colordist;
+ unsigned short color0, color1, tempcolor;
+ unsigned int bits = 0, bits2 = 0;
+ unsigned char *colorptr;
+ unsigned char enc = 0;
+ unsigned char cv[4][4];
+
+ bestcolor[0][0] = bestcolor[0][0] & 0xf8;
+ bestcolor[0][1] = bestcolor[0][1] & 0xfc;
+ bestcolor[0][2] = bestcolor[0][2] & 0xf8;
+ bestcolor[1][0] = bestcolor[1][0] & 0xf8;
+ bestcolor[1][1] = bestcolor[1][1] & 0xfc;
+ bestcolor[1][2] = bestcolor[1][2] & 0xf8;
+
+ color0 = bestcolor[0][0] << 8 | bestcolor[0][1] << 3 | bestcolor[0][2] >> 3;
+ color1 = bestcolor[1][0] << 8 | bestcolor[1][1] << 3 | bestcolor[1][2] >> 3;
+ if(color0 < color1) {
+ tempcolor = color0; color0 = color1; color1 = tempcolor;
+ colorptr = bestcolor[0]; bestcolor[0] = bestcolor[1]; bestcolor[1] = colorptr;
+ }
+
+ for(i = 0; i < 3; i ++) {
+ cv[0][i] = bestcolor[0][i];
+ cv[1][i] = bestcolor[1][i];
+ cv[2][i] = (bestcolor[0][i] * 2 + bestcolor[1][i]) / 3;
+ cv[3][i] = (bestcolor[0][i] + bestcolor[1][i] * 2) / 3;
+ }
+
+ testerror = 0;
+ for(j = 0; j < numypixels; j++) {
+ for(i = 0; i < numxpixels; i++) {
+ pixerrorbest = 0xffffffff;
+ for(colors = 0; colors < 4; colors++) {
+ colordist = srccolors[j][i][0] - cv[colors][0];
+ pixerror = colordist * colordist * REDWEIGHT;
+ colordist = srccolors[j][i][1] - cv[colors][1];
+ pixerror += colordist * colordist * GREENWEIGHT;
+ colordist = srccolors[j][i][2] - cv[colors][2];
+ pixerror += colordist * colordist * BLUEWEIGHT;
+ if(pixerror < pixerrorbest) {
+ pixerrorbest = pixerror;
+ enc = colors;
+ }
+ }
+ testerror += pixerrorbest;
+ bits |= enc << (2 * (j * 4 + i));
+ }
+ }
+ for(i = 0; i < 3; i ++) {
+ cv[2][i] = (bestcolor[0][i] + bestcolor[1][i]) / 2;
+ /* this isn't used. Looks like the black color constant can only be used
+ with RGB_DXT1 if I read the spec correctly (note though that the radeon gpu disagrees,
+ it will decode 3 to black even with DXT3/5), and due to how the color searching works
+ it won't get used even then */
+ cv[3][i] = 0;
+ }
+ testerror2 = 0;
+ for(j = 0; j < numypixels; j++) {
+ for(i = 0; i < numxpixels; i++) {
+ pixerrorbest = 0xffffffff;
+ if((type == GU_PSM_DXT1) && (srccolors[j][i][3] <= ALPHACUT)) {
+ enc = 3;
+ pixerrorbest = 0; // don't calculate error
+ } else {
+ // we're calculating the same what we have done already for colors 0-1 above...
+ for(colors = 0; colors < 3; colors++) {
+ colordist = srccolors[j][i][0] - cv[colors][0];
+ pixerror = colordist * colordist * REDWEIGHT;
+ colordist = srccolors[j][i][1] - cv[colors][1];
+ pixerror += colordist * colordist * GREENWEIGHT;
+ colordist = srccolors[j][i][2] - cv[colors][2];
+ pixerror += colordist * colordist * BLUEWEIGHT;
+ if(pixerror < pixerrorbest) {
+ pixerrorbest = pixerror;
+ // need to exchange colors later
+ if(colors > 1) enc = colors;
+ else enc = colors ^ 1;
+ }
+ }
+ }
+ testerror2 += pixerrorbest;
+ bits2 |= enc << (2 * (j * 4 + i));
+ }
+ }
+
+ /* finally we're finished, write back colors and bits */
+ if((testerror > testerror2) || (haveAlpha)) {
+ *blkaddr++ = color1 & 0xff;
+ *blkaddr++ = color1 >> 8;
+ *blkaddr++ = color0 & 0xff;
+ *blkaddr++ = color0 >> 8;
+ *blkaddr++ = (unsigned char) (bits2 & 0xff);
+ *blkaddr++ = (unsigned char) (( bits2 >> 8) & 0xff);
+ *blkaddr++ = (unsigned char) (( bits2 >> 16) & 0xff);
+ *blkaddr = (unsigned char) (bits2 >> 24);
+ } else {
+ *blkaddr++ = color0 & 0xff;
+ *blkaddr++ = color0 >> 8;
+ *blkaddr++ = color1 & 0xff;
+ *blkaddr++ = color1 >> 8;
+ *blkaddr++ = (unsigned char) (bits & 0xff);
+ *blkaddr++ = (unsigned char) (( bits >> 8) & 0xff);
+ *blkaddr++ = (unsigned char) (( bits >> 16) & 0xff);
+ *blkaddr = (unsigned char) (bits >> 24);
+ }
+}
+
+static void encodedxtcolorblockfaster( unsigned char *blkaddr, unsigned char srccolors[4][4][4], int numxpixels, int numypixels, unsigned int type ) {
+/* simplistic approach. We need two base colors, simply use the "highest" and the "lowest" color
+ present in the picture as base colors */
+
+ /* define lowest and highest color as shortest and longest vector to 0/0/0, though the
+ vectors are weighted similar to their importance in rgb-luminance conversion
+ doesn't work too well though...
+ This seems to be a rather difficult problem */
+
+ unsigned char *bestcolor[2];
+ unsigned char basecolors[2][3];
+ unsigned char i, j;
+ unsigned int lowcv, highcv, testcv;
+
+ lowcv = highcv = srccolors[0][0][0] * srccolors[0][0][0] * REDWEIGHT +
+ srccolors[0][0][1] * srccolors[0][0][1] * GREENWEIGHT +
+ srccolors[0][0][2] * srccolors[0][0][2] * BLUEWEIGHT;
+ bestcolor[0] = bestcolor[1] = srccolors[0][0];
+ for(j = 0; j < numypixels; j++) {
+ for(i = 0; i < numxpixels; i++) {
+ /* don't use this as a base color if the pixel will get black/transparent anyway */
+ testcv = srccolors[j][i][0] * srccolors[j][i][0] * REDWEIGHT +
+ srccolors[j][i][1] * srccolors[j][i][1] * GREENWEIGHT +
+ srccolors[j][i][2] * srccolors[j][i][2] * BLUEWEIGHT;
+ if(testcv > highcv) {
+ highcv = testcv;
+ bestcolor[1] = srccolors[j][i];
+ }
+ else if(testcv < lowcv) {
+ lowcv = testcv;
+ bestcolor[0] = srccolors[j][i];
+ }
+ }
+ }
+ /* make sure the original color values won't get touched... */
+ for(j = 0; j < 2; j++) {
+ for(i = 0; i < 3; i++) {
+ basecolors[j][i] = bestcolor[j][i];
+ }
+ }
+ bestcolor[0] = basecolors[0];
+ bestcolor[1] = basecolors[1];
+ /* try to find better base colors */
+ fancybasecolorsearch(blkaddr, srccolors, bestcolor, numxpixels, numypixels, type, TRUE);
+ /* find the best encoding for these colors, and store the result */
+ storedxtencodedblock(blkaddr, srccolors, bestcolor, numxpixels, numypixels, type, TRUE);
+}
+
+static void writedxt5encodedalphablock( unsigned char *blkaddr, unsigned char alphabase1, unsigned char alphabase2, unsigned char alphaenc[16]) {
+ *blkaddr++ = alphabase1;
+ *blkaddr++ = alphabase2;
+ *blkaddr++ = alphaenc[0] | (alphaenc[1] << 3) | ((alphaenc[2] & 3) << 6);
+ *blkaddr++ = (alphaenc[2] >> 2) | (alphaenc[3] << 1) | (alphaenc[4] << 4) | ((alphaenc[5] & 1) << 7);
+ *blkaddr++ = (alphaenc[5] >> 1) | (alphaenc[6] << 2) | (alphaenc[7] << 5);
+ *blkaddr++ = alphaenc[8] | (alphaenc[9] << 3) | ((alphaenc[10] & 3) << 6);
+ *blkaddr++ = (alphaenc[10] >> 2) | (alphaenc[11] << 1) | (alphaenc[12] << 4) | ((alphaenc[13] & 1) << 7);
+ *blkaddr++ = (alphaenc[13] >> 1) | (alphaenc[14] << 2) | (alphaenc[15] << 5);
+}
+
+static void encodedxt5alpha(unsigned char *blkaddr, unsigned char srccolors[4][4][4], int numxpixels, int numypixels) {
+ unsigned char alphabase[2], alphause[2];
+ short alphatest[2];
+ unsigned int alphablockerror1, alphablockerror2, alphablockerror3;
+ unsigned char i, j, aindex, acutValues[7];
+ unsigned char alphaenc1[16], alphaenc2[16], alphaenc3[16];
+ int alphaabsmin = FALSE;
+ int alphaabsmax = FALSE;
+ short alphadist;
+
+ alphatest[0] = 0;
+ alphatest[1] = 0;
+ /* find lowest and highest alpha value in block, alphabase[0] lowest, alphabase[1] highest */
+ alphabase[0] = 0xff; alphabase[1] = 0x0;
+ for(j = 0; j < numypixels; j++) {
+ for(i = 0; i < numxpixels; i++) {
+ if(srccolors[j][i][3] == 0)
+ alphaabsmin = TRUE;
+ else if(srccolors[j][i][3] == 255)
+ alphaabsmax = TRUE;
+ else {
+ if(srccolors[j][i][3] > alphabase[1]) alphabase[1] = srccolors[j][i][3];
+ if(srccolors[j][i][3] < alphabase[0]) alphabase[0] = srccolors[j][i][3];
+ }
+ }
+ }
+
+ if((alphabase[0] > alphabase[1]) && !(alphaabsmin && alphaabsmax)) { /* one color, either max or min */
+ /* shortcut here since it is a very common case (and also avoids later problems) */
+ /* || (alphabase[0] == alphabase[1] && !alphaabsmin && !alphaabsmax) */
+ /* could also thest for alpha0 == alpha1 (and not min/max), but probably not common, so don't bother */
+
+ *blkaddr++ = srccolors[0][0][3];
+ blkaddr += 1;
+ *blkaddr++ = 0;
+ *blkaddr++ = 0;
+ *blkaddr++ = 0;
+ *blkaddr++ = 0;
+ *blkaddr++ = 0;
+ *blkaddr++ = 0;
+ return;
+ }
+
+ /* find best encoding for alpha0 > alpha1 */
+ /* it's possible this encoding is better even if both alphaabsmin and alphaabsmax are true */
+ alphablockerror1 = 0x0;
+ alphablockerror2 = 0xffffffff;
+ alphablockerror3 = 0xffffffff;
+ if(alphaabsmin) alphause[0] = 0;
+ else alphause[0] = alphabase[0];
+ if(alphaabsmax) alphause[1] = 255;
+ else alphause[1] = alphabase[1];
+ /* calculate the 7 cut values, just the middle between 2 of the computed alpha values */
+ for(aindex = 0; aindex < 7; aindex++) {
+ /* don't forget here is always rounded down */
+ acutValues[aindex] = (alphause[0] * (2*aindex + 1) + alphause[1] * (14 - (2*aindex + 1))) / 14;
+ }
+
+ for(j = 0; j < numypixels; j++) {
+ for(i = 0; i < numxpixels; i++) {
+ /* maybe it's overkill to have the most complicated calculation just for the error
+ calculation which we only need to figure out if encoding1 or encoding2 is better... */
+ if(srccolors[j][i][3] > acutValues[0]) {
+ alphaenc1[4*j + i] = 0;
+ alphadist = srccolors[j][i][3] - alphause[1];
+ }
+ else if(srccolors[j][i][3] > acutValues[1]) {
+ alphaenc1[4*j + i] = 2;
+ alphadist = srccolors[j][i][3] - (alphause[1] * 6 + alphause[0] * 1) / 7;
+ }
+ else if(srccolors[j][i][3] > acutValues[2]) {
+ alphaenc1[4*j + i] = 3;
+ alphadist = srccolors[j][i][3] - (alphause[1] * 5 + alphause[0] * 2) / 7;
+ }
+ else if(srccolors[j][i][3] > acutValues[3]) {
+ alphaenc1[4*j + i] = 4;
+ alphadist = srccolors[j][i][3] - (alphause[1] * 4 + alphause[0] * 3) / 7;
+ }
+ else if(srccolors[j][i][3] > acutValues[4]) {
+ alphaenc1[4*j + i] = 5;
+ alphadist = srccolors[j][i][3] - (alphause[1] * 3 + alphause[0] * 4) / 7;
+ }
+ else if(srccolors[j][i][3] > acutValues[5]) {
+ alphaenc1[4*j + i] = 6;
+ alphadist = srccolors[j][i][3] - (alphause[1] * 2 + alphause[0] * 5) / 7;
+ }
+ else if(srccolors[j][i][3] > acutValues[6]) {
+ alphaenc1[4*j + i] = 7;
+ alphadist = srccolors[j][i][3] - (alphause[1] * 1 + alphause[0] * 6) / 7;
+ } else {
+ alphaenc1[4*j + i] = 1;
+ alphadist = srccolors[j][i][3] - alphause[0];
+ }
+ alphablockerror1 += alphadist * alphadist;
+ }
+ }
+
+ /* it's not very likely this encoding is better if both alphaabsmin and alphaabsmax
+ are false but try it anyway */
+ if(alphablockerror1 >= 32) {
+ /* don't bother if encoding is already very good, this condition should also imply
+ we have valid alphabase colors which we absolutely need (alphabase[0] <= alphabase[1]) */
+ alphablockerror2 = 0;
+ for(aindex = 0; aindex < 5; aindex++) {
+ /* don't forget here is always rounded down */
+ acutValues[aindex] = (alphabase[0] * (10 - (2*aindex + 1)) + alphabase[1] * (2*aindex + 1)) / 10;
+ }
+ for(j = 0; j < numypixels; j++) {
+ for(i = 0; i < numxpixels; i++) {
+ /* maybe it's overkill to have the most complicated calculation just for the error
+ calculation which we only need to figure out if encoding1 or encoding2 is better... */
+ if(srccolors[j][i][3] == 0) {
+ alphaenc2[4*j + i] = 6;
+ alphadist = 0;
+ }
+ else if(srccolors[j][i][3] == 255) {
+ alphaenc2[4*j + i] = 7;
+ alphadist = 0;
+ }
+ else if(srccolors[j][i][3] <= acutValues[0]) {
+ alphaenc2[4*j + i] = 0;
+ alphadist = srccolors[j][i][3] - alphabase[0];
+ }
+ else if(srccolors[j][i][3] <= acutValues[1]) {
+ alphaenc2[4*j + i] = 2;
+ alphadist = srccolors[j][i][3] - (alphabase[0] * 4 + alphabase[1] * 1) / 5;
+ }
+ else if(srccolors[j][i][3] <= acutValues[2]) {
+ alphaenc2[4*j + i] = 3;
+ alphadist = srccolors[j][i][3] - (alphabase[0] * 3 + alphabase[1] * 2) / 5;
+ }
+ else if(srccolors[j][i][3] <= acutValues[3]) {
+ alphaenc2[4*j + i] = 4;
+ alphadist = srccolors[j][i][3] - (alphabase[0] * 2 + alphabase[1] * 3) / 5;
+ }
+ else if(srccolors[j][i][3] <= acutValues[4]) {
+ alphaenc2[4*j + i] = 5;
+ alphadist = srccolors[j][i][3] - (alphabase[0] * 1 + alphabase[1] * 4) / 5;
+ } else {
+ alphaenc2[4*j + i] = 1;
+ alphadist = srccolors[j][i][3] - alphabase[1];
+ }
+ alphablockerror2 += alphadist * alphadist;
+ }
+ }
+
+ /* skip this if the error is already very small
+ this encoding is MUCH better on average than #2 though, but expensive! */
+ if((alphablockerror2 > 96) && (alphablockerror1 > 96)) {
+ short blockerrlin1 = 0;
+ short blockerrlin2 = 0;
+ unsigned char nralphainrangelow = 0;
+ unsigned char nralphainrangehigh = 0;
+ alphatest[0] = 0xff;
+ alphatest[1] = 0x0;
+ /* if we have large range it's likely there are values close to 0/255, try to map them to 0/255 */
+ for(j = 0; j < numypixels; j++) {
+ for(i = 0; i < numxpixels; i++) {
+ if((srccolors[j][i][3] > alphatest[1]) && (srccolors[j][i][3] < (255 -(alphabase[1] - alphabase[0]) / 28)))
+ alphatest[1] = srccolors[j][i][3];
+ if((srccolors[j][i][3] < alphatest[0]) && (srccolors[j][i][3] > (alphabase[1] - alphabase[0]) / 28))
+ alphatest[0] = srccolors[j][i][3];
+ }
+ }
+ /* shouldn't happen too often, don't really care about those degenerated cases */
+ if(alphatest[1] <= alphatest[0]) {
+ alphatest[0] = 1;
+ alphatest[1] = 254;
+ }
+ for(aindex = 0; aindex < 5; aindex++) {
+ /* don't forget here is always rounded down */
+ acutValues[aindex] = (alphatest[0] * (10 - (2*aindex + 1)) + alphatest[1] * (2*aindex + 1)) / 10;
+ }
+
+ /* find the "average" difference between the alpha values and the next encoded value.
+ This is then used to calculate new base values.
+ Should there be some weighting, i.e. those values closer to alphatest[x] have more weight,
+ since they will see more improvement, and also because the values in the middle are somewhat
+ likely to get no improvement at all (because the base values might move in different directions)?
+ OTOH it would mean the values in the middle are even less likely to get an improvement
+ */
+ for(j = 0; j < numypixels; j++) {
+ for(i = 0; i < numxpixels; i++) {
+ if(srccolors[j][i][3] <= alphatest[0] / 2) {
+ }
+ else if (srccolors[j][i][3] > ((255 + alphatest[1]) / 2)) {
+ }
+ else if (srccolors[j][i][3] <= acutValues[0]) {
+ blockerrlin1 += (srccolors[j][i][3] - alphatest[0]);
+ nralphainrangelow += 1;
+ }
+ else if (srccolors[j][i][3] <= acutValues[1]) {
+ blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5);
+ blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5);
+ nralphainrangelow += 1;
+ nralphainrangehigh += 1;
+ }
+ else if (srccolors[j][i][3] <= acutValues[2]) {
+ blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5);
+ blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5);
+ nralphainrangelow += 1;
+ nralphainrangehigh += 1;
+ }
+ else if (srccolors[j][i][3] <= acutValues[3]) {
+ blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5);
+ blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5);
+ nralphainrangelow += 1;
+ nralphainrangehigh += 1;
+ }
+ else if (srccolors[j][i][3] <= acutValues[4]) {
+ blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5);
+ blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5);
+ nralphainrangelow += 1;
+ nralphainrangehigh += 1;
+ }
+ else {
+ blockerrlin2 += (srccolors[j][i][3] - alphatest[1]);
+ nralphainrangehigh += 1;
+ }
+ }
+ }
+ /* shouldn't happen often, needed to avoid div by zero */
+ if(nralphainrangelow == 0) nralphainrangelow = 1;
+ if(nralphainrangehigh == 0) nralphainrangehigh = 1;
+ alphatest[0] = alphatest[0] + (blockerrlin1 / nralphainrangelow);
+ /* again shouldn't really happen often... */
+ if(alphatest[0] < 0) alphatest[0] = 0;
+
+ alphatest[1] = alphatest[1] + (blockerrlin2 / nralphainrangehigh);
+ if(alphatest[1] > 255) alphatest[1] = 255;
+
+ alphablockerror3 = 0;
+ for(aindex = 0; aindex < 5; aindex++) {
+ /* don't forget here is always rounded down */
+ acutValues[aindex] = (alphatest[0] * (10 - (2*aindex + 1)) + alphatest[1] * (2*aindex + 1)) / 10;
+ }
+ for(j = 0; j < numypixels; j++) {
+ for(i = 0; i < numxpixels; i++) {
+ /* maybe it's overkill to have the most complicated calculation just for the error
+ calculation which we only need to figure out if encoding1 or encoding2 is better... */
+ if (srccolors[j][i][3] <= alphatest[0] / 2) {
+ alphaenc3[4*j + i] = 6;
+ alphadist = srccolors[j][i][3];
+ }
+ else if (srccolors[j][i][3] > ((255 + alphatest[1]) / 2)) {
+ alphaenc3[4*j + i] = 7;
+ alphadist = 255 - srccolors[j][i][3];
+ }
+ else if (srccolors[j][i][3] <= acutValues[0]) {
+ alphaenc3[4*j + i] = 0;
+ alphadist = srccolors[j][i][3] - alphatest[0];
+ }
+ else if (srccolors[j][i][3] <= acutValues[1]) {
+ alphaenc3[4*j + i] = 2;
+ alphadist = srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5;
+ }
+ else if (srccolors[j][i][3] <= acutValues[2]) {
+ alphaenc3[4*j + i] = 3;
+ alphadist = srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5;
+ }
+ else if (srccolors[j][i][3] <= acutValues[3]) {
+ alphaenc3[4*j + i] = 4;
+ alphadist = srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5;
+ }
+ else if (srccolors[j][i][3] <= acutValues[4]) {
+ alphaenc3[4*j + i] = 5;
+ alphadist = srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5;
+ }
+ else {
+ alphaenc3[4*j + i] = 1;
+ alphadist = srccolors[j][i][3] - alphatest[1];
+ }
+ alphablockerror3 += alphadist * alphadist;
+ }
+ }
+ }
+ }
+
+ /* write the alpha values and encoding back. */
+ if((alphablockerror1 <= alphablockerror2) && (alphablockerror1 <= alphablockerror3)) {
+ writedxt5encodedalphablock( blkaddr, alphause[1], alphause[0], alphaenc1 );
+ } else if(alphablockerror2 <= alphablockerror3) {
+ writedxt5encodedalphablock( blkaddr, alphabase[0], alphabase[1], alphaenc2 );
+ } else {
+ writedxt5encodedalphablock( blkaddr, (unsigned char) alphatest[0], (unsigned char) alphatest[1], alphaenc3 );
+ }
+}
+
+static void extractsrccolors( unsigned char srcpixels[4][4][4], const GLchan *srcaddr, int srcRowStride, int numxpixels, int numypixels, int comps) {
+ unsigned char i, j, c;
+ const GLchan *curaddr;
+
+ for(j = 0; j < numypixels; j++) {
+ curaddr = srcaddr + j * srcRowStride * comps;
+ for(i = 0; i < numxpixels; i++) {
+ for(c = 0; c < comps; c++) {
+ srcpixels[j][i][c] = *curaddr++ / (CHAN_MAX / 255);
+ }
+ }
+ }
+}
+
+int tx_compress_dxtn(int srccomps, int width, int height, const unsigned char *srcPixData, unsigned int destFormat, unsigned char *dest)
+{
+ unsigned char *blkaddr = dest;
+ unsigned char srcpixels[4][4][4];
+ const GLchan *srcaddr = srcPixData;
+ int numxpixels, numypixels;//blubswillrule: hehe, "nummy pixels"
+ int i, j;
+
+ switch (destFormat)
+ {
+ case GU_PSM_DXT1:
+ for (j = 0; j < height; j += 4)
+ {
+ if (height > j + 3) numypixels = 4;
+ else numypixels = height - j;
+ srcaddr = srcPixData + j * width * srccomps;
+ for (i = 0; i < width; i += 4)
+ {
+ if (width > i + 3) numxpixels = 4;
+ else numxpixels = width - i;
+ extractsrccolors(srcpixels, srcaddr, width, numxpixels, numypixels, srccomps);
+ encodedxtcolorblockfaster(blkaddr, srcpixels, numxpixels, numypixels, destFormat);
+ srcaddr += srccomps * numxpixels;
+ blkaddr += 8;
+ }
+ }
+ Convert_DXT1((unsigned char *) dest, blkaddr - dest);
+ break;
+
+ case GU_PSM_DXT3:
+ for(j = 0; j < height; j += 4)
+ {
+ if(height > j + 3) numypixels = 4;
+ else numypixels = height - j;
+ srcaddr = srcPixData + j * width * srccomps;
+ for(i = 0; i < width; i += 4)
+ {
+ if(width > i + 3) numxpixels = 4;
+ else numxpixels = width - i;
+ extractsrccolors(srcpixels, srcaddr, width, numxpixels, numypixels, srccomps);
+ *blkaddr++ = (srcpixels[0][0][3] >> 4) | (srcpixels[0][1][3] & 0xf0);
+ *blkaddr++ = (srcpixels[0][2][3] >> 4) | (srcpixels[0][3][3] & 0xf0);
+ *blkaddr++ = (srcpixels[1][0][3] >> 4) | (srcpixels[1][1][3] & 0xf0);
+ *blkaddr++ = (srcpixels[1][2][3] >> 4) | (srcpixels[1][3][3] & 0xf0);
+ *blkaddr++ = (srcpixels[2][0][3] >> 4) | (srcpixels[2][1][3] & 0xf0);
+ *blkaddr++ = (srcpixels[2][2][3] >> 4) | (srcpixels[2][3][3] & 0xf0);
+ *blkaddr++ = (srcpixels[3][0][3] >> 4) | (srcpixels[3][1][3] & 0xf0);
+ *blkaddr++ = (srcpixels[3][2][3] >> 4) | (srcpixels[3][3][3] & 0xf0);
+ encodedxtcolorblockfaster(blkaddr, srcpixels, numxpixels, numypixels, destFormat);
+ srcaddr += srccomps * numxpixels;
+ blkaddr += 8;
+ }
+ }
+ Convert_DXT3((unsigned char *) dest, blkaddr - dest);
+ break;
+ case GU_PSM_DXT5:
+ for(j = 0; j < height; j += 4)
+ {
+ if(height > j + 3) numypixels = 4;
+ else numypixels = height - j;
+ srcaddr = srcPixData + j * width * srccomps;
+ for(i = 0; i < width; i += 4)
+ {
+ if(width > i + 3) numxpixels = 4;
+ else numxpixels = width - i;
+ extractsrccolors(srcpixels, srcaddr, width, numxpixels, numypixels, srccomps);
+ encodedxt5alpha(blkaddr, srcpixels, numxpixels, numypixels);
+ encodedxtcolorblockfaster(blkaddr + 8, srcpixels, numxpixels, numypixels, destFormat);
+ srcaddr += srccomps * numxpixels;
+ blkaddr += 16;
+ }
+ }
+ Convert_DXT5((unsigned char *) dest, blkaddr - dest);
+ break;
+ default:
+ return(0);
+ }
+ return(blkaddr - dest);
+}
diff --git a/source/psp/video_hardware_dxtn.h b/source/psp/video_hardware_dxtn.h
new file mode 100644
index 0000000..9f1e2bf
--- /dev/null
+++ b/source/psp/video_hardware_dxtn.h
@@ -0,0 +1,56 @@
+/*
+ * libtxc_dxtn
+ * Version: 0.1b
+ *
+ * Fixed some bugs with dxt1 compression
+ *
+ * Copyright (C) 2004 Roland Scheidegger
+ * All Rights Reserved.
+ * Copyright (C) 2006-2008 Franck Charlet
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef _TXC_DXTN_H_
+#define _TXC_DXTN_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned char GLchan;
+
+#define FALSE 0
+#define TRUE 1
+
+#define UBYTE_TO_CHAN(b) (b)
+#define CHAN_MAX 255
+#define RCOMP 0
+#define GCOMP 1
+#define BCOMP 2
+#define ACOMP 3
+
+extern int tx_compress_dxtn(int srccomps, int width, int height, const unsigned char *srcPixData, unsigned int destformat, unsigned char *dest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/psp/video_hardware_entity_fragment.cpp b/source/psp/video_hardware_entity_fragment.cpp
new file mode 100644
index 0000000..9f743a8
--- /dev/null
+++ b/source/psp/video_hardware_entity_fragment.cpp
@@ -0,0 +1,240 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// r_efrag.c
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+
+mnode_t *r_pefragtopnode;
+
+
+//===========================================================================
+
+/*
+===============================================================================
+
+ ENTITY FRAGMENT FUNCTIONS
+
+===============================================================================
+*/
+
+efrag_t **lastlink;
+
+vec3_t r_emins, r_emaxs;
+
+entity_t *r_addent;
+
+
+/*
+================
+R_RemoveEfrags
+
+Call when removing an object from the world or moving it to another position
+================
+*/
+void R_RemoveEfrags (entity_t *ent)
+{
+ efrag_t *ef, *old, *walk, **prev;
+
+ ef = ent->efrag;
+
+ while (ef)
+ {
+ prev = &ef->leaf->efrags;
+ while (1)
+ {
+ walk = *prev;
+ if (!walk)
+ break;
+ if (walk == ef)
+ { // remove this fragment
+ *prev = ef->leafnext;
+ break;
+ }
+ else
+ prev = &walk->leafnext;
+ }
+
+ old = ef;
+ ef = ef->entnext;
+
+ // put it on the free list
+ old->entnext = cl.free_efrags;
+ cl.free_efrags = old;
+ }
+
+ ent->efrag = NULL;
+}
+
+/*
+===================
+R_SplitEntityOnNode
+===================
+*/
+void R_SplitEntityOnNode (mnode_t *node)
+{
+ efrag_t *ef;
+ mplane_t *splitplane;
+ mleaf_t *leaf;
+ int sides;
+
+ if (node->contents == CONTENTS_SOLID)
+ {
+ return;
+ }
+
+// add an efrag if the node is a leaf
+
+ if ( node->contents < 0)
+ {
+ if (!r_pefragtopnode)
+ r_pefragtopnode = node;
+
+ leaf = (mleaf_t *)node;
+
+// grab an efrag off the free list
+ ef = cl.free_efrags;
+ if (!ef)
+ {
+ Con_Printf ("Too many efrags!\n");
+ return; // no free fragments...
+ }
+ cl.free_efrags = cl.free_efrags->entnext;
+
+ ef->entity = r_addent;
+
+// add the entity link
+ *lastlink = ef;
+ lastlink = &ef->entnext;
+ ef->entnext = NULL;
+
+// set the leaf links
+ ef->leaf = leaf;
+ ef->leafnext = leaf->efrags;
+ leaf->efrags = ef;
+
+ return;
+ }
+
+// NODE_MIXED
+
+ splitplane = node->plane;
+ sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane);
+
+ if (sides == 3)
+ {
+ // split on this plane
+ // if this is the first splitter of this bmodel, remember it
+ if (!r_pefragtopnode)
+ r_pefragtopnode = node;
+ }
+
+// recurse down the contacted sides
+ if (sides & 1)
+ R_SplitEntityOnNode (node->children[0]);
+
+ if (sides & 2)
+ R_SplitEntityOnNode (node->children[1]);
+}
+
+
+
+/*
+===========
+R_AddEfrags
+===========
+*/
+void R_AddEfrags (entity_t *ent)
+{
+ model_t *entmodel;
+ int i;
+
+ if (!ent->model)
+ return;
+
+ r_addent = ent;
+
+ lastlink = &ent->efrag;
+ r_pefragtopnode = NULL;
+
+ entmodel = ent->model;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ r_emins[i] = ent->origin[i] + entmodel->mins[i];
+ r_emaxs[i] = ent->origin[i] + entmodel->maxs[i];
+ }
+
+ R_SplitEntityOnNode (cl.worldmodel->nodes);
+
+ ent->topnode = r_pefragtopnode;
+}
+
+
+/*
+================
+R_StoreEfrags
+
+// FIXME: a lot of this goes away with edge-based
+================
+*/
+void R_StoreEfrags (efrag_t **ppefrag)
+{
+ entity_t *pent;
+ model_t *clmodel;
+ efrag_t *pefrag;
+
+
+ while ((pefrag = *ppefrag) != NULL)
+ {
+ pent = pefrag->entity;
+ clmodel = pent->model;
+
+ switch (clmodel->type)
+ {
+ case mod_alias:
+ case mod_md3:
+ case mod_halflife:
+ case mod_brush:
+ case mod_sprite:
+ pent = pefrag->entity;
+
+ if ((pent->visframe != r_framecount) &&
+ (cl_numvisedicts < MAX_VISEDICTS))
+ {
+ cl_visedicts[cl_numvisedicts++] = pent;
+
+ // mark that we've recorded this entity for this frame
+ pent->visframe = r_framecount;
+ }
+
+ ppefrag = &pefrag->leafnext;
+ break;
+
+ default:
+ Sys_Error ("R_StoreEfrags: Bad entity type %d\n", clmodel->type);
+ }
+ }
+}
+
+
diff --git a/source/psp/video_hardware_fog.cpp b/source/psp/video_hardware_fog.cpp
new file mode 100644
index 0000000..3cb356a
--- /dev/null
+++ b/source/psp/video_hardware_fog.cpp
@@ -0,0 +1,475 @@
+/*
+
+Fogging system based on FitzQuake's implementation
+
+*/
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+
+#include
+
+//==============================================================================
+//
+// GLOBAL FOG
+//
+//==============================================================================
+
+extern refdef_t r_refdef;
+
+float old_start;
+float old_end;
+float old_red;
+float old_green;
+float old_blue;
+
+float fade_time; //duration of fade
+float fade_done; //time when fade will be done
+
+/*
+=============
+Fog_Update
+
+update internal variables
+=============
+*/
+void Fog_Update (float start, float end, float red, float green, float blue, float time)
+{
+ //save previous settings for fade
+ if (time > 0)
+ {
+ //check for a fade in progress
+ if (fade_done > cl.time)
+ {
+ float f;
+
+ f = (fade_done - cl.time) / fade_time;
+ old_start = f * old_start + (1.0 - f) * r_refdef.fog_start;
+ old_end = f * old_end + (1.0 - f) * r_refdef.fog_end;
+ old_red = f * old_red + (1.0 - f) * r_refdef.fog_red;
+ old_green = f * old_green + (1.0 - f) * r_refdef.fog_green;
+ old_blue = f * old_blue + (1.0 - f) * r_refdef.fog_blue;
+ }
+ else
+ {
+ old_start = r_refdef.fog_start;
+ old_end = r_refdef.fog_end;
+ old_red = r_refdef.fog_red;
+ old_green = r_refdef.fog_green;
+ old_blue = r_refdef.fog_blue;
+ }
+ }
+
+ r_refdef.fog_start = start;
+ r_refdef.fog_end = end;
+ r_refdef.fog_red = red;
+ r_refdef.fog_green = green;
+ r_refdef.fog_blue = blue;
+ fade_time = time;
+ fade_done = cl.time + time;
+}
+
+/*
+=============
+Fog_ParseServerMessage
+
+handle an SVC_FOG message from server
+=============
+*/
+void Fog_ParseServerMessage (void)
+{
+ float start, end, red, green, blue, time;
+
+ start = MSG_ReadByte() / 255.0;
+ end = MSG_ReadByte() / 255.0;
+ red = MSG_ReadByte() / 255.0;
+ green = MSG_ReadByte() / 255.0;
+ blue = MSG_ReadByte() / 255.0;
+ time = MSG_ReadShort() / 100.0;
+
+ Fog_Update (start, end, red, green, blue, time);
+}
+
+/*
+=============
+Fog_FogCommand_f
+
+handle the 'fog' console command
+=============
+*/
+void Fog_FogCommand_f (void)
+{
+ switch (Cmd_Argc())
+ {
+ default:
+ case 1:
+ Con_Printf("usage:\n");
+ Con_Printf(" fog \n");
+ Con_Printf(" fog \n");
+ Con_Printf(" fog \n");
+ Con_Printf(" fog \n");
+ Con_Printf(" fog \n");
+ Con_Printf(" fog \n");
+ Con_Printf("current values:\n");
+ Con_Printf(" \"start\" is \"%f\"\n", r_refdef.fog_start);
+ Con_Printf(" \"end\" is \"%f\"\n", r_refdef.fog_end);
+ Con_Printf(" \"red\" is \"%f\"\n", r_refdef.fog_red);
+ Con_Printf(" \"green\" is \"%f\"\n", r_refdef.fog_green);
+ Con_Printf(" \"blue\" is \"%f\"\n", r_refdef.fog_blue);
+ Con_Printf(" \"fade\" is \"%f\"\n", fade_time);
+ break;
+ case 2: //TEST
+ Fog_Update(r_refdef.fog_start,
+ r_refdef.fog_end,
+ r_refdef.fog_red,
+ r_refdef.fog_green,
+ r_refdef.fog_blue,
+ atof(Cmd_Argv(1)));
+ break;
+ case 3:
+ Fog_Update(atof(Cmd_Argv(1)),
+ atof(Cmd_Argv(2)),
+ r_refdef.fog_red,
+ r_refdef.fog_green,
+ r_refdef.fog_blue,
+ 0.0);
+ break;
+ case 4:
+ Fog_Update(r_refdef.fog_start,
+ r_refdef.fog_end,
+ CLAMP(0.0, atof(Cmd_Argv(1)), 100.0),
+ CLAMP(0.0, atof(Cmd_Argv(2)), 100.0),
+ CLAMP(0.0, atof(Cmd_Argv(3)), 100.0),
+ 0.0);
+ break;
+ case 5: //TEST
+ Fog_Update(r_refdef.fog_start,
+ r_refdef.fog_end,
+ CLAMP(0.0, atof(Cmd_Argv(1)), 100.0),
+ CLAMP(0.0, atof(Cmd_Argv(2)), 100.0),
+ CLAMP(0.0, atof(Cmd_Argv(3)), 100.0),
+ atof(Cmd_Argv(4)));
+ break;
+ case 6:
+ Fog_Update(atof(Cmd_Argv(1)),
+ atof(Cmd_Argv(2)),
+ CLAMP(0.0, atof(Cmd_Argv(3)), 100.0),
+ CLAMP(0.0, atof(Cmd_Argv(4)), 100.0),
+ CLAMP(0.0, atof(Cmd_Argv(5)), 100.0),
+ 0.0);
+ break;
+ case 7:
+ Fog_Update(atof(Cmd_Argv(1)),
+ atof(Cmd_Argv(2)),
+ CLAMP(0.0, atof(Cmd_Argv(3)), 100.0),
+ CLAMP(0.0, atof(Cmd_Argv(4)), 100.0),
+ CLAMP(0.0, atof(Cmd_Argv(5)), 100.0),
+ atof(Cmd_Argv(6)));
+ break;
+ }
+}
+
+/*
+=============
+Fog_ParseWorldspawn
+
+called at map load
+=============
+*/
+void Fog_ParseWorldspawn (void)
+{
+ char key[128], value[4096];
+ char *data;
+
+ //initially no fog
+ r_refdef.fog_start = 0;
+ old_start = 0;
+
+ r_refdef.fog_end = -1;
+ old_end = -1;
+
+ r_refdef.fog_red = 0.0;
+ old_red = 0.0;
+
+ r_refdef.fog_green = 0.0;
+ old_green = 0.0;
+
+ r_refdef.fog_blue = 0.0;
+ old_blue = 0.0;
+
+ fade_time = 0.0;
+ fade_done = 0.0;
+
+ data = COM_Parse(cl.worldmodel->entities);
+ if (!data)
+ return; // error
+ if (com_token[0] != '{')
+ return; // error
+ while (1)
+ {
+ data = COM_Parse(data);
+ if (!data)
+ return; // error
+ if (com_token[0] == '}')
+ break; // end of worldspawn
+ if (com_token[0] == '_')
+ strcpy(key, com_token + 1);
+ else
+ strcpy(key, com_token);
+ while (key[strlen(key)-1] == ' ') // remove trailing spaces
+ key[strlen(key)-1] = 0;
+ data = COM_Parse(data);
+ if (!data)
+ return; // error
+ strcpy(value, com_token);
+
+ if (!strcmp("fog", key))
+ {
+ sscanf(value, "%f %f %f %f %f", &r_refdef.fog_start, &r_refdef.fog_end, &r_refdef.fog_red, &r_refdef.fog_green, &r_refdef.fog_blue);
+ }
+ }
+}
+
+/*
+=============
+Fog_SetupFrame
+
+called at the beginning of each frame
+=============
+*/
+void Fog_SetupFrame (void)
+{
+ float c[4];
+ float f, s, e;
+
+ if (fade_done > cl.time)
+ {
+ f = (fade_done - cl.time) / fade_time;
+ s = f * old_start + (1.0 - f) * r_refdef.fog_start;
+ e = f * old_end + (1.0 - f) * r_refdef.fog_end;
+ c[0] = f * old_red + (1.0 - f) * r_refdef.fog_red;
+ c[1] = f * old_green + (1.0 - f) * r_refdef.fog_green;
+ c[2] = f * old_blue + (1.0 - f) * r_refdef.fog_blue;
+ c[3] = r_skyfog.value;
+ }
+ else
+ {
+ s = r_refdef.fog_start;
+ e = r_refdef.fog_end;
+ c[0] = r_refdef.fog_red;
+ c[1] = r_refdef.fog_green;
+ c[2] = r_refdef.fog_blue;
+ c[3] = 1.0;
+ c[3] = r_skyfog.value;
+ }
+
+ if(e == 0)
+ e = -1;
+
+ sceGuFog ( s, e, GU_COLOR( c[0]* 0.01f, c[1]* 0.01f, c[2]* 0.01f, c[3] ) );
+
+ if(s == 0 || e < 0)
+ sceGuDisable(GU_FOG);
+}
+
+/*
+=============
+Fog_SetColorForSkyS
+Start called before drawing flat-colored sky
+Crow_bar*
+=============
+*/
+void Fog_SetColorForSkyS (void)
+{
+ if (r_refdef.fog_end > 0.0f && r_skyfog.value)
+ {
+ float a = r_refdef.fog_end * 0.00025f;
+ float r = r_refdef.fog_red * 0.01f + (a * 0.25f);
+ float g = r_refdef.fog_green * 0.01f + (a * 0.25f);
+ float b = r_refdef.fog_blue * 0.01f + (a * 0.25f);
+
+ if (a > 1.0f)
+ a = 1.0f;
+ if (r > 1.0f)
+ r = 1.0f;
+ if (g > 1.0f)
+ g = 1.0f;
+ if (b > 1.0f)
+ b = 1.0f;
+
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+ sceGuColor(GU_COLOR(r,g,b,a));
+
+ //sceGuEnable(GU_BLEND);
+ //sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, GU_COLOR(r,g,b,a), GU_COLOR(r,g,b,a));
+ }
+}
+
+/*
+=============
+Fog_SetColorForSky
+End called before drawing flat-colored sky
+Crow_bar*
+=============
+*/
+void Fog_SetColorForSkyE (void)
+{
+ if (r_refdef.fog_end > 0.0f && r_skyfog.value)
+ {
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+ sceGuColor(0xffffffff);
+ //sceGuDisable(GU_BLEND);
+ //sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
+ }
+}
+
+/*
+=============
+Fog_GetStart
+
+returns current start of fog
+=============
+*/
+float Fog_GetStart (void)
+{
+ float f;
+
+ if (fade_done > cl.time)
+ {
+ f = (fade_done - cl.time) / fade_time;
+ return f * old_start + (1.0 - f) * r_refdef.fog_start;
+ }
+ else
+ return r_refdef.fog_start;
+}
+
+/*
+=============
+Fog_GetEnd
+
+returns current end of fog
+=============
+*/
+float Fog_GetEnd (void)
+{
+ float f;
+
+ if (fade_done > cl.time)
+ {
+ f = (fade_done - cl.time) / fade_time;
+ return f * old_start + (1.0 - f) * r_refdef.fog_end;
+ }
+ else
+ return r_refdef.fog_end;
+}
+
+/*
+=============
+Fog_EnableGFog
+
+called before drawing stuff that should be fogged
+=============
+*/
+void Fog_EnableGFog (void)
+{
+ if (!Fog_GetStart() == 0 || !Fog_GetEnd() <= 0)
+ sceGuEnable(GU_FOG);
+}
+
+/*
+=============
+Fog_DisableGFog
+
+called after drawing stuff that should be fogged
+=============
+*/
+void Fog_DisableGFog (void)
+{
+ if (!Fog_GetStart() == 0 || !Fog_GetEnd() <= 0)
+ sceGuDisable(GU_FOG);
+}
+
+/*
+=============
+Fog_SetColorForSky
+
+called before drawing flat-colored sky
+=============
+*/
+/*
+void Fog_SetColorForSky (void)
+{
+ float c[3];
+ float f, d;
+
+ if (fade_done > cl.time)
+ {
+ f = (fade_done - cl.time) / fade_time;
+ d = f * old_density + (1.0 - f) * fog_density;
+ c[0] = f * old_red + (1.0 - f) * fog_red;
+ c[1] = f * old_green + (1.0 - f) * fog_green;
+ c[2] = f * old_blue + (1.0 - f) * fog_blue;
+ }
+ else
+ {
+ d = fog_density;
+ c[0] = fog_red;
+ c[1] = fog_green;
+ c[2] = fog_blue;
+ }
+
+ if (d > 0)
+ glColor3fv (c);
+}
+*/
+//==============================================================================
+//
+// VOLUMETRIC FOG
+//
+//==============================================================================
+/*
+
+void Fog_DrawVFog (void){}
+void Fog_MarkModels (void){}
+*/
+//==============================================================================
+//
+// INIT
+//
+//==============================================================================
+
+/*
+=============
+Fog_NewMap
+
+called whenever a map is loaded
+=============
+*/
+
+void Fog_NewMap (void)
+{
+ Fog_ParseWorldspawn (); //for global fog
+// Fog_MarkModels (); //for volumetric fog
+}
+
+/*
+=============
+Fog_Init
+
+called when quake initializes
+=============
+*/
+void Fog_Init (void)
+{
+ Cmd_AddCommand ("fog",Fog_FogCommand_f);
+
+ //set up global fog
+ r_refdef.fog_start = 0;
+ r_refdef.fog_end = -1;
+ r_refdef.fog_red = 0.5;
+ r_refdef.fog_green = 0.5;
+ r_refdef.fog_blue = 0.5;
+ fade_time = 1;
+}
diff --git a/source/psp/video_hardware_fullbright.cpp b/source/psp/video_hardware_fullbright.cpp
new file mode 100644
index 0000000..ce100d6
--- /dev/null
+++ b/source/psp/video_hardware_fullbright.cpp
@@ -0,0 +1,63 @@
+//video_hardware_fullbright.c
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+#include
+
+void DrawGLPoly_ex (glpoly_t *p);
+
+int FindFullbrightTexture (byte *pixels, int num_pix)
+{
+ int i;
+ for (i = 0; i < num_pix; i++)
+ if (pixels[i] > 223)
+ return 1;
+
+
+ return 0;
+}
+
+
+void ConvertPixels (byte *pixels, int num_pixels)
+{
+ int i;
+
+ for (i = 0; i < num_pixels; i++)
+ if (pixels[i] < 224)
+ pixels[i] = 255;
+}
+
+void DrawFullBrightTextures (msurface_t *first_surf, int num_surfs)
+{
+ // gl_texsort 1 version
+ int i;
+ msurface_t *fa;
+ texture_t *t;
+
+ if (r_fullbright.value)
+ return;
+
+ for (fa = first_surf, i = 0; i < num_surfs; fa++, i++)
+ {
+ // find the correct texture
+ t = R_TextureAnimation (fa->texinfo->texture);
+
+ if (t->fullbright != -1 && fa->draw_this_frame)
+ {
+ sceGuDisable(GU_FOG); //Disable fog for fullbright textures
+ sceGuEnable(GU_BLEND);
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+ GL_Bind (t->fullbright);
+ DrawGLPoly_ex (fa->polys);
+ sceGuDisable (GU_BLEND);
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+ sceGuEnable(GU_FOG);
+ }
+
+ fa->draw_this_frame = 0;
+ }
+}
+
+
diff --git a/source/psp/video_hardware_fullbright.h b/source/psp/video_hardware_fullbright.h
new file mode 100644
index 0000000..822aac0
--- /dev/null
+++ b/source/psp/video_hardware_fullbright.h
@@ -0,0 +1,9 @@
+//video_hardware_fullbright.h
+#include "video_hardware_model.h"
+
+
+int FindFullbrightTexture (byte *pixels, int num_pix);
+void DrawFullBrightTextures (msurface_t *first_surf, int num_surfs);
+void ConvertPixels (byte *pixels, int num_pixels);
+
+
diff --git a/source/psp/video_hardware_hlmdl.cpp b/source/psp/video_hardware_hlmdl.cpp
new file mode 100644
index 0000000..e321c73
--- /dev/null
+++ b/source/psp/video_hardware_hlmdl.cpp
@@ -0,0 +1,909 @@
+
+extern"C"
+{
+#include "../quakedef.h"
+}
+
+/*
+ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Half-Life Model Renderer (Experimental) Copyright (C) 2001 James 'Ender' Brown [ender@quakesrc.org] This program is
+ free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ details. You should have received a copy of the GNU General Public License along with this program; if not, write
+ to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. fromquake.h -
+
+ render.c - apart from calculations (mostly range checking or value conversion code is a mix of standard Quake 1
+ meshing, and vertex deforms. The rendering loop uses standard Quake 1 drawing, after SetupBones deforms the vertex.
+ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ Also, please note that it won't do all hl models....
+ Nor will it work 100%
+++++++++++++++++++++++++++
+ modify by Crow_bar 2009
+ 10.08.09
+++++++++++++++++++++++++++
+ */
+#include "video_hardware_hlmdl.h"
+
+#include
+#include
+#include
+using namespace std;
+extern list mapTextureNameList;
+
+extern model_t *loadmodel;
+
+extern vec3_t lightcolor; // LordHavoc: .lit support
+extern entity_t *currententity;
+extern float *shadedots;
+extern vec3_t shadevector;
+extern float shadelight, ambientlight;
+#define NUMVERTEXNORMALS 162
+extern float r_avertexnormals[NUMVERTEXNORMALS][3];
+#define SHADEDOT_QUANT 16
+extern float r_avertexnormal_dots[SHADEDOT_QUANT][256];
+
+void QuaternionGLMatrix(float x, float y, float z, float w, vec4_t *GLM)
+{
+ GLM[0][0] = 1 - 2 * y * y - 2 * z * z;
+ GLM[1][0] = 2 * x * y + 2 * w * z;
+ GLM[2][0] = 2 * x * z - 2 * w * y;
+ GLM[0][1] = 2 * x * y - 2 * w * z;
+ GLM[1][1] = 1 - 2 * x * x - 2 * z * z;
+ GLM[2][1] = 2 * y * z + 2 * w * x;
+ GLM[0][2] = 2 * x * z + 2 * w * y;
+ GLM[1][2] = 2 * y * z - 2 * w * x;
+ GLM[2][2] = 1 - 2 * x * x - 2 * y * y;
+}
+
+/*
+ =======================================================================================================================
+ QuaternionGLAngle - Convert a GL angle to a quaternion matrix
+ =======================================================================================================================
+ */
+void QuaternionGLAngle(const vec3_t angles, vec4_t quaternion)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ float yaw = angles[2] * 0.5;
+ float pitch = angles[1] * 0.5;
+ float roll = angles[0] * 0.5;
+ float siny = sin(yaw);
+ float cosy = cos(yaw);
+ float sinp = sin(pitch);
+ float cosp = cos(pitch);
+ float sinr = sin(roll);
+ float cosr = cos(roll);
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ quaternion[0] = sinr * cosp * cosy - cosr * sinp * siny;
+ quaternion[1] = cosr * sinp * cosy + sinr * cosp * siny;
+ quaternion[2] = cosr * cosp * siny - sinr * sinp * cosy;
+ quaternion[3] = cosr * cosp * cosy + sinr * sinp * siny;
+}
+
+
+
+
+
+matrix3x4 transform_matrix[128]; /* Vertex transformation matrix */
+
+int g_chrome[MAXSTUDIOVERTS][2]; // texture coords for surface normals
+int g_chromeage[MAXSTUDIOBONES]; // last time chrome vectors were updated
+vec3_t g_chromeup[MAXSTUDIOBONES]; // chrome vector "up" in bone reference frames
+vec3_t g_chromeright[MAXSTUDIOBONES]; // chrome vector "right" in bone reference frames
+vec3_t g_vright = { 50, 50, 0 };
+vec3_t *g_pvlightvalues;
+vec3_t g_blightvec[MAXSTUDIOBONES]; // light vectors in bone reference frames
+vec3_t g_lightvalues[MAXSTUDIOVERTS]; // light surface normals
+vec3_t g_lightvec; // light vector in model reference frame
+vec3_t g_lightcolor;
+int g_smodels_total; // cookie
+vec3_t m_angles;
+
+void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float tex_h, float *lv);
+void GL_Draw_HL_AliasChrome(short *order, vec3_t *transformed, float tex_w, float tex_h, float *lv);
+
+/*
+=======================================================================================================================
+ Mod_LoadHLModel - read in the model's constituent parts
+=======================================================================================================================
+*/
+
+extern char loadname[];
+extern int nonetexture;
+qboolean Mod_LoadHLModel (model_t *mod, void *buffer)
+{
+ /*~~*/
+ int i;
+
+ hlmodelcache_t *model;
+ hlmdl_header_t *header;
+ hlmdl_tex_t *tex;
+ hlmdl_bone_t *bones;
+ hlmdl_bonecontroller_t *bonectls;
+ int start, end, total;
+ /*~~*/
+ start = Hunk_LowMark ();
+ //load the model into hunk
+ model = static_cast(Hunk_Alloc(sizeof(hlmodelcache_t)));
+
+ header = static_cast(Hunk_Alloc(com_filesize));
+ memcpy(header, buffer, com_filesize);
+
+ if (header->version != 10)
+ {
+ Con_Printf("Cannot load model %s - unknown version %i\n", mod->name, header->version);
+ Hunk_FreeToLowMark(start);
+ return qfalse;
+ }
+
+ tex = (hlmdl_tex_t *) ((byte *) header + header->textures);
+ bones = (hlmdl_bone_t *) ((byte *) header + header->boneindex);
+ bonectls = (hlmdl_bonecontroller_t *) ((byte *) header + header->controllerindex);
+
+ model->header = (char *)header - (char *)model;
+ Con_Printf("HLMDL Load: header");
+
+ model->textures = (char *)tex - (char *)model;
+ Con_Printf(", tex");
+
+ model->bones = (char *)bones - (char *)model;
+ Con_Printf(", bones");
+
+ model->bonectls = (char *)bonectls - (char *)model;
+ Con_Printf(", bonectls\n");
+
+ for(i = 0; i < header->numtextures; i++)
+ {
+
+ tex[i].i = GL_LoadPalTex (tex[i].name, tex[i].w, tex[i].h, (byte *) header + tex[i].i, qtrue, GU_LINEAR, 0, (byte *) header + tex[i].w * tex[i].h + tex[i].i, PAL_RGB);
+ mapTextureNameList.push_back(tex[i].i); // for unload textures
+ }
+//
+// move the complete, relocatable alias model to the cache
+//
+ end = Hunk_LowMark ();
+
+ total = end - start;
+
+ mod->type = mod_halflife;
+
+ Cache_Alloc (&mod->cache, total, loadname);
+
+ if (!mod->cache.data)
+ return qfalse;
+
+ memcpy (mod->cache.data, model, total);
+
+ Hunk_FreeToLowMark (start);
+ return qtrue;
+}
+
+/*
+======================================================================================================================
+ HL_CurSequence - return the current sequence
+
+
+======================================================================================================================
+*/
+int HL_CurSequence(hlmodel_t model)
+{
+ return model.sequence;
+}
+
+/*
+=======================================================================================================================
+ HL_NewSequence - animation control (just some range checking really)
+=======================================================================================================================
+*/
+int HL_NewSequence(hlmodel_t *model, int _inew)
+{
+ if(_inew < 0)
+ _inew = model->header->numseq - 1;
+ else if(_inew >= model->header->numseq)
+ _inew = 0;
+
+ model->sequence = _inew;
+ model->frame = 0;
+ {
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ hlmdl_sequencelist_t *pseqdesc;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ if(_inew == 0)
+ {
+ pseqdesc = (hlmdl_sequencelist_t *) ((byte *) model->header + model->header->seqindex) + model->sequence;
+ }
+ else
+ {
+ pseqdesc = (hlmdl_sequencelist_t *) ((byte *) model->header + model->header->seqindex) + model->sequence;
+ }
+
+ Sys_Printf("Current Sequence: %s\n", pseqdesc->name);
+ }
+
+ return model->sequence;
+}
+
+/*
+=======================================================================================================================
+ HL_SetController - control where the model is facing (upper body usually)
+=======================================================================================================================
+ */
+void HL_SetController(hlmodel_t *model, int num, float value)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ int real, limit;
+ hlmdl_bonecontroller_t *control = (hlmdl_bonecontroller_t *)
+ ((byte *) model->header + model->header->controllerindex);
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ if(num >= model->header->numcontrollers) return;
+
+ if(num == 4)
+ {
+ limit = 64;
+ }
+ else
+ {
+ limit = 255;
+ }
+
+ if(control->type & (0x0008 | 0x0010 | 0x0020))
+ {
+ if(control->end < control->start) value = -value;
+
+ if(control->start + 359.0 >= control->end)
+ {
+ if(value > ((control->start + control->end) / 2.0) + 180) value = value - 360;
+ if(value < ((control->start + control->end) / 2.0) - 180) value = value + 360;
+ }
+ else
+ {
+ if(value > 360)
+ value = value - (int) (value / 360.0) * 360.0;
+ else if(value < 0)
+ value = value + (int) ((value / -360.0) + 1) * 360.0;
+ }
+ }
+
+ real = limit * (value - control[num].start) / (control[num].end - control[num].start);
+ if(real < 0) real = 0;
+ if(real > limit) real = limit;
+ model->controller[num] = real;
+}
+
+/*
+=======================================================================================================================
+ HL_CalculateBones - calculate bone positions - quaternion+vector in one function
+=======================================================================================================================
+ */
+void HL_CalculateBones(int offset, int frame, vec4_t adjust, hlmdl_bone_t *bone, hlmdl_anim_t *animation, float *destination)
+{
+ /*~~~~~~~~~~*/
+ int i;
+ vec3_t angle;
+ /*~~~~~~~~~~*/
+
+ /* For each vector */
+ for(i = 0; i < 3; i++)
+ {
+ /*~~~~~~~~~~~~~~~*/
+ int o = i + offset; /* Take the value offset - allows quaternion & vector in one function */
+ /*~~~~~~~~~~~~~~~*/
+
+ angle[i] = bone->value[o]; /* Take the bone value */
+
+ if(animation->offset[o] != 0)
+ {
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ int tempframe = frame;
+ hlmdl_animvalue_t *animvalue = (hlmdl_animvalue_t *) ((byte *) animation + animation->offset[o]);
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ /* find values including the required frame */
+ while(animvalue->num.total <= tempframe)
+ {
+ tempframe -= animvalue->num.total;
+ animvalue += animvalue->num.valid + 1;
+ }
+ if(animvalue->num.valid > tempframe)
+ {
+ if(animvalue->num.valid > (tempframe + 1))
+ angle[i] += animvalue[tempframe + 1].value * 1; // + 0 * animvalue[tempframe + 2].value * bone->scale[o];
+ else
+ angle[i] = animvalue[animvalue->num.valid].value;
+ angle[i] = bone->value[o] + angle[i] * bone->scale[o];
+ }
+ else
+ {
+ if(animvalue->num.total <= tempframe + 1)
+ {
+ angle[i] +=
+ (animvalue[animvalue->num.valid].value * 1 +
+ 0 * animvalue[animvalue->num.valid + 2].value) *
+ bone->scale[o];
+ }
+ else
+ {
+ angle[i] += animvalue[animvalue->num.valid].value * bone->scale[o];
+ }
+ }
+ }
+
+ if(bone->bonecontroller[o] != -1) { /* Add the programmable offset. */
+ angle[i] += adjust[bone->bonecontroller[o]];
+ }
+ }
+
+ if(offset < 3)
+ {
+ VectorCopy(angle, destination); /* Just a standard vector */
+ }
+ else
+ {
+ QuaternionGLAngle(angle, destination); /* A quaternion */
+ }
+}
+
+/*
+=======================================================================================================================
+ HL_CalcBoneAdj - Calculate the adjustment values for the programmable controllers
+=======================================================================================================================
+ */
+void HL_CalcBoneAdj(hlmodel_t *model)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ int i;
+ float value;
+ hlmdl_bonecontroller_t *control = (hlmdl_bonecontroller_t *)
+ ((byte *) model->header + model->header->controllerindex);
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ for(i = 0; i < model->header->numcontrollers; i++)
+ {
+ /*~~~~~~~~~~~~~~~~~~~~~*/
+ int j = control[i].index;
+ /*~~~~~~~~~~~~~~~~~~~~~*/
+/*
+ if(control[i].type & 0x8000)
+ {
+ value = model->controller[j] + control[i].start;
+ }
+ else
+ {
+ value = model->controller[j];
+ if(value < 0)
+ value = 0;
+ else if(value > 1.0)
+ value = 1.0;
+ value = (1.0 - value) * control[i].start + value * control[i].end;
+ }
+*/
+
+ if (j <= 3)
+ {
+ // check for 360% wrapping
+ if (control[i].type & STUDIO_RLOOP)
+ {
+ value = model->controller[j] * (360.0/256.0) + control[i].start;
+ }
+ else
+ {
+ value = model->controller[j] / 255.0;
+ if (value < 0)
+ value = 0;
+ if (value > 1.0)
+ value = 1.0;
+ value = (1.0 - value) * control[i].start + value * control[i].end;
+ }
+ // Con_DPrintf( "%d %d %f : %f\n", m_controller[i], m_prevcontroller[i], value, dadt );
+ }
+ else
+ {
+ value = /*model->mouth*/1 / 64.0;
+ if (value > 1.0)
+ value = 1.0;
+ value = (1.0 - value) * control[i].start + value * control[i].end;
+ // Con_DPrintf("%d %f\n", mouthopen, value );
+ }
+
+ /* Rotational controllers need their values converted */
+ switch(control[i].type & STUDIO_TYPES)
+ {
+ case STUDIO_XR:
+ case STUDIO_YR:
+ case STUDIO_ZR:
+ model->adjust[i] = value * (M_PI / 180.0);
+ break;
+ case STUDIO_X:
+ case STUDIO_Y:
+ case STUDIO_Z:
+ model->adjust[i] = value;
+ break;
+ }
+ }
+}
+
+/*
+=======================================================================================================================
+ HL_SetupBones - determine where vertex should be using bone movements
+=======================================================================================================================
+ */
+void HL_SetupBones(hlmodel_t *model)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ int i;
+ float matrix[3][4];
+ static vec3_t positions[128];
+ static vec4_t quaternions[128];
+ hlmdl_sequencelist_t *sequence = (hlmdl_sequencelist_t *) ((byte *) model->header + model->header->seqindex) +
+ model->sequence;
+ hlmdl_sequencedata_t *sequencedata = (hlmdl_sequencedata_t *)
+ ((byte *) model->header + model->header->seqgroups) +
+ sequence->seqindex;
+ hlmdl_anim_t *animation = (hlmdl_anim_t *)
+ ((byte *) model->header + sequencedata->data + sequence->index);
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ HL_CalcBoneAdj(model); /* Deal with programmable controllers */
+
+ if(sequence->motiontype & STUDIO_X)
+ positions[sequence->motionbone][0] = 0.0;
+ if(sequence->motiontype & STUDIO_Y)
+ positions[sequence->motionbone][1] = 0.0;
+ if(sequence->motiontype & STUDIO_Z)
+ positions[sequence->motionbone][2] = 0.0;
+
+ /* Sys_Printf("Frame: %i\n", model->frame); */
+ for(i = 0; i < model->header->numbones; i++)
+ {
+ /*
+ * There are two vector offsets in the structure. The first seems to be the
+ * positions of the bones, the second the quats of the bone matrix itself. We
+ * convert it inside the routine - Inconsistant, but hey.. so's the whole model
+ * format.
+ */
+ HL_CalculateBones(0, model->frame, model->adjust, model->bones + i, animation + i, positions[i]);
+ HL_CalculateBones(3, model->frame, model->adjust, model->bones + i, animation + i, quaternions[i]);
+
+ /* FIXME: Blend the bones and make them cry :) */
+ QuaternionGLMatrix(quaternions[i][0], quaternions[i][1], quaternions[i][2], quaternions[i][3], matrix);
+ matrix[0][3] = positions[i][0];
+ matrix[1][3] = positions[i][1];
+ matrix[2][3] = positions[i][2];
+
+ /* If we have a parent, take the addition. Otherwise just copy the values */
+ if(model->bones[i].parent>=0)
+ {
+ R_ConcatTransforms(transform_matrix[model->bones[i].parent], matrix, transform_matrix[i]);
+ }
+ else
+ {
+ memcpy(transform_matrix[i], matrix, 12 * sizeof(float));
+ }
+ }
+}
+
+/*
+=======================================================================================================================
+ Chrome
+=======================================================================================================================
+*/
+/*
+================
+TransformFinalVert
+================
+*/
+void Lighting (float *lv, int bone, int flags, vec3_t normal)
+{
+ float illum;
+ float lightcos;
+
+ illum = ambientlight;
+
+ if (flags & STUDIO_NF_FLATSHADE)
+ {
+ illum += shadelight * 0.8;
+ }
+ else
+ {
+ float r;
+ lightcos = DotProduct (normal, g_blightvec[bone]); // -1 colinear, 1 opposite
+
+ if (lightcos > 1.0)
+ lightcos = 1;
+
+ illum += shadelight;
+
+ //r = g_lambert;
+ //if (r <= 1.0)
+ r = 1.0;
+
+ lightcos = (lightcos + (r - 1.0)) / r; // do modified hemispherical lighting
+ if (lightcos > 0.0)
+ {
+ illum -= shadelight * lightcos;
+ }
+ if (illum <= 0)
+ illum = 0;
+ }
+
+ if (illum > 255)
+ illum = 255;
+ *lv = illum / 255.0; // Light from 0 to 1.0
+}
+
+void VectorIRotate (const vec3_t in1, const float in2[3][4], vec3_t out)
+{
+ out[0] = in1[0]*in2[0][0] + in1[1]*in2[1][0] + in1[2]*in2[2][0];
+ out[1] = in1[0]*in2[0][1] + in1[1]*in2[1][1] + in1[2]*in2[2][1];
+ out[2] = in1[0]*in2[0][2] + in1[1]*in2[1][2] + in1[2]*in2[2][2];
+}
+
+void SetupLighting ( hlmodel_t *model )
+{ // dr_mabuse1981: edited abit to make halflife model NOT look ughly and for Shpulds model brightness cvar
+ int i;
+ if(r_model_brightness.value)
+ ambientlight = 256;
+ else
+ ambientlight = 128;
+ shadelight = 33;
+
+ g_lightvec[0] = shadevector[0];
+ g_lightvec[1] = shadevector[1];
+ g_lightvec[2] = shadevector[2];
+
+ g_lightcolor[0] = lightcolor[0];
+ g_lightcolor[1] = lightcolor[1];
+ g_lightcolor[2] = lightcolor[2];
+
+ // TODO: only do it for bones that actually have textures
+ for (i = 0; i < model->header->numbones; i++)
+ {
+ VectorIRotate( g_lightvec, transform_matrix[i], g_blightvec[i] );
+ }
+}
+
+void Chrome (int *pchrome, int bone, vec3_t normal)
+{
+ float n;
+
+ if (g_chromeage[bone] != g_smodels_total)
+ {
+ // calculate vectors from the viewer to the bone. This roughly adjusts for position
+ vec3_t chromeupvec; // g_chrome t vector in world reference frame
+ vec3_t chromerightvec; // g_chrome s vector in world reference frame
+ vec3_t tmp; // vector pointing at bone in world reference frame
+ VectorScale( m_angles, -1, tmp );
+ tmp[0] += transform_matrix[bone][0][3];
+ tmp[1] += transform_matrix[bone][1][3];
+ tmp[2] += transform_matrix[bone][2][3];
+ VectorNormalize( tmp );
+ CrossProduct( tmp, g_vright, chromeupvec );
+ VectorNormalize( chromeupvec );
+ CrossProduct( tmp, chromeupvec, chromerightvec );
+ VectorNormalize( chromerightvec );
+ VectorIRotate( chromeupvec, transform_matrix[bone], g_chromeup[bone] );
+ VectorIRotate( chromerightvec, transform_matrix[bone], g_chromeright[bone] );
+ g_chromeage[bone] = g_smodels_total;
+ }
+ // calc s coord
+ n = DotProduct( normal, g_chromeright[bone] );
+ pchrome[0] = (n + 1.0) * 32; // FIX: make this a float
+ // calc t coord
+ n = DotProduct( normal, g_chromeup[bone] );
+ pchrome[1] = (n + 1.0) * 32; // FIX: make this a float
+}
+
+/*
+=======================================================================================================================
+ R_Draw_HL_AliasModel - main drawing function
+=======================================================================================================================
+*/
+void R_DrawHLModel(entity_t *curent)
+{
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ hlmodelcache_t *modelc = static_cast(Mod_Extradata(curent->model));
+ hlmodel_t model;
+ int b, m, v;
+ short *skins;
+ hlmdl_sequencelist_t *sequence;
+ float *lv;
+ float lv_tmp;
+ int lnum;
+ vec3_t dist;
+ float add;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ //general model
+ model.header = (hlmdl_header_t *) ((char *)modelc + modelc->header);
+ model.textures = (hlmdl_tex_t *) ((char *)modelc + modelc->textures);
+ model.bones = (hlmdl_bone_t *) ((char *)modelc + modelc->bones);
+ model.bonectls = (hlmdl_bonecontroller_t *) ((char *)modelc + modelc->bonectls);
+
+ //specific to entity
+ model.sequence = curent->frame;
+ model.frame = 0;
+ model.frametime = 0;
+
+ HL_NewSequence(&model, curent->frame);
+
+ skins = (short *) ((byte *) model.header + model.header->skins);
+ sequence = (hlmdl_sequencelist_t *) ((byte *) model.header + model.header->seqindex) +
+ model.sequence;
+
+ model.controller[0] = curent->bonecontrols[0];
+ model.controller[1] = curent->bonecontrols[1];
+ model.controller[2] = curent->bonecontrols[2];
+ model.controller[3] = curent->bonecontrols[3];
+
+ model.frametime += (cl.time /* - cl.lerpents[curent->keynum].framechange*/)*sequence->timing;
+
+ if (model.frametime>=1)
+ {
+ model.frame += (int) model.frametime;
+ model.frametime -= (int)model.frametime;
+ }
+
+ if (!sequence->numframes)
+ return;
+/*
+ if(model.frame >= sequence->numframes)
+ model.frame %= sequence->numframes;
+*/
+ model.frame = curent->frame; // dr_mabuse1981: This makes your Halflife model frame based (only for sequence 0 atm but better than the old shit.)
+ if (sequence->motiontype)
+ model.frame = sequence->numframes-1;
+
+ sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
+ sceGuShadeModel(GU_SMOOTH);
+
+ Con_Printf("%s %i\n", sequence->name, sequence->unknown1[0]);
+
+ sceGumPushMatrix();
+
+ for(int i = 0; i < 3; i++) m_angles[i] = currententity->angles[i];
+
+ R_LightPoint(curent->origin); // LordHavoc: lightcolor is all that matters from this
+
+ for (lnum=0 ; lnum= cl.time)
+ {
+ VectorSubtract (currententity->origin,
+ cl_dlights[lnum].origin,
+ dist);
+ add = cl_dlights[lnum].radius - Length(dist);
+
+ // LordHavoc: .lit support begin
+ if (add > 0)
+ {
+ lightcolor[0] += add * cl_dlights[lnum].color[0];
+ lightcolor[1] += add * cl_dlights[lnum].color[1];
+ lightcolor[2] += add * cl_dlights[lnum].color[2];
+ }
+ // LordHavoc: .lit support end
+ }
+ }
+
+ VectorScale(lightcolor, 1.0f / 200.0f, lightcolor);
+
+ float an;
+ an = curent->angles[1]/180*M_PI;
+ shadevector[0] = cosf(-an);
+ shadevector[1] = sinf(-an);
+ shadevector[2] = 1;
+ VectorNormalize (shadevector);
+
+ R_BlendedRotateForEntity(curent, 0);
+
+ HL_SetupBones(&model); /* Setup the bones */
+ SetupLighting(&model); /* Setup the light */
+
+ g_smodels_total++; // render data cache cookie
+
+ g_pvlightvalues = &g_lightvalues[0];
+
+ /* Manipulate each mesh directly */
+ for(b = 0; b < model.header->numbodyparts; b++)
+ {
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ hlmdl_bodypart_t *bodypart = (hlmdl_bodypart_t *) ((byte *) model.header + model.header->bodypartindex) +
+ b;
+ int bodyindex = (0 / bodypart->base) % bodypart->nummodels;
+ hlmdl_model_t *amodel = (hlmdl_model_t *) ((byte *) model.header + bodypart->modelindex) + bodyindex;
+ byte *bone = ((byte *) model.header + amodel->vertinfoindex);
+ byte *nbone = ((byte *) model.header + amodel->norminfoindex);
+ vec3_t *verts = (vec3_t *) ((byte *) model.header + amodel->vertindex);
+ vec3_t *norms = (vec3_t *) ((byte *) model.header + amodel->normindex);
+ vec3_t transformed[2048];
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ for(v = 0; v < amodel->numverts; v++) // Transform per the matrix
+ {
+ VectorTransform(verts[v], transform_matrix[bone[v]], transformed[v]);
+ }
+
+ lv = (float *)g_pvlightvalues;
+ for(m = 0; m < amodel->nummesh; m++)
+ {
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ hlmdl_mesh_t *mesh = (hlmdl_mesh_t *) ((byte *) model.header + amodel->meshindex) + m;
+ float tex_w = 1.0f / model.textures[skins[mesh->skinindex]].w;
+ float tex_h = 1.0f / model.textures[skins[mesh->skinindex]].h;
+ int flags = model.textures[skins[mesh->skinindex]].flags;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ for (int c = 0; c < mesh->numnorms; c++, lv += 3, norms++, nbone++)
+ {
+ Lighting (&lv_tmp, *nbone, flags, (float *)norms);
+ // FIX: move this check out of the inner loop
+ if (flags & STUDIO_NF_CHROME)
+ Chrome(g_chrome[(float (*)[3])lv - g_pvlightvalues], *nbone, (float *)norms );
+
+ lv[0] = lv_tmp * g_lightcolor[0];
+ lv[1] = lv_tmp * g_lightcolor[1];
+ lv[2] = lv_tmp * g_lightcolor[2];
+ }
+
+ if (model.textures[skins[mesh->skinindex]].flags & STUDIO_NF_CHROME)
+ {
+ GL_Bind(model.textures[skins[mesh->skinindex]].i);
+ GL_Draw_HL_AliasChrome((short *) ((byte *) model.header + mesh->index), transformed, tex_w, tex_h, lv);
+ }
+ else
+ {
+ GL_Bind(model.textures[skins[mesh->skinindex]].i);
+ GL_Draw_HL_AliasFrame((short *) ((byte *) model.header + mesh->index), transformed, tex_w, tex_h, lv);
+ }
+ }
+ }
+ sceGuShadeModel(GU_FLAT);
+ sceGumPopMatrix();
+ sceGumUpdateMatrix();
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+}
+
+/*
+ =======================================================================================================================
+ GL_Draw_HL_AliasFrame - clip and draw all triangles
+ =======================================================================================================================
+ */
+void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float tex_h, float *lv)
+{
+ int count = 0;
+ while (1)
+ {
+ count = *order++;
+
+ if (!count)
+ break; // done
+
+ int prim;
+
+ if(count < 0)
+ {
+ count = -count;
+ prim = GU_TRIANGLE_FAN;
+ }
+ else
+ {
+ prim = GU_TRIANGLE_STRIP;
+ }
+
+ // Allocate the vertices.
+ struct vertex
+ {
+ float u, v;
+ unsigned int color;
+ float x, y, z;
+ };
+
+ vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * count));
+
+ for (int vertex_index = 0; vertex_index < count; ++vertex_index)
+ {
+ float *verts = transformed[order[0]];
+
+ out[vertex_index].u = order[2] * tex_w;
+ out[vertex_index].v = order[3] * tex_h;
+ order += 4;
+
+ lv = g_pvlightvalues[order[1]];
+ //color clamp
+ for(int i = 0; i < 3; i++)
+ {
+ if(lv[i] > 1)
+ lv[i] = 1;
+
+ if(lv[i] < 0)
+ lv[i] = 0;
+ }
+ out[vertex_index].x = verts[0];
+ out[vertex_index].y = verts[1];
+ out[vertex_index].z = verts[2];
+ out[vertex_index].color = GU_COLOR(lv[0], lv[1], lv[2], 1.0f);
+ }
+ if(r_showtris.value)
+ {
+ sceGuDisable(GU_TEXTURE_2D);
+ }
+ sceGuDrawArray(r_showtris.value ? GU_LINE_STRIP : prim, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_COLOR_8888, count, 0, out);
+ if(r_showtris.value)
+ {
+ sceGuEnable(GU_TEXTURE_2D);
+ }
+ }
+}
+
+void GL_Draw_HL_AliasChrome(short *order, vec3_t *transformed, float tex_w, float tex_h, float *lv)
+{
+ int count = 0;
+ while (1)
+ {
+ count = *order++;
+
+ if (!count)
+ break; // done
+
+ int prim;
+
+ if(count < 0)
+ {
+ count = -count;
+ prim = GU_TRIANGLE_FAN;
+ }
+ else
+ {
+ prim = GU_TRIANGLE_STRIP;
+ }
+
+ // Allocate the vertices.
+ struct vertex
+ {
+ float u, v;
+ unsigned int color;
+ float x, y, z;
+ };
+
+ vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * count));
+
+ for (int vertex_index = 0; vertex_index < count; ++vertex_index)
+ {
+ float *verts = transformed[order[0]];
+
+ out[vertex_index].u = g_chrome[order[1]][0] * tex_w;
+ out[vertex_index].v = g_chrome[order[1]][1] * tex_h;
+ order += 4;
+
+ lv = g_pvlightvalues[order[1]];
+ //color clamp
+ for(int i = 0; i < 3; i++)
+ {
+ if(lv[i] > 1)
+ lv[i] = 1;
+
+ if(lv[i] < 0)
+ lv[i] = 0;
+ }
+ out[vertex_index].x = verts[0];
+ out[vertex_index].y = verts[1];
+ out[vertex_index].z = verts[2];
+ out[vertex_index].color = GU_COLOR(lv[0], lv[1], lv[2], 1.0f);
+ }
+ if(r_showtris.value)
+ {
+ sceGuDisable(GU_TEXTURE_2D);
+ }
+ sceGuDrawArray(r_showtris.value ? GU_LINE_STRIP : prim, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_COLOR_8888, count, 0, out);
+ if(r_showtris.value)
+ {
+ sceGuEnable(GU_TEXTURE_2D);
+ }
+ }
+}
+
diff --git a/source/psp/video_hardware_hlmdl.h b/source/psp/video_hardware_hlmdl.h
new file mode 100644
index 0000000..4a80c57
--- /dev/null
+++ b/source/psp/video_hardware_hlmdl.h
@@ -0,0 +1,356 @@
+/*
+ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Half-Life Model Renderer (Experimental) Copyright (C) 2001 James 'Ender' Brown [ender@quakesrc.org] This program is
+ free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ details. You should have received a copy of the GNU General Public License along with this program; if not, write
+ to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. fromquake.h -
+
+ model_hl.h - halflife model structure
+ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ */
+#define HLPOLYHEADER (('T' << 24) + ('S' << 16) + ('D' << 8) + 'I') /* little-endian "IDST" */
+#define HLMDLHEADER "IDST"
+
+#define MAXSTUDIOTRIANGLES 20000 // TODO: tune this
+#define MAXSTUDIOVERTS 2048 // TODO: tune this
+#define MAXSTUDIOSEQUENCES 256 // total animation sequences
+#define MAXSTUDIOSKINS 100 // total textures
+#define MAXSTUDIOSRCBONES 512 // bones allowed at source movement
+#define MAXSTUDIOBONES 128 // total bones actually used
+#define MAXSTUDIOMODELS 32 // sub-models per model
+#define MAXSTUDIOBODYPARTS 32
+#define MAXSTUDIOGROUPS 4
+#define MAXSTUDIOANIMATIONS 512 // per sequence
+#define MAXSTUDIOMESHES 256
+#define MAXSTUDIOEVENTS 1024
+#define MAXSTUDIOPIVOTS 256
+#define MAXSTUDIOCONTROLLERS 8
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ main model header
+ -----------------------------------------------------------------------------------------------------------------------
+ */
+typedef struct
+{
+ int filetypeid; //IDSP
+ int version; //10
+ char name[64];
+ int filesize;
+ vec3_t unknown3[5];
+
+ int flags;
+ int numbones;
+ int boneindex;
+ int numcontrollers;
+ int controllerindex;
+ int unknown5[2];
+ int numseq;
+ int seqindex;
+
+ int numseqgroups; // demand loaded sequences
+ int seqgroups;
+
+ int numtextures;
+ int textures;
+ int texturedataindex;
+ int numskinref; // replaceable textures
+ int numskinfamilies;
+
+
+ int skins;
+ int numbodyparts;
+ int bodypartindex;
+
+ int unknown9[8];
+} hlmdl_header_t;
+
+typedef struct
+{
+ int id;
+ int version;
+ char name[64];
+ int length;
+ vec3_t eyeposition; // ideal eye position
+ vec3_t min; // ideal movement hull size
+ vec3_t max;
+ vec3_t bbmin; // clipping bounding box
+ vec3_t bbmax;
+ int flags;
+ int numbones; // bones
+ int boneindex;
+ int numbonecontrollers; // bone controllers
+ int bonecontrollerindex;
+ int numhitboxes; // complex bounding boxes
+ int hitboxindex;
+ int numseq; // animation sequences
+ int seqindex;
+ int numseqgroups; // demand loaded sequences
+ int seqgroupindex;
+ int numtextures; // raw textures
+ int textureindex;
+
+ int texturedataindex;
+ int numskinref; // replaceable textures
+ int numskinfamilies;
+
+ int skinindex;
+ int numbodyparts;
+ int bodypartindex;
+ int numattachments; // queryable attachable points
+ int attachmentindex;
+ int soundtable;
+ int soundindex;
+ int soundgroups;
+ int soundgroupindex;
+ int numtransitions; // animation node to animation node transition graph
+ int transitionindex;
+} studiohdr_t;
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ skin info
+ -----------------------------------------------------------------------------------------------------------------------
+*/
+
+// lighting options
+#define STUDIO_NF_FLATSHADE 0x0001
+#define STUDIO_NF_CHROME 0x0002
+#define STUDIO_NF_FULLBRIGHT 0x0004
+
+typedef struct
+{
+ char name[64];
+ int flags;
+ int w; /* width */
+ int h; /* height */
+ int i; /* index */
+} hlmdl_tex_t;
+
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ body part index
+ -----------------------------------------------------------------------------------------------------------------------
+ */
+typedef struct
+{
+ char name[64];
+ int nummodels;
+ int base;
+ int modelindex;
+} hlmdl_bodypart_t;
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ meshes
+ -----------------------------------------------------------------------------------------------------------------------
+ */
+typedef struct
+{
+ int numtris;
+ int index;
+ int skinindex;
+ int numnorms; // per mesh normals
+ int normindex; // normal vec3_t
+} hlmdl_mesh_t;
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ bones
+ -----------------------------------------------------------------------------------------------------------------------
+ */
+typedef struct
+{
+ char name[32];
+ int parent;
+ int unknown1;
+ int bonecontroller[6];
+ float value[6];
+ float scale[6];
+} hlmdl_bone_t;
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ bone controllers
+ -----------------------------------------------------------------------------------------------------------------------
+ */
+typedef struct
+{
+ int name;
+ int type;
+ float start;
+ float end;
+ int unknown1;
+ int index;
+} hlmdl_bonecontroller_t;
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ halflife model descriptor
+ -----------------------------------------------------------------------------------------------------------------------
+ */
+typedef struct
+{
+ char name[64];
+
+ int type;
+
+ float boundingradius;
+
+ int nummesh;
+ int meshindex;
+
+ int numverts;
+ int vertinfoindex;
+ int vertindex;
+
+ int numnorms;
+ int norminfoindex;
+ int normindex;
+
+ int numgroups;
+ int groupindex;
+} hlmdl_model_t;
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ animation
+ -----------------------------------------------------------------------------------------------------------------------
+ */
+typedef struct
+{
+ unsigned short offset[6];
+} hlmdl_anim_t;
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ animation frames
+ -----------------------------------------------------------------------------------------------------------------------
+ */
+typedef union
+{
+ struct
+ {
+ byte valid;
+ byte total;
+ } num;
+ short value;
+} hlmdl_animvalue_t;
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ sequence descriptions
+ -----------------------------------------------------------------------------------------------------------------------
+ */
+typedef struct
+{
+ char name[32];
+ float timing;
+ int unknown1[5];
+ int numframes;
+ int unknown2[2];
+ int motiontype;
+ int motionbone;
+ vec3_t unknown3;
+ int unknown4[2];
+ vec3_t unknown5[2];
+ int unknown6;
+ int index;
+ int unknown7[2];
+ float unknown[4];
+ int unknown8;
+ int seqindex;
+ int unknown9[4];
+} hlmdl_sequencelist_t;
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ sequence groups
+ -----------------------------------------------------------------------------------------------------------------------
+ */
+typedef struct
+{
+ char name[96]; /* should be split label[32] and name[64] */
+ void * cache;
+ int data;
+} hlmdl_sequencedata_t;
+
+/*
+ -----------------------------------------------------------------------------------------------------------------------
+ halflife model internal structure
+ -----------------------------------------------------------------------------------------------------------------------
+ */
+typedef struct
+{
+ int sequence;
+ int frame; /* Current animation sequence and frame */
+ float frametime; /* Time of last frame drawn */
+ float controller[4]; /* Position of bone controllers */
+ float adjust[4];
+
+ /* Static pointers */
+ hlmdl_header_t *header;
+ hlmdl_tex_t *textures;
+ hlmdl_bone_t *bones;
+ hlmdl_bonecontroller_t *bonectls;
+} hlmodel_t;
+
+typedef struct //this is stored as the cache. an hlmodel_t is generated when drawing
+{
+ int header;
+ int textures;
+ int bones;
+ int bonectls;
+} hlmodelcache_t;
+
+// lighting options
+#define STUDIO_NF_FLATSHADE 0x0001
+#define STUDIO_NF_CHROME 0x0002
+#define STUDIO_NF_FULLBRIGHT 0x0004
+
+// motion flags
+#define STUDIO_X 0x0001
+#define STUDIO_Y 0x0002
+#define STUDIO_Z 0x0004
+#define STUDIO_XR 0x0008
+#define STUDIO_YR 0x0010
+#define STUDIO_ZR 0x0020
+#define STUDIO_LX 0x0040
+#define STUDIO_LY 0x0080
+#define STUDIO_LZ 0x0100
+#define STUDIO_AX 0x0200
+#define STUDIO_AY 0x0400
+#define STUDIO_AZ 0x0800
+#define STUDIO_AXR 0x1000
+#define STUDIO_AYR 0x2000
+#define STUDIO_AZR 0x4000
+#define STUDIO_TYPES 0x7FFF
+#define STUDIO_RLOOP 0x8000 // controller that wraps shortest distance
+
+// sequence flags
+#define STUDIO_LOOPING 0x0001
+
+// bone flags
+#define STUDIO_HAS_NORMALS 0x0001
+#define STUDIO_HAS_VERTICES 0x0002
+#define STUDIO_HAS_BBOX 0x0004
+#define STUDIO_HAS_CHROME 0x0008 // if any of the textures have chrome on them
+
+#define RAD_TO_STUDIO (32768.0/M_PI)
+#define STUDIO_TO_RAD (M_PI/32768.0)
+
+/* HL mathlib prototypes: */
+void QuaternionGLAngle(const vec3_t angles, vec4_t quaternion);
+void QuaternionGLMatrix(float x, float y, float z, float w, vec4_t *GLM);
+//void UploadTexture(hlmdl_tex_t *ptexture, qbyte *data, qbyte *pal);
+
+/* HL drawing */
+qboolean Mod_LoadHLModel (model_t *mod, void *buffer);
+int HL_CurSequence(hlmodel_t model);
+int HL_NewSequence(hlmodel_t * model, int _inew);
+void HL_SetController(hlmodel_t *model, int num, float value);
+void R_DrawHLModel(entity_t *curent);
diff --git a/source/psp/video_hardware_images.cpp b/source/psp/video_hardware_images.cpp
new file mode 100644
index 0000000..c453194
--- /dev/null
+++ b/source/psp/video_hardware_images.cpp
@@ -0,0 +1,1075 @@
+/*
+Copyright (C) 2008-2009 Crow_bar.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include
+
+extern "C"
+{
+#include
+#include "../quakedef.h"
+}
+
+cvar_t jpeg_compression_level = {"jpeg_compression_level", "75"};
+
+int image_width;
+int image_height;
+static int image_palette_type = 0;
+static byte image_palette[1024];
+
+
+#define IMAGE_MAX_DIMENSIONS 4096
+
+/*
+=============
+LoadJPG
+=============
+*/
+byte *LoadJPG (FILE *fin, int matchwidth, int matchheight)
+{
+ int i, row_stride;
+ byte *data, *scanline, *p;
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+
+ cinfo.err = jpeg_std_error (&jerr);
+ jpeg_create_decompress (&cinfo);
+ jpeg_stdio_src (&cinfo, fin);
+ jpeg_read_header (&cinfo, true);
+ jpeg_start_decompress (&cinfo);
+
+ if (cinfo.image_width > IMAGE_MAX_DIMENSIONS || cinfo.image_height > IMAGE_MAX_DIMENSIONS)
+ {
+ return NULL;
+ }
+
+ if ((matchwidth && cinfo.image_width != matchwidth) || (matchheight && cinfo.image_height != matchheight))
+ {
+ jpeg_finish_decompress (&cinfo);
+ jpeg_destroy_decompress (&cinfo);
+ return NULL;
+ }
+
+ Con_Printf("JPG: Allocating data\n");
+
+ data = static_cast(malloc (cinfo.image_width * cinfo.image_height * 4));
+ row_stride = cinfo.output_width * cinfo.output_components;
+ scanline = static_cast(malloc (row_stride));
+
+ Con_Printf("JPG: done allocating data\n");
+
+ p = data;
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines (&cinfo, &scanline, 1);
+
+ // convert the image to RGBA
+ switch (cinfo.output_components)
+ {
+ // RGB images
+ case 3:
+ for (i=0 ; imanufacturer != 0x0a
+ || pcx->version != 5
+ || pcx->encoding != 1
+ || pcx->bits_per_pixel != 8
+ || pcx->xmax >= 320
+ || pcx->ymax >= 256)
+ {
+ Con_Printf ("Bad pcx file\n");
+ return NULL;
+ }
+
+ if (matchwidth && (pcx->xmax+1) != matchwidth)
+ {
+ fclose (f);
+ return NULL;
+ }
+
+ if (matchheight && (pcx->ymax+1) != matchheight)
+ {
+ fclose (f);
+ return NULL;
+ }
+
+ // seek to palette
+ fseek (f, -768, SEEK_END);
+ fread (palette, 1, 768, f);
+
+ fseek (f, sizeof(pcxbuf) - 4, SEEK_SET);
+
+ count = (pcx->xmax+1) * (pcx->ymax+1);
+
+ image_rgba = static_cast(malloc( count ));
+
+ for (y=0 ; y<=pcx->ymax ; y++)
+ {
+ pix = image_rgba + y * (pcx->xmax+1);
+ for (x=0 ; x<=pcx->xmax ; ) // muff - fixed - was referencing ymax
+ {
+ dataByte = fgetc(f);
+
+ if((dataByte & 0xC0) == 0xC0)
+ {
+ runLength = dataByte & 0x3F;
+ dataByte = fgetc(f);
+ }
+ else
+ runLength = 1;
+
+ while(runLength-- > 0)
+ pix[x++] = dataByte;
+ }
+ }
+ image_width = pcx->xmax+1;
+ image_height = pcx->ymax+1;
+
+ memcpy(image_palette, palette, sizeof(palette));
+ image_palette_type = PAL_RGB;
+
+ fclose (f);
+
+ return image_rgba;
+}
+
+/*
+================
+LoadWal
+================
+*/
+
+typedef struct miptexq2_s
+{
+ char name[32];
+ unsigned width, height;
+ unsigned offsets[4]; // four mip maps stored
+ char animname[32]; // next frame in animation chain
+ int flags;
+ int contents;
+ int value;
+} miptexq2_t;
+
+byte *LoadWAL (char *name)
+{
+ miptexq2_t *mt;
+ int width, height, ofs, size;
+ byte *data;
+
+ mt = (miptexq2_t*)COM_LoadFile (name, 0);
+ if (!mt)
+ {
+ return NULL;
+ }
+
+ width = LittleLong (mt->width);
+ height = LittleLong (mt->height);
+ ofs = LittleLong (mt->offsets[0]);
+
+ size = width * height;
+
+ data = static_cast(malloc(size));
+ memcpy(data, (byte *)mt + ofs, size);
+
+ image_palette_type = PAL_Q2;
+
+ Z_Free(mt);
+
+ return data;
+}
+
+/*
+=========================================================
+
+ Targa
+
+=========================================================
+*/
+
+#define TGA_MAXCOLORS 16384
+
+/* Definitions for image types. */
+#define TGA_Null 0 /* no image data */
+#define TGA_Map 1 /* Uncompressed, color-mapped images. */
+#define TGA_RGB 2 /* Uncompressed, RGB images. */
+#define TGA_Mono 3 /* Uncompressed, black and white images. */
+#define TGA_RLEMap 9 /* Runlength encoded color-mapped images. */
+#define TGA_RLERGB 10 /* Runlength encoded RGB images. */
+#define TGA_RLEMono 11 /* Compressed, black and white images. */
+#define TGA_CompMap 32 /* Compressed color-mapped data, using Huffman, Delta, and runlength encoding. */
+#define TGA_CompMap4 33 /* Compressed color-mapped data, using Huffman, Delta, and runlength encoding. 4-pass quadtree-type process. */
+
+/* Definitions for interleave flag. */
+#define TGA_IL_None 0 /* non-interleaved. */
+#define TGA_IL_Two 1 /* two-way (even/odd) interleaving */
+#define TGA_IL_Four 2 /* four way interleaving */
+#define TGA_IL_Reserved 3 /* reserved */
+
+/* Definitions for origin flag */
+#define TGA_O_UPPER 0 /* Origin in lower left-hand corner. */
+#define TGA_O_LOWER 1 /* Origin in upper left-hand corner. */
+
+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
+=============
+*/
+byte *LoadTGA (FILE *fin, int matchwidth, int matchheight)
+{
+ int w, h, x, y, realrow, truerow, baserow, i, temp1, temp2, pixel_size, map_idx;
+ int RLE_count, RLE_flag, size, interleave, origin;
+ bool mapped, rlencoded;
+ byte *data, *dst, r, g, b, a, j, k, l, *ColorMap;
+ TargaHeader header;
+
+ header.id_length = fgetc (fin);
+ header.colormap_type = fgetc (fin);
+ header.image_type = fgetc (fin);
+ header.colormap_index = fgetLittleShort (fin);
+ header.colormap_length = fgetLittleShort (fin);
+ header.colormap_size = fgetc (fin);
+ header.x_origin = fgetLittleShort (fin);
+ header.y_origin = fgetLittleShort (fin);
+ header.width = fgetLittleShort (fin);
+ header.height = fgetLittleShort (fin);
+ header.pixel_size = fgetc (fin);
+ header.attributes = fgetc (fin);
+
+ if (header.width > IMAGE_MAX_DIMENSIONS || header.height > IMAGE_MAX_DIMENSIONS)
+ {
+ Con_DPrintf ("TGA image %s exceeds maximum supported dimensions\n");
+ fclose (fin);
+ return NULL;
+ }
+
+ if ((matchwidth && header.width != matchwidth) || (matchheight && header.height != matchheight))
+ {
+ fclose (fin);
+ return NULL;
+ }
+
+ if (header.id_length != 0)
+ fseek (fin, header.id_length, SEEK_CUR);
+
+ /* validate TGA type */
+ switch (header.image_type)
+ {
+ case TGA_Map:
+ case TGA_RGB:
+ case TGA_Mono:
+ case TGA_RLEMap:
+ case TGA_RLERGB:
+ case TGA_RLEMono:
+ break;
+
+ default:
+ Con_DPrintf ("Unsupported TGA image %s: Only type 1 (map), 2 (RGB), 3 (mono), 9 (RLEmap), 10 (RLERGB), 11 (RLEmono) TGA images supported\n");
+ fclose (fin);
+ return NULL;
+ }
+
+ /* validate color depth */
+ switch (header.pixel_size)
+ {
+ case 8:
+ case 15:
+ case 16:
+ case 24:
+ case 32:
+ break;
+
+ default:
+ Con_DPrintf ("Unsupported TGA image %s: Only 8, 15, 16, 24 or 32 bit images (with colormaps) supported\n");
+ fclose (fin);
+ return NULL;
+ }
+
+ r = g = b = a = l = 0;
+
+ /* if required, read the color map information. */
+ ColorMap = NULL;
+ mapped = (header.image_type == TGA_Map || header.image_type == TGA_RLEMap) && header.colormap_type == 1;
+ if (mapped)
+ {
+ /* validate colormap size */
+ switch (header.colormap_size)
+ {
+ case 8:
+ case 15:
+ case 16:
+ case 32:
+ case 24:
+ break;
+
+ default:
+ Con_DPrintf ("Unsupported TGA image %s: Only 8, 15, 16, 24 or 32 bit colormaps supported\n");
+ fclose (fin);
+ return NULL;
+ }
+
+ temp1 = header.colormap_index;
+ temp2 = header.colormap_length;
+ if ((temp1 + temp2 + 1) >= TGA_MAXCOLORS)
+ {
+ fclose (fin);
+ return NULL;
+ }
+ ColorMap = static_cast(malloc (TGA_MAXCOLORS * 4));
+ map_idx = 0;
+ for (i = temp1 ; i < temp1 + temp2 ; ++i, map_idx += 4)
+ {
+ /* read appropriate number of bytes, break into rgb & put in map. */
+ switch (header.colormap_size)
+ {
+ case 8: /* grey scale, read and triplicate. */
+ r = g = b = getc (fin);
+ a = 255;
+ break;
+
+ case 15: /* 5 bits each of red green and blue. */
+ /* watch byte order. */
+ j = getc (fin);
+ k = getc (fin);
+ l = ((unsigned int)k << 8) + j;
+ r = (byte)(((k & 0x7C) >> 2) << 3);
+ g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
+ b = (byte)((j & 0x1F) << 3);
+ a = 255;
+ break;
+
+ case 16: /* 5 bits each of red green and blue, 1 alpha bit. */
+ /* watch byte order. */
+ j = getc (fin);
+ k = getc (fin);
+ l = ((unsigned int)k << 8) + j;
+ r = (byte)(((k & 0x7C) >> 2) << 3);
+ g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
+ b = (byte)((j & 0x1F) << 3);
+ a = (k & 0x80) ? 255 : 0;
+ break;
+
+ case 24: /* 8 bits each of blue, green and red. */
+ b = getc (fin);
+ g = getc (fin);
+ r = getc (fin);
+ a = 255;
+ l = 0;
+ break;
+
+ case 32: /* 8 bits each of blue, green, red and alpha. */
+ b = getc (fin);
+ g = getc (fin);
+ r = getc (fin);
+ a = getc (fin);
+ l = 0;
+ break;
+ }
+ ColorMap[map_idx+0] = r;
+ ColorMap[map_idx+1] = g;
+ ColorMap[map_idx+2] = b;
+ ColorMap[map_idx+3] = a;
+ }
+ }
+
+ /* check run-length encoding. */
+ rlencoded = (header.image_type == TGA_RLEMap || header.image_type == TGA_RLERGB || header.image_type == TGA_RLEMono);
+ RLE_count = RLE_flag = 0;
+
+ image_width = w = header.width;
+ image_height = h = header.height;
+
+ size = w * h * 4;
+ data = static_cast(calloc (size, 1));
+
+ /* read the Targa file body and convert to portable format. */
+ pixel_size = header.pixel_size;
+ origin = (header.attributes & 0x20) >> 5;
+ interleave = (header.attributes & 0xC0) >> 6;
+ truerow = baserow = 0;
+ for (y=0 ; y> 2) << 3);
+ g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
+ b = (byte)((j & 0x1F) << 3);
+ a = 255;
+ break;
+
+ case 16: /* 5 bits each of red green and blue, 1 alpha bit. */
+ /* watch byte order. */
+ j = getc (fin);
+ k = getc (fin);
+ l = ((unsigned int)k << 8) + j;
+ r = (byte)(((k & 0x7C) >> 2) << 3);
+ g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
+ b = (byte)((j & 0x1F) << 3);
+ a = (k & 0x80) ? 255 : 0;
+ break;
+
+ case 24: /* 8 bits each of blue, green and red. */
+ b = getc (fin);
+ g = getc (fin);
+ r = getc (fin);
+ a = 255;
+ l = 0;
+ break;
+
+ case 32: /* 8 bits each of blue, green, red and alpha. */
+ b = getc (fin);
+ g = getc (fin);
+ r = getc (fin);
+ a = getc (fin);
+ l = 0;
+ break;
+
+ default:
+ Con_DPrintf ("Malformed TGA image: Illegal pixel_size '%d'\n", pixel_size);
+ fclose (fin);
+ free (data);
+ if (mapped)
+ free (ColorMap);
+ return NULL;
+ }
+
+PixEncode:
+ if (mapped)
+ {
+ map_idx = l * 4;
+ *dst++ = ColorMap[map_idx+0];
+ *dst++ = ColorMap[map_idx+1];
+ *dst++ = ColorMap[map_idx+2];
+ *dst++ = ColorMap[map_idx+3];
+ }
+ else
+ {
+ *dst++ = r;
+ *dst++ = g;
+ *dst++ = b;
+ *dst++ = a;
+ }
+ }
+
+ if (interleave == TGA_IL_Four)
+ truerow += 4;
+ else if (interleave == TGA_IL_Two)
+ truerow += 2;
+ else
+ truerow++;
+ if (truerow >= h)
+ truerow = ++baserow;
+ }
+
+ if (mapped)
+ free (ColorMap);
+
+ fclose (fin);
+
+ return data;
+}
+
+/*
+=========================================================
+
+ PNG
+
+=========================================================
+*/
+
+static void PNG_IO_user_read_data (png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ FILE *f = (FILE *)png_get_io_ptr(png_ptr);
+
+ fread (data, 1, length, f);
+}
+
+/*
+static void PNG_IO_user_write_data (png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ FILE *f = (FILE *)png_get_io_ptr(png_ptr);
+
+ fwrite (data, 1, length, f);
+}
+
+static void PNG_IO_user_flush_data (png_structp png_ptr)
+{
+ FILE *f = (FILE *)png_get_io_ptr(png_ptr);
+
+ fflush (f);
+}
+*/
+
+/*
+=============
+LoadPNG
+=============
+*/
+byte *LoadPNG (FILE *fin, int matchwidth, int matchheight)
+{
+ int y, width, height, bitdepth, colortype;
+ int interlace, compression, filter, bytesperpixel;
+ byte header[8], **rowpointers, *data;
+ png_structp png_ptr;
+ png_infop pnginfo;
+ unsigned long rowbytes;
+
+ fread (header, 1, 8, fin);
+
+ if (png_sig_cmp(header, 0, 8))
+ {
+ Con_DPrintf ("Invalid PNG image\n");
+ fclose (fin);
+ return NULL;
+ }
+
+ if (!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)))
+ {
+ fclose (fin);
+ return NULL;
+ }
+
+ if (!(pnginfo = png_create_info_struct(png_ptr)))
+ {
+ png_destroy_read_struct (&png_ptr, &pnginfo, NULL);
+ fclose (fin);
+ return NULL;
+ }
+
+ /* naievil -- invalid use of incomplete type
+ if (setjmp(png_ptr->jmpbuf))
+ {
+ png_destroy_read_struct (&png_ptr, &pnginfo, NULL);
+ fclose (fin);
+ return NULL;
+ }
+ */
+
+ png_set_read_fn (png_ptr, fin, PNG_IO_user_read_data);
+ png_set_sig_bytes (png_ptr, 8);
+ png_read_info (png_ptr, pnginfo);
+ png_get_IHDR (png_ptr, pnginfo, (png_uint_32 *)&width, (png_uint_32 *)&height, &bitdepth, &colortype, &interlace, &compression, &filter);
+
+ if (width > IMAGE_MAX_DIMENSIONS || height > IMAGE_MAX_DIMENSIONS)
+ {
+ Con_DPrintf ("PNG image exceeds maximum supported dimensions\n");
+ png_destroy_read_struct (&png_ptr, &pnginfo, NULL);
+ fclose (fin);
+ return NULL;
+ }
+
+ if ((matchwidth && width != matchwidth) || (matchheight && height != matchheight))
+ {
+ png_destroy_read_struct (&png_ptr, &pnginfo, NULL);
+ fclose (fin);
+ return NULL;
+ }
+
+ if (colortype == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_set_palette_to_rgb (png_ptr);
+ png_set_filler (png_ptr, 255, PNG_FILLER_AFTER);
+ }
+
+ if (colortype == PNG_COLOR_TYPE_GRAY && bitdepth < 8)
+ png_set_expand_gray_1_2_4_to_8 (png_ptr);
+
+ if (png_get_valid(png_ptr, pnginfo, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha (png_ptr);
+
+ if (colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb (png_ptr);
+
+ if (colortype != PNG_COLOR_TYPE_RGBA)
+ png_set_filler (png_ptr, 255, PNG_FILLER_AFTER);
+
+ if (bitdepth < 8)
+ png_set_expand (png_ptr);
+ else if (bitdepth == 16)
+ png_set_strip_16 (png_ptr);
+
+ png_read_update_info (png_ptr, pnginfo);
+ rowbytes = png_get_rowbytes (png_ptr, pnginfo);
+ bytesperpixel = png_get_channels (png_ptr, pnginfo);
+ bitdepth = png_get_bit_depth (png_ptr, pnginfo);
+
+ if (bitdepth != 8 || bytesperpixel != 4)
+ {
+ Con_DPrintf ("Unsupported PNG image: Bad color depth and/or bpp\n");
+ png_destroy_read_struct (&png_ptr, &pnginfo, NULL);
+ fclose (fin);
+ return NULL;
+ }
+
+ data = static_cast(malloc (height * rowbytes));
+ rowpointers = static_cast(malloc (height * sizeof(*rowpointers)));
+
+ for (y=0 ; y(malloc(numPixels * 4));
+
+ for ( row = rows-1; row >= 0; row-- )
+ {
+ pixbuf = bmpRGBA + row*columns*4;
+
+ for ( column = 0; column < columns; column++ )
+ {
+ unsigned char red, green, blue, alpha;
+ int palIndex;
+ unsigned short shortPixel;
+
+ switch ( bmpHeader.bitsPerPixel )
+ {
+ case 8:
+ palIndex = getc(fin);
+ *pixbuf++ = bmpHeader.palette[palIndex][2];
+ *pixbuf++ = bmpHeader.palette[palIndex][1];
+ *pixbuf++ = bmpHeader.palette[palIndex][0];
+ *pixbuf++ = 0xff;
+ break;
+ case 16:
+ shortPixel = * ( unsigned short * ) pixbuf;
+ pixbuf += 2;
+ *pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7;
+ *pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2;
+ *pixbuf++ = ( shortPixel & ( 31 ) ) << 3;
+ *pixbuf++ = 0xff;
+ break;
+
+ 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);
+ alpha = getc (fin);
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alpha;
+ break;
+ default:
+ Sys_Error("LoadBMP: illegal pixel_size '%d' in file\n", bmpHeader.bitsPerPixel);
+ break;
+ }
+ }
+ }
+
+ fclose(fin);
+
+ return bmpRGBA;
+}
+
+/*
+=============
+loadimagepixels
+=============
+*/
+byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight)
+{
+ FILE *f;
+ char basename[128], name[128];
+ byte *c;
+ COM_StripExtension(filename, basename); // strip the extension to allow TGA and PCX
+ c = (byte*)(basename);
+
+ while (*c)
+ {
+ if (*c == '*')
+ *c = '+';
+ c++;
+ }
+
+ com_netpath[0] = 0;
+/*
+ sprintf (name, "%s.wal", basename);
+ if (data = LoadWAL(name))
+ return data;
+*/
+
+ sprintf (name, "%s.tga", basename);
+ FS_FOpenFile (name, &f);
+ if (f)
+ return LoadTGA (f, matchwidth, matchheight);
+
+ sprintf (name, "%s.pcx", basename);
+ FS_FOpenFile (name, &f);
+ if (f)
+ return LoadPCX (f, matchwidth, matchheight);
+
+ sprintf (name, "%s.jpg", basename);
+ FS_FOpenFile (name, &f);
+ if (f)
+ {
+ return LoadJPG (f, matchwidth, matchheight);
+ }
+ sprintf (name, "%s.png", basename);
+ FS_FOpenFile (name, &f);
+ if (f)
+ return LoadPNG (f, matchwidth, matchheight);
+
+ sprintf (name, "%s.bmp", basename);
+ FS_FOpenFile (name, &f);
+ if (f)
+ return LoadBMP (f, matchwidth, matchheight);
+ if (complain)
+ Con_Printf ("Couldn't load %s .tga .jpg .bmp .png \n", filename);
+
+ return NULL;
+}
+
+/*
+=============
+loadtextureimage
+=============
+*/
+
+int loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, int filter)
+{
+ int texture_index;
+ byte *data;
+ data = loadimagepixels (filename, complain, matchwidth, matchheight);
+ if(!data)
+ {
+ return 0;
+ }
+
+ if(image_palette_type)
+ {
+ texture_index = GL_LoadPalTex (filename, image_width, image_height, data, qtrue, filter, 0, (byte *)image_palette, image_palette_type);
+ memset(image_palette, 0, sizeof(image_palette)); // clear temp pal
+ image_palette_type = 0;
+ }
+ else
+ {
+ texture_index = GL_LoadImages (filename, image_width, image_height, data, qtrue, filter, 0, 4);
+ }
+ free(data);
+ return texture_index;
+}
diff --git a/source/psp/video_hardware_images.h b/source/psp/video_hardware_images.h
new file mode 100644
index 0000000..367d253
--- /dev/null
+++ b/source/psp/video_hardware_images.h
@@ -0,0 +1,31 @@
+/*
+Copyright (C) 1996-2003 A Nourai, Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// image.h
+
+extern int image_width, image_height;
+
+extern cvar_t png_compression_level, jpeg_compression_level;
+
+int Image_WriteTGA (char *filename, byte *pixels, int width, int height);
+int Image_WritePNG (char *filename, int compression, byte *pixels, int width, int height);
+int Image_WritePNGPLTE (char *filename, int compression, byte *pixels, int width, int height, byte *palette);
+int Image_WriteJPEG (char *filename, int compression, byte *pixels, int width, int height);
+int Image_WritePCX (char *filename, byte *data, int width, int height, byte *palette);
+
diff --git a/source/psp/video_hardware_light.cpp b/source/psp/video_hardware_light.cpp
new file mode 100644
index 0000000..1091359
--- /dev/null
+++ b/source/psp/video_hardware_light.cpp
@@ -0,0 +1,357 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// r_light.c
+
+extern "C"
+{
+#include "../quakedef.h"
+}
+#include
+
+int r_dlightframecount;
+
+
+/*
+==================
+R_AnimateLight
+==================
+*/
+void R_AnimateLight (void)
+{
+ int i,j,k;
+
+//
+// light animations
+// 'm' is normal light, 'a' is no light, 'z' is double bright
+ i = (int)(cl.time*10);
+ for (j=0 ; jdie < cl.time || !l->radius)
+ continue;
+ R_RenderDlight (l);
+ }
+
+ glColor3f (1,1,1);
+ glDisable (GL_BLEND);
+ glEnable (GL_TEXTURE_2D);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDepthMask (1);*/
+/*
+ sceGuEnable(GU_LIGHTING);
+ sceGuEnable(GU_LIGHT0);
+ sceGuEnable(GU_LIGHT1);
+ sceGuEnable(GU_LIGHT2);
+ sceGuEnable(GU_LIGHT3);
+
+ l = cl_dlights;
+ for (i=0 ; idie < cl.time || !l->radius || light_num > 3)
+ continue;
+
+ ScePspFVector3 pos = { l->origin[0]*3.0, l->origin[1]*3.0, l->origin[2]*3.0};
+ sceGuLight(i, GU_POINTLIGHT, GU_DIFFUSE_AND_SPECULAR, &pos);
+ sceGuLightColor(i, GU_DIFFUSE, GU_COLOR(l->color[0], l->color[1], l->color[2], 1));
+ sceGuLightAtt(i,1.0f,0.0f,0.0f);
+ light_num++;
+ }
+
+ sceGuSpecular(12.0f);
+ sceGuAmbient(0x00222222);
+*/
+}
+
+
+/*
+=============================================================================
+
+DYNAMIC LIGHTS
+
+=============================================================================
+*/
+void R_MarkLights (dlight_t *light, int bit, mnode_t *node)
+{
+ mplane_t *splitplane;
+ float dist, l, maxdist;
+ msurface_t *surf;
+ int i, j, s, t, sidebit;
+ vec3_t impact;
+
+loc0:
+ if (node->contents < 0)
+ return;
+
+ splitplane = node->plane;
+// dist = PlaneDiff(light->origin, splitplane);
+
+ if (splitplane->type < 3)
+ dist = light->origin[splitplane->type] - splitplane->dist;
+ else
+ dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist;
+
+ if (dist > light->radius)
+ {
+ node = node->children[0];
+ goto loc0;
+ }
+ if (dist < -light->radius)
+ {
+ node = node->children[1];
+ goto loc0;
+ }
+
+ maxdist = light->radius * light->radius;
+// mark the polygons
+ surf = cl.worldmodel->surfaces + node->firstsurface;
+ for (i=0 ; inumsurfaces ; i++, surf++)
+ {
+ dist = DotProduct (light->origin, surf->plane->normal) - surf->plane->dist; // JT030305 - fix light bleed through
+ if (dist >= 0)
+ sidebit = 0;
+ else
+ sidebit = SURF_PLANEBACK;
+
+ if ( (surf->flags & SURF_PLANEBACK) != sidebit ) //Discoloda
+ continue; //Discoloda
+
+ for (j=0 ; j<3 ; j++)
+ impact[j] = light->origin[j] - surf->plane->normal[j]*dist;
+
+ // clamp center of light to corner and check brightness
+ l = DotProduct(impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
+ s = l + 0.5;
+ s = bound(0, s, surf->extents[0]);
+ s = l - s;
+ l = DotProduct(impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
+ t = l + 0.5;
+ t = bound(0, t, surf->extents[1]);
+ t = l - t;
+
+ // compare to minimum light
+ if ((s*s + t*t + dist*dist) < maxdist)
+ {
+ if (surf->dlightframe != r_dlightframecount) // not dynamic until now
+ {
+ surf->dlightbits = bit;
+ surf->dlightframe = r_dlightframecount;
+ }
+ else // already dynamic
+ {
+ surf->dlightbits |= bit;
+ }
+ }
+ }
+ if (node->children[0]->contents >= 0)
+ R_MarkLights (light, bit, node->children[0]);
+ if (node->children[1]->contents >= 0)
+ R_MarkLights (light, bit, node->children[1]);
+}
+
+/*
+=============
+R_PushDlights
+=============
+*/
+void R_PushDlights (void)
+{
+ int i;
+ dlight_t *l;
+
+ r_dlightframecount = r_framecount + 1; // because the count hasn't
+ // advanced yet for this frame
+ l = cl_dlights;
+
+ for (i=0 ; idie < cl.time || !l->radius)
+ continue;
+ R_MarkLights ( l, 1<nodes );
+ }
+}
+
+
+/*
+=============================================================================
+
+LIGHT SAMPLING
+
+=============================================================================
+*/
+
+mplane_t *lightplane;
+vec3_t lightspot;
+
+// LordHavoc: .lit support begin
+// LordHavoc: original code replaced entirely
+int RecursiveLightPoint (vec3_t color, mnode_t *node, vec3_t start, vec3_t end)
+{
+ float front, back, frac;
+ vec3_t mid;
+
+loc0:
+ if (node->contents < 0)
+ return false; // didn't hit anything
+
+// calculate mid point
+ if (node->plane->type < 3)
+ {
+ front = start[node->plane->type] - node->plane->dist;
+ back = end[node->plane->type] - node->plane->dist;
+ }
+ else
+ {
+ front = DotProduct(start, node->plane->normal) - node->plane->dist;
+ back = DotProduct(end, node->plane->normal) - node->plane->dist;
+ }
+
+ // LordHavoc: optimized recursion
+ if ((back < 0) == (front < 0))
+// return RecursiveLightPoint (color, node->children[front < 0], start, end);
+ {
+ node = node->children[front < 0];
+ goto loc0;
+ }
+
+ frac = front / (front-back);
+ mid[0] = start[0] + (end[0] - start[0])*frac;
+ mid[1] = start[1] + (end[1] - start[1])*frac;
+ mid[2] = start[2] + (end[2] - start[2])*frac;
+
+// go down front side
+ if (RecursiveLightPoint (color, node->children[front < 0], start, mid))
+ return true; // hit something
+ else
+ {
+ int i, ds, dt;
+ msurface_t *surf;
+ // check for impact on this node
+ VectorCopy (mid, lightspot);
+ lightplane = node->plane;
+
+ surf = cl.worldmodel->surfaces + node->firstsurface;
+ for (i = 0;i < node->numsurfaces;i++, surf++)
+ {
+ if (surf->flags & SURF_DRAWTILED)
+ continue; // no lightmaps
+
+ ds = (int) ((float) DotProduct (mid, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
+ dt = (int) ((float) DotProduct (mid, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
+
+ if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
+ continue;
+
+ ds -= surf->texturemins[0];
+ dt -= surf->texturemins[1];
+
+ if (ds > surf->extents[0] || dt > surf->extents[1])
+ continue;
+
+ if (surf->samples)
+ {
+ // LordHavoc: enhanced to interpolate lighting
+ byte *lightmap;
+ int maps, line3, dsfrac = ds & 15, dtfrac = dt & 15, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
+ float scale;
+ line3 = ((surf->extents[0]>>4)+1)*3;
+
+ lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
+
+ for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
+ {
+ scale = (float) d_lightstylevalue[surf->styles[maps]] * 1.0 / 256.0;
+ r00 += (float) lightmap[ 0] * scale;g00 += (float) lightmap[ 1] * scale;b00 += (float) lightmap[2] * scale;
+ r01 += (float) lightmap[ 3] * scale;g01 += (float) lightmap[ 4] * scale;b01 += (float) lightmap[5] * scale;
+ r10 += (float) lightmap[line3+0] * scale;g10 += (float) lightmap[line3+1] * scale;b10 += (float) lightmap[line3+2] * scale;
+ r11 += (float) lightmap[line3+3] * scale;g11 += (float) lightmap[line3+4] * scale;b11 += (float) lightmap[line3+5] * scale;
+ lightmap += ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
+ }
+
+ color[0] += (float) ((int) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)));
+ color[1] += (float) ((int) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)));
+ color[2] += (float) ((int) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)));
+ }
+ return true; // success
+ }
+
+ // go down back side
+ return RecursiveLightPoint (color, node->children[front >= 0], mid, end);
+ }
+}
+
+vec3_t lightcolor; // LordHavoc: used by model rendering
+int R_LightPoint (vec3_t p)
+{
+ vec3_t end;
+
+ if (r_fullbright.value || !cl.worldmodel->lightdata)
+ {
+ lightcolor[0] = lightcolor[1] = lightcolor[2] = 255;
+ return 255;
+ }
+
+ end[0] = p[0];
+ end[1] = p[1];
+ end[2] = p[2] - 2048;
+
+ lightcolor[0] = lightcolor[1] = lightcolor[2] = 0;
+ RecursiveLightPoint (lightcolor, cl.worldmodel->nodes, p, end);
+ return ((lightcolor[0] + lightcolor[1] + lightcolor[2]) * (1.0f / 3.0f));
+}
+// LordHavoc: .lit support end
diff --git a/source/psp/video_hardware_main.cpp b/source/psp/video_hardware_main.cpp
new file mode 100644
index 0000000..9dc7366
--- /dev/null
+++ b/source/psp/video_hardware_main.cpp
@@ -0,0 +1,4297 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2007 Peter Mackay and Chris Swindle.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// r_main.c
+
+extern "C"
+{
+#include "../quakedef.h"
+#include "iridlibs/perflib.h"
+float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);
+}
+
+//includes
+#include "video_hardware_hlmdl.h"
+#include
+#include
+
+#include "clipping.hpp"
+
+using namespace quake;
+
+//prototypes
+extern "C" void V_CalcBlend (void);
+
+void Fog_SetupFrame (void);
+void Fog_EnableGFog (void);
+void Fog_DisableGFog (void);
+void R_DrawDecals (void);
+void R_RenderDecals (void);
+void R_MarkLeaves (void);
+void QMB_LetItRain(void);
+void QMB_LaserSight (void);
+void VID_SetPaletteH2();
+void VID_SetPaletteTX();
+
+// globals
+const float piconst = GU_PI / 180.0f;
+
+entity_t r_worldentity;
+entity_t *currententity;
+
+qboolean r_cache_thrash; // compatability
+qboolean envmap;
+qboolean mirror;
+
+// view origin
+vec3_t vup;
+vec3_t vpn;
+vec3_t vright;
+vec3_t r_origin;
+
+vec3_t modelorg, r_entorigin;
+
+
+int r_visframecount; // bumped when going to a new PVS
+int r_framecount; // used for dlight push checking
+int c_brush_polys, c_alias_polys, c_md3_polys;
+int currenttexture = -1; // to avoid unnecessary texture sets
+int cnttextures[2] = {-1, -1}; // cached
+int particletexture; // little dot for particles
+int playertextures; // up to 16 color translated skins
+int mirrortexturenum; // quake texturenum, not gltexturenum
+int game_fps; // should probably move this somewhere less.. lame
+
+mplane_t *mirror_plane;
+mplane_t frustum[4];
+
+// screen size info
+refdef_t r_refdef;
+mleaf_t *r_viewleaf;
+mleaf_t *r_oldviewleaf;
+texture_t *r_notexture_mip;
+
+bool fixlight;
+bool alphafunc2;
+bool alphafunc;
+
+ScePspFMatrix4 r_world_matrix;
+ScePspFMatrix4 r_base_world_matrix;
+ScePspFMatrix4 md3mult;
+
+int d_lightstylevalue[256]; // 8.8 fraction of base light value
+
+cvar_t r_partalpha = {"r_partalpha", "0.8",qtrue};
+cvar_t r_norefresh = {"r_norefresh", "0"};
+cvar_t r_drawentities = {"r_drawentities", "1"};
+cvar_t r_drawviewmodel = {"r_drawviewmodel", "1"};
+cvar_t r_speeds = {"r_speeds", "0"};
+cvar_t r_fullbright = {"r_fullbright", "0"};
+cvar_t r_lightmap = {"r_lightmap", "0"};
+cvar_t r_shadows = {"r_shadows", "0"};
+cvar_t r_mirroralpha = {"r_mirroralpha", "1",qtrue};
+cvar_t r_wateralpha = {"r_wateralpha", "0.6",qtrue};
+cvar_t r_vsync = {"r_vsync", "0",qtrue};
+cvar_t r_farclip = {"r_farclip", "4096"}; //far cliping for q3 models
+cvar_t r_loadq3models = {"r_loadq3models", "0",qtrue}; //replace player model to q3 player
+cvar_t cl_loadmapcfg = {"cl_loadmapcfg", "0",qtrue}; //Load individual cfg for map
+cvar_t r_restexf = {"r_restexf", "0",qtrue}; //texture resampler setup
+cvar_t r_texcompr = {"r_texcompr", "5",qtrue}; //texture compression setup (default DXT5) warning DXT1 conflicted with switches palettes
+cvar_t r_maxrange = {"r_maxrange", "4096"}; //render distance
+cvar_t r_skydis = {"r_skydis", "2560",qtrue};
+cvar_t r_skyfog = {"r_skyfog", "1",qtrue};
+cvar_t r_caustics = {"r_caustics", "1",qtrue};
+cvar_t r_detail = {"r_detail", "1",qtrue};
+cvar_t r_detail_mipmaps = {"r_detail_mipmaps", "1",qtrue};
+cvar_t r_detail_mipmaps_func = {"r_detail_mipmaps_func", "2",qtrue};
+cvar_t r_detail_mipmaps_bias = {"r_detail_mipmaps_bias", "-6",qtrue};
+cvar_t r_asynch = {"r_asynch", "0"};
+cvar_t r_ipolations = {"r_ipolations", "0"};
+cvar_t r_i_model_animation = {"r_i_model_animation", "1",qtrue}; // Toggle smooth model animation
+cvar_t r_i_model_transform = {"r_i_model_transform", "1",qtrue}; // Toggle smooth model movement
+cvar_t r_mipmaps = {"r_mipmaps", "1",qtrue};
+cvar_t r_mipmaps_func = {"r_mipmaps_func", "2",qtrue}; // Adjust mip map calculations
+cvar_t r_mipmaps_bias = {"r_mipmaps_bias", "-7",qtrue}; // Adjust mip map bias
+cvar_t r_retro = {"r_retro", "0",qtrue}; // dr_mabuse1981: "retro filter".
+cvar_t r_dynamic = {"r_dynamic", "1"};
+cvar_t r_novis = {"r_novis", "0"};
+cvar_t r_tex_scale_down = {"r_tex_scale_down", "1",qtrue};
+cvar_t r_particles_simple = {"r_particles_simple", "0",qtrue};
+cvar_t gl_keeptjunctions = {"gl_keeptjunctions", "0"};
+cvar_t r_waterripple = {"r_waterripple", "2",qtrue};
+cvar_t r_waterwarp = {"r_waterwarp", "1",qtrue};
+cvar_t r_fastsky = {"r_fastsky", "1",qtrue};
+cvar_t r_skycolor = {"r_skycolor", "64 64 70",qtrue};
+cvar_t r_showbboxes = {"r_showbboxes", "0"};
+cvar_t r_showbboxes_full = {"r_showbboxes_full", "0",qtrue};
+cvar_t r_showtris = {"r_showtris", "0"};
+cvar_t r_showtris_full = {"r_showtris_full", "0",qtrue};
+cvar_t r_polyblend = {"r_polyblend", "1",qtrue};
+
+//QMB
+cvar_t r_explosiontype = {"r_explosiontype", "0",qtrue};
+cvar_t r_laserpoint = {"r_laserpoint", "0",qtrue};
+cvar_t r_part_explosions = {"r_part_explosions", "1",qtrue};
+cvar_t r_part_trails = {"r_part_trails", "1",qtrue};
+cvar_t r_part_sparks = {"r_part_sparks", "1",qtrue};
+cvar_t r_part_spikes = {"r_part_spikes", "1",qtrue};
+cvar_t r_part_gunshots = {"r_part_gunshots", "1",qtrue};
+cvar_t r_part_blood = {"r_part_blood", "1",qtrue};
+cvar_t r_part_telesplash = {"r_part_telesplash", "1",qtrue};
+cvar_t r_part_blobs = {"r_part_blobs", "1",qtrue};
+cvar_t r_part_lavasplash = {"r_part_lavasplash", "1",qtrue};
+cvar_t r_part_flames = {"r_part_flames", "1",qtrue};
+cvar_t r_part_lightning = {"r_part_lightning", "1",qtrue};
+cvar_t r_part_flies = {"r_part_flies", "1",qtrue};
+cvar_t r_part_muzzleflash = {"r_part_muzzleflash", "1",qtrue};
+cvar_t r_flametype = {"r_flametype", "2",qtrue};
+//Shpuld
+cvar_t r_model_brightness = { "r_model_brightness", "1", qtrue}; // Toggle high brightness model lighting
+
+//MotoLegacy
+cvar_t nzp_decals = {"nzp_decals", "1", qtrue};
+cvar_t nzp_particles = {"nzp_particles", "1", qtrue};
+
+extern cvar_t cl_maxfps;
+
+
+
+
+
+bool modelIsArm (char *m_name)
+{
+ if (!strcmp (m_name, "progs/ai/zal.mdl") ||
+ !strcmp (m_name, "progs/ai/zar.mdl") ||
+ !strcmp (m_name, "progs/ai/bzal.mdl") ||
+ !strcmp (m_name, "progs/ai/bzar.mdl") ||
+ !strcmp (m_name, "progs/ai/zalc.mdl") ||
+ !strcmp (m_name, "progs/ai/zarc.mdl"))
+ return true;
+
+ return false;
+
+}
+bool modelIsHead (char *m_name)
+{
+ if (!strcmp (m_name, "progs/ai/zh.mdl") ||
+ !strcmp (m_name, "progs/ai/bzh.mdl") ||
+ !strcmp (m_name, "progs/ai/zhc.mdl"))
+ return true;
+
+ return false;
+
+}
+bool modelIsBody (char *m_name)
+{
+ if (!strcmp (m_name, "progs/ai/zb.mdl") ||
+ !strcmp (m_name, "progs/ai/bzb.mdl") ||
+ !strcmp (m_name, "progs/ai/zbc.mdl"))
+ return true;
+
+ return false;
+
+}
+/*
+================
+ConvertMatrix
+By Crow_bar for MD3
+================
+*/
+void ConvertMatrix(float *a, float *b)
+{
+ for (int i = 0; i < 16; i++)
+ a[i] = b[i];
+}
+
+/*
+=============
+R_RotateForTagEntity
+=============
+*/
+void R_RotateForTagEntity (tagentity_t *tagent, md3tag_t *tag, float *m)
+{
+ int i;
+ float lerpfrac, timepassed;
+
+ // positional interpolation
+ timepassed = cl.time - tagent->tag_translate_start_time;
+
+ if (tagent->tag_translate_start_time == 0 || timepassed > 1)
+ {
+ tagent->tag_translate_start_time = cl.time;
+ VectorCopy (tag->pos, tagent->tag_pos1);
+ VectorCopy (tag->pos, tagent->tag_pos2);
+ }
+
+ if (!VectorCompare(tag->pos, tagent->tag_pos2))
+ {
+ tagent->tag_translate_start_time = cl.time;
+ VectorCopy (tagent->tag_pos2, tagent->tag_pos1);
+ VectorCopy (tag->pos, tagent->tag_pos2);
+ lerpfrac = 0;
+ }
+ else
+ {
+ lerpfrac = timepassed / 0.1;
+ if (cl.paused || lerpfrac > 1)
+ lerpfrac = 1;
+ }
+
+ VectorInterpolate (tagent->tag_pos1, lerpfrac, tagent->tag_pos2, m + 12);
+ m[15] = 1;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ // orientation interpolation (Euler angles, yuck!)
+ timepassed = cl.time - tagent->tag_rotate_start_time[i];
+
+ if (tagent->tag_rotate_start_time[i] == 0 || timepassed > 1)
+ {
+ tagent->tag_rotate_start_time[i] = cl.time;
+ VectorCopy (tag->rot[i], tagent->tag_rot1[i]);
+ VectorCopy (tag->rot[i], tagent->tag_rot2[i]);
+ }
+
+ if (!VectorCompare(tag->rot[i], tagent->tag_rot2[i]))
+ {
+ tagent->tag_rotate_start_time[i] = cl.time;
+ VectorCopy (tagent->tag_rot2[i], tagent->tag_rot1[i]);
+ VectorCopy (tag->rot[i], tagent->tag_rot2[i]);
+ lerpfrac = 0;
+ }
+ else
+ {
+ lerpfrac = timepassed / 0.1;
+ if (cl.paused || lerpfrac > 1)
+ lerpfrac = 1;
+ }
+
+ VectorInterpolate (tagent->tag_rot1[i], lerpfrac, tagent->tag_rot2[i], m + i*4);
+ m[i*4+3] = 0;
+ }
+}
+
+/*
+=============
+R_RotateForViewEntity
+=============
+*/
+void R_RotateForViewEntity (entity_t *ent)
+{
+ // Translate.
+ const ScePspFVector3 translation =
+ {
+ ent->origin[0], ent->origin[1], ent->origin[2]
+ };
+ sceGumTranslate(&translation);
+
+ // Rotate.
+ const ScePspFVector3 rotation =
+ {
+ ent->angles[ROLL] * piconst,
+ -ent->angles[PITCH] * piconst,
+ ent->angles[YAW] * piconst
+ };
+ sceGumRotateZYX(&rotation);
+}
+
+/*
+=================
+R_CullBox -- replaced with new function from lordhavoc
+
+Returns true if the box is completely outside the frustum
+=================
+*/
+qboolean R_CullBox (vec3_t emins, vec3_t emaxs)
+{
+ int i;
+ mplane_t *p;
+
+
+
+ for (i = 0;i < 4;i++)
+ {
+ p = frustum + i;
+ switch(p->signbits)
+ {
+ default:
+ case 0:
+ if (p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2] < p->dist)
+ return qtrue;
+ break;
+ case 1:
+ if (p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2] < p->dist)
+ return qtrue;
+ break;
+ case 2:
+ if (p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2] < p->dist)
+ return qtrue;
+ break;
+ case 3:
+ if (p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2] < p->dist)
+ return qtrue;
+ break;
+ case 4:
+ if (p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2] < p->dist)
+ return qtrue;
+ break;
+ case 5:
+ if (p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2] < p->dist)
+ return qtrue;
+ break;
+ case 6:
+ if (p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2] < p->dist)
+ return qtrue;
+ break;
+ case 7:
+ if (p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2] < p->dist)
+ return qtrue;
+ break;
+ }
+ }
+ return qfalse;
+}
+
+/*
+=================
+R_CullSphere
+
+Returns true if the sphere is completely outside the frustum
+=================
+*/
+qboolean R_CullSphere (vec3_t centre, float radius)
+{
+ int i;
+ mplane_t *p;
+
+ for (i=0, p=frustum ; i<4 ; i++, p++)
+ {
+ if (PlaneDiff(centre, p) <= -radius)
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+=============
+R_RotateForEntity
+=============
+*/
+void R_RotateForEntity (entity_t *e, int shadow)
+{
+ // Translate.
+ const ScePspFVector3 translation =
+ {
+ e->origin[0], e->origin[1], e->origin[2]
+ };
+ sceGumTranslate(&translation);
+/*
+ // Scale.
+ const ScePspFVector3 scale =
+ {
+ e->scale, e->scale, e->scale
+ };
+ sceGumScale(&scale);
+*/
+
+ // Rotate.
+ sceGumRotateZ(e->angles[YAW] * (GU_PI / 180.0f));
+ if (shadow == 0)
+ {
+ sceGumRotateY (-e->angles[PITCH] * (GU_PI / 180.0f));
+ sceGumRotateX (e->angles[ROLL] * (GU_PI / 180.0f));
+ }
+
+ sceGumUpdateMatrix();
+}
+
+/*
+=============
+R_InterpolateEntity
+
+was R_BlendedRotateForEntity
+fenix@io.com: model transform interpolation
+
+modified by blubswillrule
+//fixme (come back and fix this once we can test on psp and view the true issue with interpolation)
+=============
+*/
+
+void R_InterpolateEntity(entity_t *e, int shadow) // Tomaz - New Shadow
+{
+ float timepassed;
+ float blend;
+ vec3_t deltaVec;
+ int i;
+
+ // positional interpolation
+
+ timepassed = realtime - e->translate_start_time;
+
+ //notes to self (blubs)
+ //-Added this method, and commented out the check for r_i_model_transforms.value
+ //tried the snapping interpolation, though it worked, it was still a bit jittery...
+ //problem with linear interpolation is we don't know the exact time it should take to move from origin1 to origin2...
+ //looks like the rotation interpolation doesn't work all that great either, rotation could benefit from the snapping interpolation that I use
+ //if I get this method to work well, make sure we go back and check for r_i_model_transforms again, (because vmodel and other models that don't use interpolation)
+ //probably go back and edit animations too as I redo the last 2 textures..
+
+
+ /*if (e->translate_start_time == 0 || timepassed > 1)
+ {
+ e->translate_start_time = realtime;
+ VectorCopy (e->origin, e->origin1);
+ VectorCopy (e->origin, e->origin2);
+ }
+
+ //our origin has been updated
+ if (!VectorCompare (e->origin, e->origin2))
+ {
+ e->translate_start_time = realtime;
+ VectorCopy (e->origin2, e->origin1);
+ VectorCopy (e->origin, e->origin2);
+ blend = 0;
+ }
+ else
+ {
+ blend = 1;
+ if (cl.paused)
+ blend = 0;
+
+ e->origin1[0] += (blend * 0.25 * (e->origin2[0] - e->origin1[0]));
+ e->origin1[1] += (blend * 0.25 * (e->origin2[1] - e->origin1[1]));
+ e->origin1[2] += (blend * 0.25 * (e->origin2[2] - e->origin1[2]));
+ }
+
+ //VectorSubtract (e->origin2, e->origin1, deltaVec);
+
+ // Translate.
+ const ScePspFVector3 translation =
+ {*/
+ /*e->origin[0] + (blend * deltaVec[0]),
+ e->origin[1] + (blend * deltaVec[1]),
+ e->origin[2] + (blend * deltaVec[2])*/
+ /*
+ e->origin1[0], e->origin1[1], e->origin1[2]
+ };
+ */
+
+ if (e->translate_start_time == 0 || timepassed > 1)
+ {
+ e->translate_start_time = realtime;
+ VectorCopy (e->origin, e->origin1);
+ VectorCopy (e->origin, e->origin2);
+ }
+
+ //our origin has been updated
+ if (!VectorCompare (e->origin, e->origin2))
+ {
+ e->translate_start_time = realtime;
+ VectorCopy (e->origin2, e->origin1);
+ VectorCopy (e->origin, e->origin2);
+ blend = 0;
+ }
+ else
+ {
+ blend = timepassed / 0.4;//0.1 not sure what this value should be...
+ //technically this value should be the total amount of time that we take from 1 position to the next, it's practically how long it should take us to go from one location to the next...
+ if (cl.paused || blend > 1)
+ blend = 0;
+ }
+
+ VectorSubtract (e->origin2, e->origin1, deltaVec);
+
+ // Translate.
+ const ScePspFVector3 translation =
+ {
+ e->origin[0] + (blend * deltaVec[0]),
+ e->origin[1] + (blend * deltaVec[1]),
+ e->origin[2] + (blend * deltaVec[2])
+ };
+ sceGumTranslate(&translation);
+
+
+ // orientation interpolation (Euler angles, yuck!)
+ timepassed = realtime - e->rotate_start_time;
+
+ if (e->rotate_start_time == 0 || timepassed > 1)
+ {
+ e->rotate_start_time = realtime;
+ VectorCopy (e->angles, e->angles1);
+ VectorCopy (e->angles, e->angles2);
+ }
+
+ if (!VectorCompare (e->angles, e->angles2))
+ {
+ e->rotate_start_time = realtime;
+ VectorCopy (e->angles2, e->angles1);
+ VectorCopy (e->angles, e->angles2);
+ blend = 0;
+ }
+ else
+ {
+ blend = timepassed / 0.1;
+ if (cl.paused || blend > 1)
+ blend = 1;
+ }
+
+ VectorSubtract (e->angles2, e->angles1, deltaVec);
+
+ // always interpolate along the shortest path
+ for (i = 0; i < 3; i++)
+ {
+ if (deltaVec[i] > 180)
+ {
+ deltaVec[i] -= 360;
+ }
+ else if (deltaVec[i] < -180)
+ {
+ deltaVec[i] += 360;
+ }
+ }
+
+ // Rotate.
+ sceGumRotateZ((e->angles1[YAW] + ( blend * deltaVec[YAW])) * (GU_PI / 180.0f));
+ if (shadow == 0)
+ {
+ sceGumRotateY ((-e->angles1[PITCH] + (-blend * deltaVec[PITCH])) * (GU_PI / 180.0f));
+ sceGumRotateX ((e->angles1[ROLL] + ( blend * deltaVec[ROLL])) * (GU_PI / 180.0f));
+ }
+
+ sceGumUpdateMatrix();
+}
+
+
+
+/*
+=============
+R_BlendedRotateForEntity
+
+fenix@io.com: model transform interpolation
+=============
+*/
+void R_BlendedRotateForEntity (entity_t *e, int shadow) // Tomaz - New Shadow
+{
+ float timepassed;
+ float blend;
+ vec3_t d;
+ int i;
+
+ // positional interpolation
+
+ timepassed = realtime - e->translate_start_time;
+
+ if (e->translate_start_time == 0 || timepassed > 1)
+ {
+ e->translate_start_time = realtime;
+ VectorCopy (e->origin, e->origin1);
+ VectorCopy (e->origin, e->origin2);
+ }
+
+ if (!VectorCompare (e->origin, e->origin2))
+ {
+ e->translate_start_time = realtime;
+ VectorCopy (e->origin2, e->origin1);
+ VectorCopy (e->origin, e->origin2);
+ blend = 0;
+ }
+ else
+ {
+ blend = timepassed / 0.1;
+ if (cl.paused || blend > 1)
+ blend = 0;
+ }
+
+ VectorSubtract (e->origin2, e->origin1, d);
+
+ // Translate.
+ const ScePspFVector3 translation = {
+ e->origin[0] + (blend * d[0]),
+ e->origin[1] + (blend * d[1]),
+ e->origin[2] + (blend * d[2])
+ };
+ sceGumTranslate(&translation);
+/*
+ // Scale.
+ const ScePspFVector3 scale = {
+ e->scale + (blend * d[0]),
+ e->scale + (blend * d[1]),
+ e->scale + (blend * d[2]
+ };
+ sceGumScale(&scale);
+*/
+ // orientation interpolation (Euler angles, yuck!)
+ timepassed = realtime - e->rotate_start_time;
+
+ if (e->rotate_start_time == 0 || timepassed > 1)
+ {
+ e->rotate_start_time = realtime;
+ VectorCopy (e->angles, e->angles1);
+ VectorCopy (e->angles, e->angles2);
+ }
+
+ if (!VectorCompare (e->angles, e->angles2))
+ {
+ e->rotate_start_time = realtime;
+ VectorCopy (e->angles2, e->angles1);
+ VectorCopy (e->angles, e->angles2);
+ blend = 0;
+ }
+ else
+ {
+ blend = timepassed / 0.1;
+ if (cl.paused || blend > 1)
+ blend = 1;
+ }
+
+ VectorSubtract (e->angles2, e->angles1, d);
+
+ // always interpolate along the shortest path
+ for (i = 0; i < 3; i++)
+ {
+ if (d[i] > 180)
+ {
+ d[i] -= 360;
+ }
+ else if (d[i] < -180)
+ {
+ d[i] += 360;
+ }
+ }
+
+ // Rotate.
+ sceGumRotateZ((e->angles1[YAW] + ( blend * d[YAW])) * (GU_PI / 180.0f));
+ if (shadow == 0)
+ {
+ sceGumRotateY ((-e->angles1[PITCH] + (-blend * d[PITCH])) * (GU_PI / 180.0f));
+ sceGumRotateX ((e->angles1[ROLL] + ( blend * d[ROLL])) * (GU_PI / 180.0f));
+ }
+
+ sceGumUpdateMatrix();
+}
+
+/*
+=============================================================
+
+ SPRITE MODELS
+
+=============================================================
+*/
+
+extern vec3_t lightcolor; // LordHavoc: .lit support
+
+/*
+================
+R_GetSpriteFrame
+================
+*/
+mspriteframe_t *R_GetSpriteFrame (entity_t *currententity)
+{
+ msprite_t *psprite;
+ mspritegroup_t *pspritegroup;
+ mspriteframe_t *pspriteframe;
+ int i, numframes, frame;
+ float *pintervals, fullinterval, targettime, time;
+
+ psprite = static_cast(currententity->model->cache.data);
+ frame = currententity->frame;
+
+
+ if ((frame >= psprite->numframes) || (frame < 0))
+ {
+ Con_Printf ("R_DrawSprite: no such frame %d\n", frame);
+ frame = 0;
+ }
+
+ if (psprite->frames[frame].type == SPR_SINGLE)
+ {
+ pspriteframe = psprite->frames[frame].frameptr;
+ }
+ else
+ {
+ pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
+ pintervals = pspritegroup->intervals;
+ numframes = pspritegroup->numframes;
+ fullinterval = pintervals[numframes-1];
+
+ time = cl.time + currententity->syncbase;
+
+ // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
+ // are positive, so we don't have to worry about division by 0
+ targettime = time - ((int)(time / fullinterval)) * fullinterval;
+
+ for (i=0 ; i<(numframes-1) ; i++)
+ {
+ if (pintervals[i] > targettime)
+ break;
+ }
+
+ pspriteframe = pspritegroup->frames[i];
+ }
+
+ return pspriteframe;
+}
+
+/*
+=================
+R_DrawSpriteModel
+
+=================
+*/
+void R_DrawSpriteModel (entity_t *e)
+{
+ vec3_t point, v_forward, v_right, v_up;
+ msprite_t *psprite;
+ mspriteframe_t *frame;
+ float *s_up, *s_right;
+ float angle, sr, cr;
+ bool additive = false;
+ bool filter = false;
+
+ // don't even bother culling, because it's just a single polygon without a surface cache
+ frame = R_GetSpriteFrame (e);
+ psprite = static_cast(currententity->model->cache.data);
+
+ switch(psprite->type)
+ {
+ case SPR_VP_PARALLEL_UPRIGHT: //faces view plane, up is towards the heavens
+ v_up[0] = 0;
+ v_up[1] = 0;
+ v_up[2] = 1;
+ s_up = v_up;
+ s_right = vright;
+ break;
+ case SPR_FACING_UPRIGHT: //faces camera origin, up is towards the heavens
+ VectorSubtract(currententity->origin, r_origin, v_forward);
+ v_forward[2] = 0;
+ VectorNormalizeFast(v_forward);
+ v_right[0] = v_forward[1];
+ v_right[1] = -v_forward[0];
+ v_right[2] = 0;
+ v_up[0] = 0;
+ v_up[1] = 0;
+ v_up[2] = 1;
+ s_up = v_up;
+ s_right = v_right;
+ break;
+ case SPR_VP_PARALLEL: //faces view plane, up is towards the top of the screen
+ s_up = vup;
+ s_right = vright;
+ break;
+ case SPR_ORIENTED: //pitch yaw roll are independent of camera
+ AngleVectors (currententity->angles, v_forward, v_right, v_up);
+ s_up = v_up;
+ s_right = v_right;
+ break;
+ case SPR_VP_PARALLEL_ORIENTED: //faces view plane, but obeys roll value
+ angle = currententity->angles[ROLL] * M_PI_DIV_180;
+ #ifdef PSP_VFPU
+ sr = vfpu_sinf(angle);
+ cr = vfpu_cosf(angle);
+ #else
+ sr = sin(angle);
+ cr = cos(angle);
+ #endif
+ v_right[0] = vright[0] * cr + vup[0] * sr;
+ v_up[0] = vright[0] * -sr + vup[0] * cr;
+ v_right[1] = vright[1] * cr + vup[1] * sr;
+ v_up[1] = vright[1] * -sr + vup[1] * cr;
+ v_right[2] = vright[2] * cr + vup[2] * sr;
+ v_up[2] = vright[2] * -sr + vup[2] * cr;
+ s_up = v_up;
+ s_right = v_right;
+ break;
+ default:
+ return;
+ }
+
+ if (psprite->beamlength == 10) // we use the beam length of sprites, since they are unused by quake anyway.
+ additive = true;
+
+ if (psprite->beamlength == 20)
+ filter = true;
+
+ // Bind the texture.
+ GL_Bind(frame->gl_texturenum);
+
+ sceGuEnable(GU_BLEND);
+ sceGuDepthMask(GU_TRUE);
+
+ Fog_DisableGFog ();
+
+ if (additive)
+ {
+ sceGuDepthMask(GU_TRUE);
+ sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0xFFFFFFFF);
+ sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGB);
+ }
+ else if (filter)
+ {
+ sceGuDepthMask(GU_TRUE);
+ sceGuBlendFunc(GU_ADD, GU_FIX, GU_SRC_COLOR, 0, 0);
+ sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGB);
+ }
+ else
+ sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
+
+ // Allocate memory for this polygon.
+ glvert_t* const vertices =
+ static_cast