From df729933f89163ece17474de284518a10eabb592 Mon Sep 17 00:00:00 2001 From: Timothee 'TTimo' Besset Date: Sun, 7 Oct 2012 11:52:47 -0500 Subject: [PATCH 1/9] compile q3data on Linux --- SConscript.q3data | 19 + tools/quake3/bspc/.deps | 249 - tools/quake3/bspc/.gitignore | 5 - tools/quake3/bspc/Conscript | 75 - tools/quake3/bspc/LICENSE | 339 -- tools/quake3/bspc/Makefile | 109 - tools/quake3/bspc/README.md | 48 - tools/quake3/bspc/_files.c | 63 - tools/quake3/bspc/aas_areamerging.c | 390 -- tools/quake3/bspc/aas_areamerging.h | 24 - tools/quake3/bspc/aas_cfg.c | 254 - tools/quake3/bspc/aas_cfg.h | 77 - tools/quake3/bspc/aas_create.c | 1141 ----- tools/quake3/bspc/aas_create.h | 136 - tools/quake3/bspc/aas_edgemelting.c | 108 - tools/quake3/bspc/aas_edgemelting.h | 24 - tools/quake3/bspc/aas_facemerging.c | 282 - tools/quake3/bspc/aas_facemerging.h | 24 - tools/quake3/bspc/aas_file.c | 549 -- tools/quake3/bspc/aas_file.h | 25 - tools/quake3/bspc/aas_gsubdiv.c | 656 --- tools/quake3/bspc/aas_gsubdiv.h | 25 - tools/quake3/bspc/aas_map.c | 849 --- tools/quake3/bspc/aas_map.h | 23 - tools/quake3/bspc/aas_prunenodes.c | 89 - tools/quake3/bspc/aas_prunenodes.h | 24 - tools/quake3/bspc/aas_store.c | 1082 ---- tools/quake3/bspc/aas_store.h | 107 - tools/quake3/bspc/aasfile.h | 252 - tools/quake3/bspc/be_aas_bspc.c | 277 - tools/quake3/bspc/be_aas_bspc.h | 23 - tools/quake3/bspc/brushbsp.c | 1872 ------- tools/quake3/bspc/bspc.c | 1036 ---- tools/quake3/bspc/bspc.sln | 19 - tools/quake3/bspc/bspc.vcproj | 1771 ------- tools/quake3/bspc/cfgq3.c | 84 - tools/quake3/bspc/csg.c | 1005 ---- tools/quake3/bspc/deps/botlib/aasfile.h | 267 - tools/quake3/bspc/deps/botlib/be_aas.h | 221 - tools/quake3/bspc/deps/botlib/be_aas_bsp.h | 89 - tools/quake3/bspc/deps/botlib/be_aas_bspq3.c | 487 -- .../quake3/bspc/deps/botlib/be_aas_cluster.c | 1545 ------ .../quake3/bspc/deps/botlib/be_aas_cluster.h | 38 - tools/quake3/bspc/deps/botlib/be_aas_debug.c | 777 --- tools/quake3/bspc/deps/botlib/be_aas_debug.h | 62 - tools/quake3/bspc/deps/botlib/be_aas_def.h | 306 -- tools/quake3/bspc/deps/botlib/be_aas_entity.c | 437 -- tools/quake3/bspc/deps/botlib/be_aas_entity.h | 63 - tools/quake3/bspc/deps/botlib/be_aas_file.c | 582 --- tools/quake3/bspc/deps/botlib/be_aas_file.h | 42 - tools/quake3/bspc/deps/botlib/be_aas_funcs.h | 47 - tools/quake3/bspc/deps/botlib/be_aas_main.c | 429 -- tools/quake3/bspc/deps/botlib/be_aas_main.h | 61 - tools/quake3/bspc/deps/botlib/be_aas_move.c | 1101 ---- tools/quake3/bspc/deps/botlib/be_aas_move.h | 71 - .../quake3/bspc/deps/botlib/be_aas_optimize.c | 312 -- .../quake3/bspc/deps/botlib/be_aas_optimize.h | 33 - tools/quake3/bspc/deps/botlib/be_aas_reach.c | 4547 ----------------- tools/quake3/bspc/deps/botlib/be_aas_reach.h | 68 - tools/quake3/bspc/deps/botlib/be_aas_route.c | 2209 -------- tools/quake3/bspc/deps/botlib/be_aas_route.h | 67 - .../quake3/bspc/deps/botlib/be_aas_routealt.c | 240 - .../quake3/bspc/deps/botlib/be_aas_routealt.h | 40 - tools/quake3/bspc/deps/botlib/be_aas_sample.c | 1394 ----- tools/quake3/bspc/deps/botlib/be_aas_sample.h | 69 - tools/quake3/bspc/deps/botlib/be_ai_char.c | 790 --- tools/quake3/bspc/deps/botlib/be_ai_char.h | 48 - tools/quake3/bspc/deps/botlib/be_ai_chat.c | 3017 ----------- tools/quake3/bspc/deps/botlib/be_ai_chat.h | 113 - tools/quake3/bspc/deps/botlib/be_ai_gen.c | 134 - tools/quake3/bspc/deps/botlib/be_ai_gen.h | 33 - tools/quake3/bspc/deps/botlib/be_ai_goal.c | 1821 ------- tools/quake3/bspc/deps/botlib/be_ai_goal.h | 118 - tools/quake3/bspc/deps/botlib/be_ai_move.c | 3610 ------------- tools/quake3/bspc/deps/botlib/be_ai_move.h | 142 - tools/quake3/bspc/deps/botlib/be_ai_weap.c | 543 -- tools/quake3/bspc/deps/botlib/be_ai_weap.h | 104 - tools/quake3/bspc/deps/botlib/be_ai_weight.c | 912 ---- tools/quake3/bspc/deps/botlib/be_ai_weight.h | 83 - tools/quake3/bspc/deps/botlib/be_ea.c | 508 -- tools/quake3/bspc/deps/botlib/be_ea.h | 66 - tools/quake3/bspc/deps/botlib/be_interface.c | 881 ---- tools/quake3/bspc/deps/botlib/be_interface.h | 57 - tools/quake3/bspc/deps/botlib/botlib.h | 516 -- tools/quake3/bspc/deps/botlib/l_crc.c | 151 - tools/quake3/bspc/deps/botlib/l_crc.h | 29 - tools/quake3/bspc/deps/botlib/l_libvar.c | 294 -- tools/quake3/bspc/deps/botlib/l_libvar.h | 63 - tools/quake3/bspc/deps/botlib/l_log.c | 169 - tools/quake3/bspc/deps/botlib/l_log.h | 46 - tools/quake3/bspc/deps/botlib/l_memory.c | 463 -- tools/quake3/bspc/deps/botlib/l_memory.h | 76 - tools/quake3/bspc/deps/botlib/l_precomp.c | 3233 ------------ tools/quake3/bspc/deps/botlib/l_precomp.h | 180 - tools/quake3/bspc/deps/botlib/l_script.c | 1441 ------ tools/quake3/bspc/deps/botlib/l_script.h | 247 - tools/quake3/bspc/deps/botlib/l_struct.c | 462 -- tools/quake3/bspc/deps/botlib/l_struct.h | 76 - tools/quake3/bspc/deps/botlib/l_utils.h | 37 - tools/quake3/bspc/deps/botlib/lcc.mak | 55 - tools/quake3/bspc/deps/botlib/linux-i386.mak | 92 - tools/quake3/bspc/deps/qcommon/cm_load.c | 839 --- tools/quake3/bspc/deps/qcommon/cm_local.h | 196 - tools/quake3/bspc/deps/qcommon/cm_patch.c | 1773 ------- tools/quake3/bspc/deps/qcommon/cm_patch.h | 103 - tools/quake3/bspc/deps/qcommon/cm_polylib.c | 737 --- tools/quake3/bspc/deps/qcommon/cm_polylib.h | 68 - tools/quake3/bspc/deps/qcommon/cm_public.h | 76 - tools/quake3/bspc/deps/qcommon/cm_test.c | 478 -- tools/quake3/bspc/deps/qcommon/cm_trace.c | 1471 ------ tools/quake3/bspc/deps/qcommon/cmd.c | 715 --- tools/quake3/bspc/deps/qcommon/common.c | 3317 ------------ tools/quake3/bspc/deps/qcommon/cvar.c | 906 ---- tools/quake3/bspc/deps/qcommon/files.c | 3419 ------------- tools/quake3/bspc/deps/qcommon/huffman.c | 437 -- tools/quake3/bspc/deps/qcommon/ioapi.h | 75 - tools/quake3/bspc/deps/qcommon/md4.c | 303 -- tools/quake3/bspc/deps/qcommon/msg.c | 1757 ------- tools/quake3/bspc/deps/qcommon/net_chan.c | 742 --- tools/quake3/bspc/deps/qcommon/puff.h | 43 - tools/quake3/bspc/deps/qcommon/q_platform.h | 374 -- tools/quake3/bspc/deps/qcommon/q_shared.h | 1304 ----- tools/quake3/bspc/deps/qcommon/qcommon.h | 1190 ----- tools/quake3/bspc/deps/qcommon/qfiles.h | 581 --- tools/quake3/bspc/deps/qcommon/surfaceflags.h | 80 - tools/quake3/bspc/deps/qcommon/unzip.c | 4299 ---------------- tools/quake3/bspc/deps/qcommon/unzip.h | 338 -- tools/quake3/bspc/deps/qcommon/vm.c | 835 --- .../quake3/bspc/deps/qcommon/vm_interpreted.c | 889 ---- tools/quake3/bspc/deps/qcommon/vm_local.h | 190 - .../quake3/bspc/deps/qcommon/vm_powerpc_asm.h | 156 - tools/quake3/bspc/deps/qcommon/vm_ppc.c | 1479 ------ tools/quake3/bspc/deps/qcommon/vm_ppc_new.c | 2119 -------- tools/quake3/bspc/deps/qcommon/vm_sparc.h | 78 - tools/quake3/bspc/deps/qcommon/vm_x86.c | 1196 ----- tools/quake3/bspc/faces.c | 978 ---- tools/quake3/bspc/gldraw.c | 232 - tools/quake3/bspc/glfile.c | 149 - tools/quake3/bspc/l_bsp_ent.c | 180 - tools/quake3/bspc/l_bsp_ent.h | 58 - tools/quake3/bspc/l_bsp_hl.c | 893 ---- tools/quake3/bspc/l_bsp_hl.h | 314 -- tools/quake3/bspc/l_bsp_q1.c | 620 --- tools/quake3/bspc/l_bsp_q1.h | 275 - tools/quake3/bspc/l_bsp_q2.c | 1134 ---- tools/quake3/bspc/l_bsp_q2.h | 98 - tools/quake3/bspc/l_bsp_q3.c | 824 --- tools/quake3/bspc/l_bsp_q3.h | 81 - tools/quake3/bspc/l_bsp_sin.c | 1186 ----- tools/quake3/bspc/l_bsp_sin.h | 106 - tools/quake3/bspc/l_cmd.c | 1234 ----- tools/quake3/bspc/l_cmd.h | 158 - tools/quake3/bspc/l_log.c | 215 - tools/quake3/bspc/l_log.h | 42 - tools/quake3/bspc/l_math.c | 289 -- tools/quake3/bspc/l_math.h | 93 - tools/quake3/bspc/l_mem.c | 441 -- tools/quake3/bspc/l_mem.h | 51 - tools/quake3/bspc/l_poly.c | 1411 ----- tools/quake3/bspc/l_poly.h | 120 - tools/quake3/bspc/l_qfiles.c | 663 --- tools/quake3/bspc/l_qfiles.h | 91 - tools/quake3/bspc/l_threads.c | 1505 ------ tools/quake3/bspc/l_threads.h | 45 - tools/quake3/bspc/l_utils.c | 259 - tools/quake3/bspc/l_utils.h | 79 - tools/quake3/bspc/lcc.mak | 61 - tools/quake3/bspc/leakfile.c | 101 - tools/quake3/bspc/linux-i386.mak | 109 - tools/quake3/bspc/map.c | 1274 ----- tools/quake3/bspc/map_hl.c | 1114 ---- tools/quake3/bspc/map_q1.c | 1174 ----- tools/quake3/bspc/map_q2.c | 1162 ----- tools/quake3/bspc/map_q3.c | 680 --- tools/quake3/bspc/map_sin.c | 1211 ----- tools/quake3/bspc/nodraw.c | 47 - tools/quake3/bspc/portals.c | 1297 ----- tools/quake3/bspc/prtfile.c | 287 -- tools/quake3/bspc/q2files.h | 487 -- tools/quake3/bspc/q3files.h | 384 -- tools/quake3/bspc/qbsp.h | 477 -- tools/quake3/bspc/qfiles.h | 487 -- tools/quake3/bspc/sinfiles.h | 365 -- tools/quake3/bspc/tetrahedron.c | 1389 ----- tools/quake3/bspc/tetrahedron.h | 24 - tools/quake3/bspc/textures.c | 228 - tools/quake3/bspc/tree.c | 283 - tools/quake3/bspc/writebsp.c | 595 --- ...3data.vcproj.out-of-date => q3data.vcproj} | 0 189 files changed, 19 insertions(+), 108953 deletions(-) create mode 100644 SConscript.q3data delete mode 100644 tools/quake3/bspc/.deps delete mode 100644 tools/quake3/bspc/.gitignore delete mode 100644 tools/quake3/bspc/Conscript delete mode 100644 tools/quake3/bspc/LICENSE delete mode 100644 tools/quake3/bspc/Makefile delete mode 100644 tools/quake3/bspc/README.md delete mode 100644 tools/quake3/bspc/_files.c delete mode 100644 tools/quake3/bspc/aas_areamerging.c delete mode 100644 tools/quake3/bspc/aas_areamerging.h delete mode 100644 tools/quake3/bspc/aas_cfg.c delete mode 100644 tools/quake3/bspc/aas_cfg.h delete mode 100644 tools/quake3/bspc/aas_create.c delete mode 100644 tools/quake3/bspc/aas_create.h delete mode 100644 tools/quake3/bspc/aas_edgemelting.c delete mode 100644 tools/quake3/bspc/aas_edgemelting.h delete mode 100644 tools/quake3/bspc/aas_facemerging.c delete mode 100644 tools/quake3/bspc/aas_facemerging.h delete mode 100644 tools/quake3/bspc/aas_file.c delete mode 100644 tools/quake3/bspc/aas_file.h delete mode 100644 tools/quake3/bspc/aas_gsubdiv.c delete mode 100644 tools/quake3/bspc/aas_gsubdiv.h delete mode 100644 tools/quake3/bspc/aas_map.c delete mode 100644 tools/quake3/bspc/aas_map.h delete mode 100644 tools/quake3/bspc/aas_prunenodes.c delete mode 100644 tools/quake3/bspc/aas_prunenodes.h delete mode 100644 tools/quake3/bspc/aas_store.c delete mode 100644 tools/quake3/bspc/aas_store.h delete mode 100644 tools/quake3/bspc/aasfile.h delete mode 100644 tools/quake3/bspc/be_aas_bspc.c delete mode 100644 tools/quake3/bspc/be_aas_bspc.h delete mode 100644 tools/quake3/bspc/brushbsp.c delete mode 100644 tools/quake3/bspc/bspc.c delete mode 100644 tools/quake3/bspc/bspc.sln delete mode 100644 tools/quake3/bspc/bspc.vcproj delete mode 100644 tools/quake3/bspc/cfgq3.c delete mode 100644 tools/quake3/bspc/csg.c delete mode 100644 tools/quake3/bspc/deps/botlib/aasfile.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_bsp.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_bspq3.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_cluster.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_cluster.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_debug.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_debug.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_def.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_entity.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_entity.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_file.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_file.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_funcs.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_main.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_main.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_move.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_move.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_optimize.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_optimize.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_reach.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_reach.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_route.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_route.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_routealt.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_routealt.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_sample.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_aas_sample.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_char.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_char.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_chat.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_chat.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_gen.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_gen.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_goal.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_goal.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_move.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_move.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_weap.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_weap.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_weight.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_ai_weight.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_ea.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_ea.h delete mode 100644 tools/quake3/bspc/deps/botlib/be_interface.c delete mode 100644 tools/quake3/bspc/deps/botlib/be_interface.h delete mode 100644 tools/quake3/bspc/deps/botlib/botlib.h delete mode 100644 tools/quake3/bspc/deps/botlib/l_crc.c delete mode 100644 tools/quake3/bspc/deps/botlib/l_crc.h delete mode 100644 tools/quake3/bspc/deps/botlib/l_libvar.c delete mode 100644 tools/quake3/bspc/deps/botlib/l_libvar.h delete mode 100644 tools/quake3/bspc/deps/botlib/l_log.c delete mode 100644 tools/quake3/bspc/deps/botlib/l_log.h delete mode 100644 tools/quake3/bspc/deps/botlib/l_memory.c delete mode 100644 tools/quake3/bspc/deps/botlib/l_memory.h delete mode 100644 tools/quake3/bspc/deps/botlib/l_precomp.c delete mode 100644 tools/quake3/bspc/deps/botlib/l_precomp.h delete mode 100644 tools/quake3/bspc/deps/botlib/l_script.c delete mode 100644 tools/quake3/bspc/deps/botlib/l_script.h delete mode 100644 tools/quake3/bspc/deps/botlib/l_struct.c delete mode 100644 tools/quake3/bspc/deps/botlib/l_struct.h delete mode 100644 tools/quake3/bspc/deps/botlib/l_utils.h delete mode 100644 tools/quake3/bspc/deps/botlib/lcc.mak delete mode 100644 tools/quake3/bspc/deps/botlib/linux-i386.mak delete mode 100644 tools/quake3/bspc/deps/qcommon/cm_load.c delete mode 100644 tools/quake3/bspc/deps/qcommon/cm_local.h delete mode 100644 tools/quake3/bspc/deps/qcommon/cm_patch.c delete mode 100644 tools/quake3/bspc/deps/qcommon/cm_patch.h delete mode 100644 tools/quake3/bspc/deps/qcommon/cm_polylib.c delete mode 100644 tools/quake3/bspc/deps/qcommon/cm_polylib.h delete mode 100644 tools/quake3/bspc/deps/qcommon/cm_public.h delete mode 100644 tools/quake3/bspc/deps/qcommon/cm_test.c delete mode 100644 tools/quake3/bspc/deps/qcommon/cm_trace.c delete mode 100644 tools/quake3/bspc/deps/qcommon/cmd.c delete mode 100644 tools/quake3/bspc/deps/qcommon/common.c delete mode 100644 tools/quake3/bspc/deps/qcommon/cvar.c delete mode 100644 tools/quake3/bspc/deps/qcommon/files.c delete mode 100644 tools/quake3/bspc/deps/qcommon/huffman.c delete mode 100644 tools/quake3/bspc/deps/qcommon/ioapi.h delete mode 100644 tools/quake3/bspc/deps/qcommon/md4.c delete mode 100644 tools/quake3/bspc/deps/qcommon/msg.c delete mode 100644 tools/quake3/bspc/deps/qcommon/net_chan.c delete mode 100644 tools/quake3/bspc/deps/qcommon/puff.h delete mode 100644 tools/quake3/bspc/deps/qcommon/q_platform.h delete mode 100644 tools/quake3/bspc/deps/qcommon/q_shared.h delete mode 100644 tools/quake3/bspc/deps/qcommon/qcommon.h delete mode 100644 tools/quake3/bspc/deps/qcommon/qfiles.h delete mode 100644 tools/quake3/bspc/deps/qcommon/surfaceflags.h delete mode 100644 tools/quake3/bspc/deps/qcommon/unzip.c delete mode 100644 tools/quake3/bspc/deps/qcommon/unzip.h delete mode 100644 tools/quake3/bspc/deps/qcommon/vm.c delete mode 100644 tools/quake3/bspc/deps/qcommon/vm_interpreted.c delete mode 100644 tools/quake3/bspc/deps/qcommon/vm_local.h delete mode 100644 tools/quake3/bspc/deps/qcommon/vm_powerpc_asm.h delete mode 100644 tools/quake3/bspc/deps/qcommon/vm_ppc.c delete mode 100644 tools/quake3/bspc/deps/qcommon/vm_ppc_new.c delete mode 100644 tools/quake3/bspc/deps/qcommon/vm_sparc.h delete mode 100644 tools/quake3/bspc/deps/qcommon/vm_x86.c delete mode 100644 tools/quake3/bspc/faces.c delete mode 100644 tools/quake3/bspc/gldraw.c delete mode 100644 tools/quake3/bspc/glfile.c delete mode 100644 tools/quake3/bspc/l_bsp_ent.c delete mode 100644 tools/quake3/bspc/l_bsp_ent.h delete mode 100644 tools/quake3/bspc/l_bsp_hl.c delete mode 100644 tools/quake3/bspc/l_bsp_hl.h delete mode 100644 tools/quake3/bspc/l_bsp_q1.c delete mode 100644 tools/quake3/bspc/l_bsp_q1.h delete mode 100644 tools/quake3/bspc/l_bsp_q2.c delete mode 100644 tools/quake3/bspc/l_bsp_q2.h delete mode 100644 tools/quake3/bspc/l_bsp_q3.c delete mode 100644 tools/quake3/bspc/l_bsp_q3.h delete mode 100644 tools/quake3/bspc/l_bsp_sin.c delete mode 100644 tools/quake3/bspc/l_bsp_sin.h delete mode 100644 tools/quake3/bspc/l_cmd.c delete mode 100644 tools/quake3/bspc/l_cmd.h delete mode 100644 tools/quake3/bspc/l_log.c delete mode 100644 tools/quake3/bspc/l_log.h delete mode 100644 tools/quake3/bspc/l_math.c delete mode 100644 tools/quake3/bspc/l_math.h delete mode 100644 tools/quake3/bspc/l_mem.c delete mode 100644 tools/quake3/bspc/l_mem.h delete mode 100644 tools/quake3/bspc/l_poly.c delete mode 100644 tools/quake3/bspc/l_poly.h delete mode 100644 tools/quake3/bspc/l_qfiles.c delete mode 100644 tools/quake3/bspc/l_qfiles.h delete mode 100644 tools/quake3/bspc/l_threads.c delete mode 100644 tools/quake3/bspc/l_threads.h delete mode 100644 tools/quake3/bspc/l_utils.c delete mode 100644 tools/quake3/bspc/l_utils.h delete mode 100644 tools/quake3/bspc/lcc.mak delete mode 100644 tools/quake3/bspc/leakfile.c delete mode 100644 tools/quake3/bspc/linux-i386.mak delete mode 100644 tools/quake3/bspc/map.c delete mode 100644 tools/quake3/bspc/map_hl.c delete mode 100644 tools/quake3/bspc/map_q1.c delete mode 100644 tools/quake3/bspc/map_q2.c delete mode 100644 tools/quake3/bspc/map_q3.c delete mode 100644 tools/quake3/bspc/map_sin.c delete mode 100644 tools/quake3/bspc/nodraw.c delete mode 100644 tools/quake3/bspc/portals.c delete mode 100644 tools/quake3/bspc/prtfile.c delete mode 100644 tools/quake3/bspc/q2files.h delete mode 100644 tools/quake3/bspc/q3files.h delete mode 100644 tools/quake3/bspc/qbsp.h delete mode 100644 tools/quake3/bspc/qfiles.h delete mode 100644 tools/quake3/bspc/sinfiles.h delete mode 100644 tools/quake3/bspc/tetrahedron.c delete mode 100644 tools/quake3/bspc/tetrahedron.h delete mode 100644 tools/quake3/bspc/textures.c delete mode 100644 tools/quake3/bspc/tree.c delete mode 100644 tools/quake3/bspc/writebsp.c rename tools/quake3/q3data/{q3data.vcproj.out-of-date => q3data.vcproj} (100%) diff --git a/SConscript.q3data b/SConscript.q3data new file mode 100644 index 00000000..9d68a40b --- /dev/null +++ b/SConscript.q3data @@ -0,0 +1,19 @@ +# -*- mode: python -*- +# GtkRadiant build scripts +# TTimo +# http://scons.org/ + +import os + +Import( [ 'utils', 'config', 'settings', 'lib_objects' ] ) + +env = Environment() +settings.SetupEnvironment( env, config['name'] ) +env.Prepend( CPPPATH = [ '#tools/quake3/common' ] ) +env.Append( LIBS = [ 'm', 'pthread' ] ) +proj = utils.vcproj( os.path.join( GetLaunchDir(), 'tools/quake3/q3data/q3data.vcproj' ) ) +objects = lib_objects +objects += [ os.path.join( 'tools/quake3/q3data', i ) for i in proj.getSourceFiles() ] +q3data = env.Program( 'q3data', objects ) + +Return( 'q3data' ) diff --git a/tools/quake3/bspc/.deps b/tools/quake3/bspc/.deps deleted file mode 100644 index 61757050..00000000 --- a/tools/quake3/bspc/.deps +++ /dev/null @@ -1,249 +0,0 @@ -_files.o: _files.c -aas_areamerging.o: aas_areamerging.c qbsp.h l_cmd.h l_math.h l_poly.h \ - l_threads.h deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h \ - l_utils.h l_log.h l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h \ - aas_create.h aas_store.h deps/botlib/be_aas.h deps/botlib/be_aas_def.h -aas_cfg.o: aas_cfg.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h aas_store.h \ - deps/botlib/be_aas.h deps/botlib/be_aas_def.h aas_cfg.h \ - deps/botlib/l_precomp.h deps/botlib/l_struct.h deps/botlib/l_libvar.h -aas_create.o: aas_create.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h aas_create.h \ - aas_store.h deps/botlib/be_aas.h deps/botlib/be_aas_def.h aas_gsubdiv.h \ - aas_facemerging.h aas_areamerging.h aas_edgemelting.h aas_prunenodes.h \ - aas_cfg.h deps/qcommon/surfaceflags.h -aas_edgemelting.o: aas_edgemelting.c qbsp.h l_cmd.h l_math.h l_poly.h \ - l_threads.h deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h \ - l_utils.h l_log.h l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h \ - aas_create.h -aas_facemerging.o: aas_facemerging.c qbsp.h l_cmd.h l_math.h l_poly.h \ - l_threads.h deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h \ - l_utils.h l_log.h l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h \ - aas_create.h -aas_file.o: aas_file.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h aas_file.h \ - aas_store.h deps/botlib/be_aas.h deps/botlib/be_aas_def.h aas_create.h -aas_gsubdiv.o: aas_gsubdiv.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h aas_create.h \ - aas_store.h deps/botlib/be_aas.h deps/botlib/be_aas_def.h aas_cfg.h -aas_map.o: aas_map.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h aas_store.h \ - deps/botlib/be_aas.h deps/botlib/be_aas_def.h aas_cfg.h \ - deps/qcommon/surfaceflags.h -aas_prunenodes.o: aas_prunenodes.c qbsp.h l_cmd.h l_math.h l_poly.h \ - l_threads.h deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h \ - l_utils.h l_log.h l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h \ - aas_create.h -aas_store.o: aas_store.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h aas_file.h \ - aas_store.h deps/botlib/be_aas.h deps/botlib/be_aas_def.h aas_create.h \ - aas_cfg.h -be_aas_bspc.o: be_aas_bspc.c deps/qcommon/q_shared.h \ - deps/qcommon/q_platform.h deps/qcommon/surfaceflags.h l_log.h l_qfiles.h \ - deps/qcommon/unzip.h deps/botlib/l_memory.h deps/botlib/l_script.h \ - deps/botlib/l_precomp.h deps/botlib/l_struct.h deps/botlib/aasfile.h \ - deps/botlib/botlib.h deps/botlib/be_aas.h deps/botlib/be_aas_def.h \ - deps/botlib/be_aas_main.h deps/botlib/be_aas_entity.h \ - deps/botlib/be_aas_sample.h deps/botlib/be_aas_cluster.h \ - deps/botlib/be_aas_reach.h deps/botlib/be_aas_route.h \ - deps/botlib/be_aas_routealt.h deps/botlib/be_aas_debug.h \ - deps/botlib/be_aas_file.h deps/botlib/be_aas_optimize.h \ - deps/botlib/be_aas_bsp.h deps/botlib/be_aas_move.h \ - deps/qcommon/cm_public.h deps/qcommon/qfiles.h -be_aas_bspq3.o: deps/botlib/be_aas_bspq3.c deps/qcommon/q_shared.h \ - deps/qcommon/q_platform.h deps/qcommon/surfaceflags.h \ - deps/botlib/l_memory.h deps/botlib/l_script.h deps/botlib/l_precomp.h \ - deps/botlib/l_struct.h deps/botlib/aasfile.h deps/botlib/botlib.h \ - deps/botlib/be_aas.h deps/botlib/be_aas_funcs.h \ - deps/botlib/be_aas_main.h deps/botlib/be_aas_entity.h \ - deps/botlib/be_aas_sample.h deps/botlib/be_aas_cluster.h \ - deps/botlib/be_aas_reach.h deps/botlib/be_aas_route.h \ - deps/botlib/be_aas_routealt.h deps/botlib/be_aas_debug.h \ - deps/botlib/be_aas_file.h deps/botlib/be_aas_optimize.h \ - deps/botlib/be_aas_bsp.h deps/botlib/be_aas_move.h \ - deps/botlib/be_aas_def.h -be_aas_cluster.o: deps/botlib/be_aas_cluster.c deps/qcommon/q_shared.h \ - deps/qcommon/q_platform.h deps/qcommon/surfaceflags.h \ - deps/botlib/l_memory.h deps/botlib/l_script.h deps/botlib/l_precomp.h \ - deps/botlib/l_struct.h deps/botlib/l_log.h deps/botlib/l_libvar.h \ - deps/botlib/aasfile.h deps/botlib/botlib.h deps/botlib/be_aas.h \ - deps/botlib/be_aas_funcs.h deps/botlib/be_aas_main.h \ - deps/botlib/be_aas_entity.h deps/botlib/be_aas_sample.h \ - deps/botlib/be_aas_cluster.h deps/botlib/be_aas_reach.h \ - deps/botlib/be_aas_route.h deps/botlib/be_aas_routealt.h \ - deps/botlib/be_aas_debug.h deps/botlib/be_aas_file.h \ - deps/botlib/be_aas_optimize.h deps/botlib/be_aas_bsp.h \ - deps/botlib/be_aas_move.h deps/botlib/be_aas_def.h -be_aas_move.o: deps/botlib/be_aas_move.c deps/qcommon/q_shared.h \ - deps/qcommon/q_platform.h deps/qcommon/surfaceflags.h \ - deps/botlib/l_memory.h deps/botlib/l_script.h deps/botlib/l_precomp.h \ - deps/botlib/l_struct.h deps/botlib/l_libvar.h deps/botlib/aasfile.h \ - deps/botlib/botlib.h deps/botlib/be_aas.h deps/botlib/be_aas_funcs.h \ - deps/botlib/be_aas_main.h deps/botlib/be_aas_entity.h \ - deps/botlib/be_aas_sample.h deps/botlib/be_aas_cluster.h \ - deps/botlib/be_aas_reach.h deps/botlib/be_aas_route.h \ - deps/botlib/be_aas_routealt.h deps/botlib/be_aas_debug.h \ - deps/botlib/be_aas_file.h deps/botlib/be_aas_optimize.h \ - deps/botlib/be_aas_bsp.h deps/botlib/be_aas_move.h \ - deps/botlib/be_aas_def.h -be_aas_optimize.o: deps/botlib/be_aas_optimize.c deps/qcommon/q_shared.h \ - deps/qcommon/q_platform.h deps/qcommon/surfaceflags.h \ - deps/botlib/l_libvar.h deps/botlib/l_memory.h deps/botlib/l_script.h \ - deps/botlib/l_precomp.h deps/botlib/l_struct.h deps/botlib/aasfile.h \ - deps/botlib/botlib.h deps/botlib/be_aas.h deps/botlib/be_aas_funcs.h \ - deps/botlib/be_aas_main.h deps/botlib/be_aas_entity.h \ - deps/botlib/be_aas_sample.h deps/botlib/be_aas_cluster.h \ - deps/botlib/be_aas_reach.h deps/botlib/be_aas_route.h \ - deps/botlib/be_aas_routealt.h deps/botlib/be_aas_debug.h \ - deps/botlib/be_aas_file.h deps/botlib/be_aas_optimize.h \ - deps/botlib/be_aas_bsp.h deps/botlib/be_aas_move.h \ - deps/botlib/be_interface.h deps/botlib/be_aas_def.h -be_aas_reach.o: deps/botlib/be_aas_reach.c deps/qcommon/q_shared.h \ - deps/qcommon/q_platform.h deps/qcommon/surfaceflags.h \ - deps/botlib/l_log.h deps/botlib/l_memory.h deps/botlib/l_script.h \ - deps/botlib/l_libvar.h deps/botlib/l_precomp.h deps/botlib/l_struct.h \ - deps/botlib/aasfile.h deps/botlib/botlib.h deps/botlib/be_aas.h \ - deps/botlib/be_aas_funcs.h deps/botlib/be_aas_main.h \ - deps/botlib/be_aas_entity.h deps/botlib/be_aas_sample.h \ - deps/botlib/be_aas_cluster.h deps/botlib/be_aas_reach.h \ - deps/botlib/be_aas_route.h deps/botlib/be_aas_routealt.h \ - deps/botlib/be_aas_debug.h deps/botlib/be_aas_file.h \ - deps/botlib/be_aas_optimize.h deps/botlib/be_aas_bsp.h \ - deps/botlib/be_aas_move.h deps/botlib/be_aas_def.h -be_aas_sample.o: deps/botlib/be_aas_sample.c deps/qcommon/q_shared.h \ - deps/qcommon/q_platform.h deps/qcommon/surfaceflags.h \ - deps/botlib/l_memory.h deps/botlib/l_script.h deps/botlib/l_precomp.h \ - deps/botlib/l_struct.h deps/botlib/aasfile.h deps/botlib/botlib.h \ - deps/botlib/be_aas.h deps/botlib/be_interface.h \ - deps/botlib/be_aas_funcs.h deps/botlib/be_aas_main.h \ - deps/botlib/be_aas_entity.h deps/botlib/be_aas_sample.h \ - deps/botlib/be_aas_cluster.h deps/botlib/be_aas_reach.h \ - deps/botlib/be_aas_route.h deps/botlib/be_aas_routealt.h \ - deps/botlib/be_aas_debug.h deps/botlib/be_aas_file.h \ - deps/botlib/be_aas_optimize.h deps/botlib/be_aas_bsp.h \ - deps/botlib/be_aas_move.h deps/botlib/be_aas_def.h -brushbsp.o: brushbsp.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h aas_store.h \ - deps/botlib/be_aas.h deps/botlib/be_aas_def.h aas_cfg.h -bspc.o: bspc.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h \ - deps/botlib/be_aas_cluster.h deps/botlib/be_aas_optimize.h aas_create.h \ - aas_store.h deps/botlib/be_aas.h deps/botlib/be_aas_def.h aas_file.h \ - aas_cfg.h be_aas_bspc.h -cm_load.o: deps/qcommon/cm_load.c deps/qcommon/cm_local.h \ - deps/qcommon/q_shared.h deps/qcommon/q_platform.h \ - deps/qcommon/surfaceflags.h deps/qcommon/qcommon.h \ - deps/qcommon/cm_public.h deps/qcommon/qfiles.h deps/qcommon/cm_polylib.h \ - l_qfiles.h deps/qcommon/unzip.h -cm_patch.o: deps/qcommon/cm_patch.c deps/qcommon/cm_local.h \ - deps/qcommon/q_shared.h deps/qcommon/q_platform.h \ - deps/qcommon/surfaceflags.h deps/qcommon/qcommon.h \ - deps/qcommon/cm_public.h deps/qcommon/qfiles.h deps/qcommon/cm_polylib.h \ - deps/qcommon/cm_patch.h -cm_test.o: deps/qcommon/cm_test.c deps/qcommon/cm_local.h \ - deps/qcommon/q_shared.h deps/qcommon/q_platform.h \ - deps/qcommon/surfaceflags.h deps/qcommon/qcommon.h \ - deps/qcommon/cm_public.h deps/qcommon/qfiles.h deps/qcommon/cm_polylib.h -cm_trace.o: deps/qcommon/cm_trace.c deps/qcommon/cm_local.h \ - deps/qcommon/q_shared.h deps/qcommon/q_platform.h \ - deps/qcommon/surfaceflags.h deps/qcommon/qcommon.h \ - deps/qcommon/cm_public.h deps/qcommon/qfiles.h deps/qcommon/cm_polylib.h -csg.o: csg.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h -glfile.o: glfile.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h -l_bsp_ent.o: l_bsp_ent.c l_cmd.h l_math.h l_mem.h l_log.h \ - deps/botlib/l_script.h l_bsp_ent.h -l_bsp_hl.o: l_bsp_hl.c l_cmd.h l_math.h l_mem.h l_log.h \ - deps/botlib/l_script.h l_bsp_hl.h l_bsp_ent.h -l_bsp_q1.o: l_bsp_q1.c l_cmd.h l_math.h l_mem.h l_log.h \ - deps/botlib/l_script.h l_bsp_q1.h l_bsp_ent.h -l_bsp_q2.o: l_bsp_q2.c l_cmd.h l_math.h l_mem.h l_log.h l_poly.h \ - deps/botlib/l_script.h q2files.h l_bsp_q2.h l_bsp_ent.h -l_bsp_q3.o: l_bsp_q3.c l_cmd.h l_math.h l_mem.h l_log.h l_poly.h \ - deps/botlib/l_script.h l_qfiles.h deps/qcommon/unzip.h l_bsp_q3.h \ - q3files.h l_bsp_ent.h -l_bsp_sin.o: l_bsp_sin.c l_cmd.h l_math.h l_mem.h l_log.h l_poly.h \ - deps/botlib/l_script.h l_bsp_ent.h l_bsp_sin.h sinfiles.h -l_cmd.o: l_cmd.c l_cmd.h l_log.h l_mem.h -l_libvar.o: deps/botlib/l_libvar.c deps/qcommon/q_shared.h \ - deps/qcommon/q_platform.h deps/qcommon/surfaceflags.h \ - deps/botlib/l_memory.h deps/botlib/l_libvar.h -l_log.o: l_log.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h -l_math.o: l_math.c l_cmd.h l_math.h -l_mem.o: l_mem.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h -l_poly.o: l_poly.c l_cmd.h l_math.h l_poly.h l_log.h l_mem.h -l_precomp.o: deps/botlib/l_precomp.c qbsp.h l_cmd.h l_math.h l_poly.h \ - l_threads.h deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h \ - l_utils.h l_log.h l_qfiles.h deps/qcommon/unzip.h deps/botlib/l_log.h \ - l_mem.h deps/botlib/l_precomp.h -l_qfiles.o: l_qfiles.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h -l_script.o: deps/botlib/l_script.c qbsp.h l_cmd.h l_math.h l_poly.h \ - l_threads.h deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h \ - l_utils.h l_log.h l_qfiles.h deps/qcommon/unzip.h deps/botlib/l_log.h \ - l_mem.h -l_struct.o: deps/botlib/l_struct.c qbsp.h l_cmd.h l_math.h l_poly.h \ - l_threads.h deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h \ - l_utils.h l_log.h l_qfiles.h deps/qcommon/unzip.h deps/botlib/l_log.h \ - l_mem.h deps/botlib/l_precomp.h deps/botlib/l_struct.h -l_threads.o: l_threads.c l_cmd.h l_threads.h l_log.h l_mem.h -l_utils.o: l_utils.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h -leakfile.o: leakfile.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h -map.o: map.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h l_bsp_hl.h l_bsp_q1.h l_bsp_q2.h \ - l_bsp_q3.h q3files.h l_bsp_sin.h sinfiles.h deps/botlib/aasfile.h \ - aas_store.h deps/botlib/be_aas.h deps/botlib/be_aas_def.h aas_cfg.h -map_hl.o: map_hl.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h l_bsp_hl.h aas_map.h -map_q1.o: map_q1.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h l_bsp_q1.h aas_map.h -map_q2.o: map_q2.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h aas_store.h \ - deps/botlib/be_aas.h deps/botlib/be_aas_def.h aas_cfg.h aas_map.h \ - l_bsp_q2.h -map_q3.o: map_q3.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h deps/botlib/aasfile.h aas_store.h \ - deps/botlib/be_aas.h deps/botlib/be_aas_def.h aas_cfg.h aas_map.h \ - l_bsp_q3.h q3files.h deps/qcommon/cm_patch.h deps/qcommon/surfaceflags.h -map_sin.o: map_sin.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h l_bsp_sin.h sinfiles.h aas_map.h -md4.o: deps/qcommon/md4.c -nodraw.o: nodraw.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h -portals.o: portals.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h -textures.o: textures.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h l_bsp_q2.h -tree.o: tree.c qbsp.h l_cmd.h l_math.h l_poly.h l_threads.h \ - deps/botlib/l_script.h l_bsp_ent.h q2files.h l_mem.h l_utils.h l_log.h \ - l_qfiles.h deps/qcommon/unzip.h -unzip.o: deps/qcommon/unzip.c deps/qcommon/unzip.h diff --git a/tools/quake3/bspc/.gitignore b/tools/quake3/bspc/.gitignore deleted file mode 100644 index 8c19c9d9..00000000 --- a/tools/quake3/bspc/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -bspc_g -bspc -*.log -*.o -*.user \ No newline at end of file diff --git a/tools/quake3/bspc/Conscript b/tools/quake3/bspc/Conscript deleted file mode 100644 index 1f23dd1a..00000000 --- a/tools/quake3/bspc/Conscript +++ /dev/null @@ -1,75 +0,0 @@ -# bspc compile - -Import qw( BSPC_BASE_CFLAGS BUILD_DIR INSTALL_DIR CC CXX LINK ); - -@BSPC_FILES = qw( - aas_areamerging.c - aas_cfg.c - aas_create.c - aas_edgemelting.c - aas_facemerging.c - aas_file.c - aas_gsubdiv.c - aas_map.c - aas_prunenodes.c - aas_store.c - be_aas_bspc.c - deps/botlib/be_aas_bspq3.c - deps/botlib/be_aas_cluster.c - deps/botlib/be_aas_move.c - deps/botlib/be_aas_optimize.c - deps/botlib/be_aas_reach.c - deps/botlib/be_aas_sample.c - brushbsp.c - bspc.c - deps/qcommon/cm_load.c - deps/qcommon/cm_patch.c - deps/qcommon/cm_test.c - deps/qcommon/cm_trace.c - csg.c - glfile.c - l_bsp_ent.c - l_bsp_hl.c - l_bsp_q1.c - l_bsp_q2.c - l_bsp_q3.c - l_bsp_sin.c - l_cmd.c - deps/botlib/l_libvar.c - l_log.c - l_math.c - l_mem.c - l_poly.c - deps/botlib/l_precomp.c - l_qfiles.c - deps/botlib/l_script.c - deps/botlib/l_struct.c - l_threads.c - l_utils.c - leakfile.c - map.c - map_hl.c - map_q1.c - map_q2.c - map_q3.c - map_sin.c - deps/qcommon/md4.c - nodraw.c - portals.c - textures.c - tree.c - deps/qcommon/unzip.c - ); -$BSPC_REF = \@BSPC_FILES; - -$env = new cons( - CC => $CC, - CXX => $CXX, - LINK => $LINK, - CFLAGS => $BSPC_BASE_CFLAGS, - LIBS => '-ldl -lm -lpthread' -); - -Program $env 'bspc', @$BSPC_REF; -# this should install to Q3 or something? -Install $env $INSTALL_DIR, 'bspc'; diff --git a/tools/quake3/bspc/LICENSE b/tools/quake3/bspc/LICENSE deleted file mode 100644 index d159169d..00000000 --- a/tools/quake3/bspc/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/tools/quake3/bspc/Makefile b/tools/quake3/bspc/Makefile deleted file mode 100644 index 0733aaa4..00000000 --- a/tools/quake3/bspc/Makefile +++ /dev/null @@ -1,109 +0,0 @@ -CC=gcc -CFLAGS=\ - -Dstricmp=strcasecmp -DCom_Memcpy=memcpy -DCom_Memset=memset \ - -DMAC_STATIC= -DQDECL= -DLINUX -DBSPC -D_FORTIFY_SOURCE=2 \ - -I. -Ideps -Wall - -RELEASE_CFLAGS=-O3 -ffast-math -DEBUG_CFLAGS=-g -O0 -ffast-math -LDFLAGS=-lm -lpthread - -DO_CC=$(CC) $(CFLAGS) -o $@ -c $< - -############################################################################# -# SETUP AND BUILD BSPC -############################################################################# - -.c.o: - $(DO_CC) - -GAME_OBJS = \ - _files.o\ - aas_areamerging.o\ - aas_cfg.o\ - aas_create.o\ - aas_edgemelting.o\ - aas_facemerging.o\ - aas_file.o\ - aas_gsubdiv.o\ - aas_map.o\ - aas_prunenodes.o\ - aas_store.o\ - be_aas_bspc.o\ - deps/botlib/be_aas_bspq3.o\ - deps/botlib/be_aas_cluster.o\ - deps/botlib/be_aas_move.o\ - deps/botlib/be_aas_optimize.o\ - deps/botlib/be_aas_reach.o\ - deps/botlib/be_aas_sample.o\ - brushbsp.o\ - bspc.o\ - deps/qcommon/cm_load.o\ - deps/qcommon/cm_patch.o\ - deps/qcommon/cm_test.o\ - deps/qcommon/cm_trace.o\ - csg.o\ - glfile.o\ - l_bsp_ent.o\ - l_bsp_hl.o\ - l_bsp_q1.o\ - l_bsp_q2.o\ - l_bsp_q3.o\ - l_bsp_sin.o\ - l_cmd.o\ - deps/botlib/l_libvar.o\ - l_log.o\ - l_math.o\ - l_mem.o\ - l_poly.o\ - deps/botlib/l_precomp.o\ - l_qfiles.o\ - deps/botlib/l_script.o\ - deps/botlib/l_struct.o\ - l_threads.o\ - l_utils.o\ - leakfile.o\ - map.o\ - map_hl.o\ - map_q1.o\ - map_q2.o\ - map_q3.o\ - map_sin.o\ - deps/qcommon/md4.o\ - nodraw.o\ - portals.o\ - textures.o\ - tree.o\ - deps/qcommon/unzip.o - - #tetrahedron.o - -EXEC = bspc - -all: release - -debug: CFLAGS += $(DEBUG_CFLAGS) -debug: $(EXEC)_g - -release: CFLAGS += $(RELEASE_CFLAGS) -release: $(EXEC) - -$(EXEC): $(GAME_OBJS) - $(CC) $(LDFLAGS) -o $@ $(GAME_OBJS) - strip $@ - -$(EXEC)_g: $(GAME_OBJS) - $(CC) $(LDFLAGS) -o $@ $(GAME_OBJS) - -############################################################################# -# MISC -############################################################################# -.PHONY: clean depend - -clean: - -rm -f $(GAME_OBJS) $(EXEC) $(EXEC)_g - -depend: - $(CC) $(CFLAGS) -MM $(GAME_OBJS:.o=.c) > .deps - -include .deps diff --git a/tools/quake3/bspc/README.md b/tools/quake3/bspc/README.md deleted file mode 100644 index afb24c4f..00000000 --- a/tools/quake3/bspc/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# bspc - -This is the [Quake III: Arena](http://www.idsoftware.com/games/quake/quake3-arena/) BSP-to-AAS compiler. - -## Downloading - -You can download the latest version [here](https://github.com/bnoordhuis/bspc). - -## Compiling - -Dead simple: - - make - -## Usage - -Straight from the source: - - Usage: bspc [- [- ...]] - Example 1: bspc -bsp2aas /quake3/baseq3/maps/mymap?.bsp - Example 2: bspc -bsp2aas /quake3/baseq3/pak0.pk3/maps/q3dm*.bsp - - Switches: - bsp2aas <[pakfilter/]filter.bsp> = convert BSP to AAS - reach = compute reachability & clusters - cluster = compute clusters - aasopt = optimize aas file - aasinfo = show AAS file info - output = set output path - threads = set number of threads to X - cfg = use this cfg file - optimize = enable optimization - noverbose = disable verbose output - breadthfirst = breadth first bsp building - nobrushmerge = don't merge brushes - noliquids = don't write liquids to map - freetree = free the bsp tree - nocsg = disables brush chopping - forcesidesvisible = force all sides to be visible - grapplereach = calculate grapple reachabilities - -## Support - -[File a bug report](https://github.com/bnoordhuis/bspc/issues) if you run into issues. - -## License - -This program is licensed under the GNU Public License v2.0 and any later version. diff --git a/tools/quake3/bspc/_files.c b/tools/quake3/bspc/_files.c deleted file mode 100644 index 8f4fcffc..00000000 --- a/tools/quake3/bspc/_files.c +++ /dev/null @@ -1,63 +0,0 @@ -//=========================================================================== -// -// Name: _files.c -// Function: -// Programmer: Mr Elusive -// Last update: 1999-12-02 -// Tab Size: 4 -//=========================================================================== - -/* - -aas_areamerging.c //AAS area merging -aas_cfg.c //AAS configuration for different games -aas_create.c //AAS creating -aas_edgemelting.c //AAS edge melting -aas_facemerging.c //AAS face merging -aas_file.c //AAS file writing -aas_gsubdiv.c //AAS gravitational and ladder subdivision -aas_map.c //AAS map brush creation -aas_prunenodes.c //AAS node pruning -aas_store.c //AAS file storing - -map.c //map file loading and writing -map_hl.c //Half-Life map loading -map_q1.c //Quake1 map loading -map_q2.c //Quake2 map loading -map_q3.c //Quake3 map loading -map_sin.c //Sin map loading -tree.c //BSP tree management + node pruning (*) -brushbsp.c //brush bsp creation (*) -portals.c //BSP portal creation and leaf filling (*) -csg.c //Constructive Solid Geometry brush chopping (*) -leakfile.c //leak file writing (*) -textures.c //Quake2 BSP textures (*) - -l_bsp_ent.c //BSP entity parsing -l_bsp_hl.c //Half-Life BSP loading and writing -l_bsp_q1.c //Quake1 BSP loading and writing -l_bsp_q2.c //Quake2 BSP loading and writing -l_bsp_q3.c //Quake2 BSP loading and writing -l_bsp_sin.c //Sin BSP loading and writing -l_cmd.c //cmd library -l_log.c //log file library -l_math.c //math library -l_mem.c //memory management library -l_poly.c //polygon (winding) library -l_script.c //script file parsing library -l_threads.c //multi-threading library -l_utils.c //utility library -l_qfiles.c //loading of quake files - -gldraw.c //GL drawing (*) -glfile.c //GL file writing (*) -nodraw.c //no draw module (*) - -bspc.c //BSPC Win32 console version -winbspc.c //WinBSPC Win32 GUI version -win32_terminal.c //Win32 terminal output -win32_qfiles.c //Win32 game file management (also .pak .sin) -win32_font.c //Win32 fonts -win32_folder.c //Win32 folder dialogs - -*/ diff --git a/tools/quake3/bspc/aas_areamerging.c b/tools/quake3/bspc/aas_areamerging.c deleted file mode 100644 index 04055cd4..00000000 --- a/tools/quake3/bspc/aas_areamerging.c +++ /dev/null @@ -1,390 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "botlib/aasfile.h" -#include "aas_create.h" -#include "aas_store.h" - -#define CONVEX_EPSILON 0.3 - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_node_t *AAS_RefreshMergedTree_r(tmp_node_t *tmpnode) -{ - tmp_area_t *tmparea; - - //if this is a solid leaf - if (!tmpnode) return NULL; - //if this is an area leaf - if (tmpnode->tmparea) - { - tmparea = tmpnode->tmparea; - while(tmparea->mergedarea) tmparea = tmparea->mergedarea; - tmpnode->tmparea = tmparea; - return tmpnode; - } //end if - //do the children recursively - tmpnode->children[0] = AAS_RefreshMergedTree_r(tmpnode->children[0]); - tmpnode->children[1] = AAS_RefreshMergedTree_r(tmpnode->children[1]); - return tmpnode; -} //end of the function AAS_RefreshMergedTree_r -//=========================================================================== -// returns true if the two given faces would create a non-convex area at -// the given sides, otherwise false is returned -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int NonConvex(tmp_face_t *face1, tmp_face_t *face2, int side1, int side2) -{ - int i; - winding_t *w1, *w2; - plane_t *plane1, *plane2; - - w1 = face1->winding; - w2 = face2->winding; - - plane1 = &mapplanes[face1->planenum ^ side1]; - plane2 = &mapplanes[face2->planenum ^ side2]; - - //check if one of the points of face1 is at the back of the plane of face2 - for (i = 0; i < w1->numpoints; i++) - { - if (DotProduct(plane2->normal, w1->p[i]) - plane2->dist < -CONVEX_EPSILON) return true; - } //end for - //check if one of the points of face2 is at the back of the plane of face1 - for (i = 0; i < w2->numpoints; i++) - { - if (DotProduct(plane1->normal, w2->p[i]) - plane1->dist < -CONVEX_EPSILON) return true; - } //end for - - return false; -} //end of the function NonConvex -//=========================================================================== -// try to merge the areas at both sides of the given face -// -// Parameter: seperatingface : face that seperates two areas -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_TryMergeFaceAreas(tmp_face_t *seperatingface) -{ - int side1, side2, area1faceflags, area2faceflags; - tmp_area_t *tmparea1, *tmparea2, *newarea; - tmp_face_t *face1, *face2, *nextface1, *nextface2; - - tmparea1 = seperatingface->frontarea; - tmparea2 = seperatingface->backarea; - - //areas must have the same presence type - if (tmparea1->presencetype != tmparea2->presencetype) return false; - //areas must have the same area contents - if (tmparea1->contents != tmparea2->contents) return false; - //areas must have the same bsp model inside (or both none) - if (tmparea1->modelnum != tmparea2->modelnum) return false; - - area1faceflags = 0; - area2faceflags = 0; - for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side1]) - { - side1 = (face1->frontarea != tmparea1); - //debug: check if the area belongs to the area - if (face1->frontarea != tmparea1 && - face1->backarea != tmparea1) Error("face does not belong to area1"); - //just continue if the face is seperating the two areas - //NOTE: a result of this is that ground and gap areas can - // be merged if the seperating face is the gap - if ((face1->frontarea == tmparea1 && - face1->backarea == tmparea2) || - (face1->frontarea == tmparea2 && - face1->backarea == tmparea1)) continue; - //get area1 face flags - area1faceflags |= face1->faceflags; - if (AAS_GapFace(face1, side1)) area1faceflags |= FACE_GAP; - // - for (face2 = tmparea2->tmpfaces; face2; face2 = face2->next[side2]) - { - side2 = (face2->frontarea != tmparea2); - //debug: check if the area belongs to the area - if (face2->frontarea != tmparea2 && - face2->backarea != tmparea2) Error("face does not belong to area2"); - //just continue if the face is seperating the two areas - //NOTE: a result of this is that ground and gap areas can - // be merged if the seperating face is the gap - if ((face2->frontarea == tmparea1 && - face2->backarea == tmparea2) || - (face2->frontarea == tmparea2 && - face2->backarea == tmparea1)) continue; - //get area2 face flags - area2faceflags |= face2->faceflags; - if (AAS_GapFace(face2, side2)) area2faceflags |= FACE_GAP; - //if the two faces would create a non-convex area - if (NonConvex(face1, face2, side1, side2)) return false; - } //end for - } //end for - //if one area has gap faces (that aren't seperating the two areas) - //and the other has ground faces (that aren't seperating the two areas), - //the areas can't be merged - if (((area1faceflags & FACE_GROUND) && (area2faceflags & FACE_GAP)) || - ((area2faceflags & FACE_GROUND) && (area1faceflags & FACE_GAP))) - { -// Log_Print(" can't merge: ground/gap\n"); - return false; - } //end if - -// Log_Print("merged area %d & %d to %d with %d faces\n", tmparea1->areanum, tmparea2->areanum, newarea->areanum, numfaces); -// return false; - // - //AAS_CheckArea(tmparea1); - //AAS_CheckArea(tmparea2); - //create the new area - newarea = AAS_AllocTmpArea(); - newarea->presencetype = tmparea1->presencetype; - newarea->contents = tmparea1->contents; - newarea->modelnum = tmparea1->modelnum; - newarea->tmpfaces = NULL; - - //add all the faces (except the seperating ones) from the first area - //to the new area - for (face1 = tmparea1->tmpfaces; face1; face1 = nextface1) - { - side1 = (face1->frontarea != tmparea1); - nextface1 = face1->next[side1]; - //don't add seperating faces - if ((face1->frontarea == tmparea1 && - face1->backarea == tmparea2) || - (face1->frontarea == tmparea2 && - face1->backarea == tmparea1)) - { - continue; - } //end if - // - AAS_RemoveFaceFromArea(face1, tmparea1); - AAS_AddFaceSideToArea(face1, side1, newarea); - } //end for - //add all the faces (except the seperating ones) from the second area - //to the new area - for (face2 = tmparea2->tmpfaces; face2; face2 = nextface2) - { - side2 = (face2->frontarea != tmparea2); - nextface2 = face2->next[side2]; - //don't add seperating faces - if ((face2->frontarea == tmparea1 && - face2->backarea == tmparea2) || - (face2->frontarea == tmparea2 && - face2->backarea == tmparea1)) - { - continue; - } //end if - // - AAS_RemoveFaceFromArea(face2, tmparea2); - AAS_AddFaceSideToArea(face2, side2, newarea); - } //end for - //free all shared faces - for (face1 = tmparea1->tmpfaces; face1; face1 = nextface1) - { - side1 = (face1->frontarea != tmparea1); - nextface1 = face1->next[side1]; - // - AAS_RemoveFaceFromArea(face1, face1->frontarea); - AAS_RemoveFaceFromArea(face1, face1->backarea); - AAS_FreeTmpFace(face1); - } //end for - // - tmparea1->mergedarea = newarea; - tmparea1->invalid = true; - tmparea2->mergedarea = newarea; - tmparea2->invalid = true; - // - AAS_CheckArea(newarea); - AAS_FlipAreaFaces(newarea); -// Log_Print("merged area %d & %d to %d with %d faces\n", tmparea1->areanum, tmparea2->areanum, newarea->areanum); - return true; -} //end of the function AAS_TryMergeFaceAreas -//=========================================================================== -// try to merge areas -// merged areas are added to the end of the convex area list so merging -// will be tried for those areas as well -// -// Parameter: - -// Returns: - -// Changes Globals: tmpaasworld -//=========================================================================== -/* -void AAS_MergeAreas(void) -{ - int side, nummerges; - tmp_area_t *tmparea, *othertmparea; - tmp_face_t *face; - - nummerges = 0; - Log_Write("AAS_MergeAreas\r\n"); - qprintf("%6d areas merged", 1); - //first merge grounded areas only - //NOTE: this is useless because the area settings aren't available yet - for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next) - { -// Log_Print("checking area %d\n", i); - //if the area is invalid - if (tmparea->invalid) - { -// Log_Print(" area invalid\n"); - continue; - } //end if - // -// if (!(tmparea->settings->areaflags & AREA_GROUNDED)) continue; - // - for (face = tmparea->tmpfaces; face; face = face->next[side]) - { - side = (face->frontarea != tmparea); - //if the face has both a front and back area - if (face->frontarea && face->backarea) - { - // - if (face->frontarea == tmparea) othertmparea = face->backarea; - else othertmparea = face->frontarea; -// if (!(othertmparea->settings->areaflags & AREA_GROUNDED)) continue; -// Log_Print(" checking area %d with %d\n", face->frontarea, face->backarea); - if (AAS_TryMergeFaceAreas(face)) - { - qprintf("\r%6d", ++nummerges); - break; - } //end if - } //end if - } //end for - } //end for - //merge all areas - for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next) - { -// Log_Print("checking area %d\n", i); - //if the area is invalid - if (tmparea->invalid) - { -// Log_Print(" area invalid\n"); - continue; - } //end if - // - for (face = tmparea->tmpfaces; face; face = face->next[side]) - { - side = (face->frontarea != tmparea); - //if the face has both a front and back area - if (face->frontarea && face->backarea) - { -// Log_Print(" checking area %d with %d\n", face->frontarea, face->backarea); - if (AAS_TryMergeFaceAreas(face)) - { - qprintf("\r%6d", ++nummerges); - break; - } //end if - } //end if - } //end for - } //end for - Log_Print("\r%6d areas merged\n", nummerges); - //refresh the merged tree - AAS_RefreshMergedTree_r(tmpaasworld.nodes); -} //end of the function AAS_MergeAreas*/ - -int AAS_GroundArea(tmp_area_t *tmparea) -{ - tmp_face_t *face; - int side; - - for (face = tmparea->tmpfaces; face; face = face->next[side]) - { - side = (face->frontarea != tmparea); - if (face->faceflags & FACE_GROUND) return true; - } //end for - return false; -} //end of the function AAS_GroundArea - -void AAS_MergeAreas(void) -{ - int side, nummerges, merges, groundfirst; - tmp_area_t *tmparea, *othertmparea; - tmp_face_t *face; - - nummerges = 0; - Log_Write("AAS_MergeAreas\r\n"); - qprintf("%6d areas merged", 1); - // - groundfirst = true; - //for (i = 0; i < 4 || merges; i++) - while(1) - { - //if (i < 2) groundfirst = true; - //else groundfirst = false; - // - merges = 0; - //first merge grounded areas only - for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next) - { - //if the area is invalid - if (tmparea->invalid) - { - continue; - } //end if - // - if (groundfirst) - { - if (!AAS_GroundArea(tmparea)) continue; - } //end if - // - for (face = tmparea->tmpfaces; face; face = face->next[side]) - { - side = (face->frontarea != tmparea); - //if the face has both a front and back area - if (face->frontarea && face->backarea) - { - // - if (face->frontarea == tmparea) othertmparea = face->backarea; - else othertmparea = face->frontarea; - // - if (groundfirst) - { - if (!AAS_GroundArea(othertmparea)) continue; - } //end if - if (AAS_TryMergeFaceAreas(face)) - { - qprintf("\r%6d", ++nummerges); - merges++; - break; - } //end if - } //end if - } //end for - } //end for - if (!merges) - { - if (groundfirst) groundfirst = false; - else break; - } //end if - } //end for - qprintf("\n"); - Log_Write("%6d areas merged\r\n", nummerges); - //refresh the merged tree - AAS_RefreshMergedTree_r(tmpaasworld.nodes); -} //end of the function AAS_MergeAreas diff --git a/tools/quake3/bspc/aas_areamerging.h b/tools/quake3/bspc/aas_areamerging.h deleted file mode 100644 index 14cb8eda..00000000 --- a/tools/quake3/bspc/aas_areamerging.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -void AAS_MergeAreas(void); - diff --git a/tools/quake3/bspc/aas_cfg.c b/tools/quake3/bspc/aas_cfg.c deleted file mode 100644 index d8da999b..00000000 --- a/tools/quake3/bspc/aas_cfg.c +++ /dev/null @@ -1,254 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -//#include "float.h" -#include "botlib/aasfile.h" -#include "aas_store.h" -#include "aas_cfg.h" -#include "botlib/l_precomp.h" -#include "botlib/l_struct.h" -#include "botlib/l_libvar.h" - -#include - -//structure field offsets -#define BBOX_OFS(x) offsetof(aas_bbox_t, x) -#define CFG_OFS(x) offsetof(cfg_t, x) - -//bounding box definition -fielddef_t bbox_fields[] = -{ - {"presencetype", BBOX_OFS(presencetype), FT_INT}, - {"flags", BBOX_OFS(flags), FT_INT}, - {"mins", BBOX_OFS(mins), FT_FLOAT|FT_ARRAY, 3}, - {"maxs", BBOX_OFS(maxs), FT_FLOAT|FT_ARRAY, 3}, - {NULL, 0, 0, 0} -}; - -fielddef_t cfg_fields[] = -{ - {"phys_gravitydirection", CFG_OFS(phys_gravitydirection), FT_FLOAT|FT_ARRAY, 3}, - {"phys_friction", CFG_OFS(phys_friction), FT_FLOAT}, - {"phys_stopspeed", CFG_OFS(phys_stopspeed), FT_FLOAT}, - {"phys_gravity", CFG_OFS(phys_gravity), FT_FLOAT}, - {"phys_waterfriction", CFG_OFS(phys_waterfriction), FT_FLOAT}, - {"phys_watergravity", CFG_OFS(phys_watergravity), FT_FLOAT}, - {"phys_maxvelocity", CFG_OFS(phys_maxvelocity), FT_FLOAT}, - {"phys_maxwalkvelocity", CFG_OFS(phys_maxwalkvelocity), FT_FLOAT}, - {"phys_maxcrouchvelocity", CFG_OFS(phys_maxcrouchvelocity), FT_FLOAT}, - {"phys_maxswimvelocity", CFG_OFS(phys_maxswimvelocity), FT_FLOAT}, - {"phys_walkaccelerate", CFG_OFS(phys_walkaccelerate), FT_FLOAT}, - {"phys_airaccelerate", CFG_OFS(phys_airaccelerate), FT_FLOAT}, - {"phys_swimaccelerate", CFG_OFS(phys_swimaccelerate), FT_FLOAT}, - {"phys_maxstep", CFG_OFS(phys_maxstep), FT_FLOAT}, - {"phys_maxsteepness", CFG_OFS(phys_maxsteepness), FT_FLOAT}, - {"phys_maxwaterjump", CFG_OFS(phys_maxwaterjump), FT_FLOAT}, - {"phys_maxbarrier", CFG_OFS(phys_maxbarrier), FT_FLOAT}, - {"phys_jumpvel", CFG_OFS(phys_jumpvel), FT_FLOAT}, - {"phys_falldelta5", CFG_OFS(phys_falldelta5), FT_FLOAT}, - {"phys_falldelta10", CFG_OFS(phys_falldelta10), FT_FLOAT}, - {"rs_waterjump", CFG_OFS(rs_waterjump), FT_FLOAT}, - {"rs_teleport", CFG_OFS(rs_teleport), FT_FLOAT}, - {"rs_barrierjump", CFG_OFS(rs_barrierjump), FT_FLOAT}, - {"rs_startcrouch", CFG_OFS(rs_startcrouch), FT_FLOAT}, - {"rs_startgrapple", CFG_OFS(rs_startgrapple), FT_FLOAT}, - {"rs_startwalkoffledge", CFG_OFS(rs_startwalkoffledge), FT_FLOAT}, - {"rs_startjump", CFG_OFS(rs_startjump), FT_FLOAT}, - {"rs_rocketjump", CFG_OFS(rs_rocketjump), FT_FLOAT}, - {"rs_bfgjump", CFG_OFS(rs_bfgjump), FT_FLOAT}, - {"rs_jumppad", CFG_OFS(rs_jumppad), FT_FLOAT}, - {"rs_aircontrolledjumppad", CFG_OFS(rs_aircontrolledjumppad), FT_FLOAT}, - {"rs_funcbob", CFG_OFS(rs_funcbob), FT_FLOAT}, - {"rs_startelevator", CFG_OFS(rs_startelevator), FT_FLOAT}, - {"rs_falldamage5", CFG_OFS(rs_falldamage5), FT_FLOAT}, - {"rs_falldamage10", CFG_OFS(rs_falldamage10), FT_FLOAT}, - {"rs_maxjumpfallheight", CFG_OFS(rs_maxjumpfallheight), FT_FLOAT}, - {NULL, 0, 0, 0} -}; - -structdef_t bbox_struct = -{ - sizeof(aas_bbox_t), bbox_fields -}; -structdef_t cfg_struct = -{ - sizeof(cfg_t), cfg_fields -}; - -//global cfg -cfg_t cfg; - -//=========================================================================== -// the default Q3A configuration -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void DefaultCfg(void) -{ - int i; - - // default all float values to infinite - for (i = 0; cfg_fields[i].name; i++) - { - if ((cfg_fields[i].type & FT_TYPE) == FT_FLOAT) - *(float *)( ((char*)&cfg) + cfg_fields[i].offset ) = FLT_MAX; - } //end for - // - cfg.numbboxes = 2; - //bbox 0 - cfg.bboxes[0].presencetype = PRESENCE_NORMAL; - cfg.bboxes[0].flags = 0; - cfg.bboxes[0].mins[0] = -15; - cfg.bboxes[0].mins[1] = -15; - cfg.bboxes[0].mins[2] = -24; - cfg.bboxes[0].maxs[0] = 15; - cfg.bboxes[0].maxs[1] = 15; - cfg.bboxes[0].maxs[2] = 32; - //bbox 1 - cfg.bboxes[1].presencetype = PRESENCE_CROUCH; - cfg.bboxes[1].flags = 1; - cfg.bboxes[1].mins[0] = -15; - cfg.bboxes[1].mins[1] = -15; - cfg.bboxes[1].mins[2] = -24; - cfg.bboxes[1].maxs[0] = 15; - cfg.bboxes[1].maxs[1] = 15; - cfg.bboxes[1].maxs[2] = 16; - // - cfg.allpresencetypes = PRESENCE_NORMAL|PRESENCE_CROUCH; - cfg.phys_gravitydirection[0] = 0; - cfg.phys_gravitydirection[1] = 0; - cfg.phys_gravitydirection[2] = -1; - cfg.phys_maxsteepness = 0.7; -} //end of the function DefaultCfg -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char * QDECL va( char *format, ... ) -{ - va_list argptr; - static char string[2][32000]; // in case va is called by nested functions - static int index = 0; - char *buf; - - buf = string[index & 1]; - index++; - - va_start (argptr, format); - vsprintf (buf, format,argptr); - va_end (argptr); - - return buf; -} //end of the function va -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void SetCfgLibVars(void) -{ - int i; - float value; - - for (i = 0; cfg_fields[i].name; i++) - { - if ((cfg_fields[i].type & FT_TYPE) == FT_FLOAT) - { - value = *(float *)(((char*)&cfg) + cfg_fields[i].offset); - if (value != FLT_MAX) - { - LibVarSet(cfg_fields[i].name, va("%f", value)); - } //end if - } //end if - } //end for -} //end of the function SetCfgLibVars -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int LoadCfgFile(char *filename) -{ - source_t *source; - token_t token; - int settingsdefined; - - source = LoadSourceFile(filename); - if (!source) - { - Log_Print("couldn't open cfg file %s\n", filename); - return false; - } //end if - - settingsdefined = false; - memset(&cfg, 0, sizeof(cfg_t)); - - while(PC_ReadToken(source, &token)) - { - if (!stricmp(token.string, "bbox")) - { - if (cfg.numbboxes >= AAS_MAX_BBOXES) - { - SourceError(source, "too many bounding box volumes defined"); - } //end if - if (!ReadStructure(source, &bbox_struct, (char *) &cfg.bboxes[cfg.numbboxes])) - { - FreeSource(source); - return false; - } //end if - cfg.allpresencetypes |= cfg.bboxes[cfg.numbboxes].presencetype; - cfg.numbboxes++; - } //end if - else if (!stricmp(token.string, "settings")) - { - if (settingsdefined) - { - SourceWarning(source, "settings already defined\n"); - } //end if - settingsdefined = true; - if (!ReadStructure(source, &cfg_struct, (char *) &cfg)) - { - FreeSource(source); - return false; - } //end if - } //end else if - } //end while - if (VectorLength(cfg.phys_gravitydirection) < 0.9 || VectorLength(cfg.phys_gravitydirection) > 1.1) - { - SourceError(source, "invalid gravity direction specified"); - } //end if - if (cfg.numbboxes <= 0) - { - SourceError(source, "no bounding volumes specified"); - } //end if - FreeSource(source); - SetCfgLibVars(); - Log_Print("using cfg file %s\n", filename); - return true; -} //end of the function LoadCfgFile diff --git a/tools/quake3/bspc/aas_cfg.h b/tools/quake3/bspc/aas_cfg.h deleted file mode 100644 index 2517a2f0..00000000 --- a/tools/quake3/bspc/aas_cfg.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#define BBOXFL_GROUNDED 1 //bounding box only valid when on ground -#define BBOXFL_NOTGROUNDED 2 //bounding box only valid when NOT on ground - -#ifndef FLT_MAX - #define FLT_MAX 3.402823466e+38F -#endif - -typedef struct cfg_s -{ - int numbboxes; //number of bounding boxes - aas_bbox_t bboxes[AAS_MAX_BBOXES]; //all the bounding boxes - int allpresencetypes; //or of all presence types - // aas settings - vec3_t phys_gravitydirection; - float phys_friction; - float phys_stopspeed; - float phys_gravity; - float phys_waterfriction; - float phys_watergravity; - float phys_maxvelocity; - float phys_maxwalkvelocity; - float phys_maxcrouchvelocity; - float phys_maxswimvelocity; - float phys_walkaccelerate; - float phys_airaccelerate; - float phys_swimaccelerate; - float phys_maxstep; - float phys_maxsteepness; - float phys_maxwaterjump; - float phys_maxbarrier; - float phys_jumpvel; - float phys_falldelta5; - float phys_falldelta10; - float rs_waterjump; - float rs_teleport; - float rs_barrierjump; - float rs_startcrouch; - float rs_startgrapple; - float rs_startwalkoffledge; - float rs_startjump; - float rs_rocketjump; - float rs_bfgjump; - float rs_jumppad; - float rs_aircontrolledjumppad; - float rs_funcbob; - float rs_startelevator; - float rs_falldamage5; - float rs_falldamage10; - float rs_maxjumpfallheight; -} cfg_t; - -extern cfg_t cfg; - -void DefaultCfg(void); -int LoadCfgFile(char *filename); diff --git a/tools/quake3/bspc/aas_create.c b/tools/quake3/bspc/aas_create.c deleted file mode 100644 index c4443d3d..00000000 --- a/tools/quake3/bspc/aas_create.c +++ /dev/null @@ -1,1141 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "botlib/aasfile.h" -#include "aas_create.h" -#include "aas_store.h" -#include "aas_gsubdiv.h" -#include "aas_facemerging.h" -#include "aas_areamerging.h" -#include "aas_edgemelting.h" -#include "aas_prunenodes.h" -#include "aas_cfg.h" -#include "qcommon/surfaceflags.h" - -//#define AW_DEBUG -//#define L_DEBUG - -#define AREAONFACESIDE(face, area) (face->frontarea != area) - -tmp_aas_t tmpaasworld; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitTmpAAS(void) -{ - //tmp faces - tmpaasworld.numfaces = 0; - tmpaasworld.facenum = 0; - tmpaasworld.faces = NULL; - //tmp convex areas - tmpaasworld.numareas = 0; - tmpaasworld.areanum = 0; - tmpaasworld.areas = NULL; - //tmp nodes - tmpaasworld.numnodes = 0; - tmpaasworld.nodes = NULL; - // - tmpaasworld.nodebuffer = NULL; -} //end of the function AAS_InitTmpAAS -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeTmpAAS(void) -{ - tmp_face_t *f, *nextf; - tmp_area_t *a, *nexta; - tmp_nodebuf_t *nb, *nextnb; - - //free all the faces - for (f = tmpaasworld.faces; f; f = nextf) - { - nextf = f->l_next; - if (f->winding) FreeWinding(f->winding); - FreeMemory(f); - } //end if - //free all tmp areas - for (a = tmpaasworld.areas; a; a = nexta) - { - nexta = a->l_next; - if (a->settings) FreeMemory(a->settings); - FreeMemory(a); - } //end for - //free all the tmp nodes - for (nb = tmpaasworld.nodebuffer; nb; nb = nextnb) - { - nextnb = nb->next; - FreeMemory(nb); - } //end for -} //end of the function AAS_FreeTmpAAS -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_face_t *AAS_AllocTmpFace(void) -{ - tmp_face_t *tmpface; - - tmpface = (tmp_face_t *) GetClearedMemory(sizeof(tmp_face_t)); - tmpface->num = tmpaasworld.facenum++; - tmpface->l_prev = NULL; - tmpface->l_next = tmpaasworld.faces; - if (tmpaasworld.faces) tmpaasworld.faces->l_prev = tmpface; - tmpaasworld.faces = tmpface; - tmpaasworld.numfaces++; - return tmpface; -} //end of the function AAS_AllocTmpFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeTmpFace(tmp_face_t *tmpface) -{ - if (tmpface->l_next) tmpface->l_next->l_prev = tmpface->l_prev; - if (tmpface->l_prev) tmpface->l_prev->l_next = tmpface->l_next; - else tmpaasworld.faces = tmpface->l_next; - //free the winding - if (tmpface->winding) FreeWinding(tmpface->winding); - //free the face - FreeMemory(tmpface); - tmpaasworld.numfaces--; -} //end of the function AAS_FreeTmpFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_area_t *AAS_AllocTmpArea(void) -{ - tmp_area_t *tmparea; - - tmparea = (tmp_area_t *) GetClearedMemory(sizeof(tmp_area_t)); - tmparea->areanum = tmpaasworld.areanum++; - tmparea->l_prev = NULL; - tmparea->l_next = tmpaasworld.areas; - if (tmpaasworld.areas) tmpaasworld.areas->l_prev = tmparea; - tmpaasworld.areas = tmparea; - tmpaasworld.numareas++; - return tmparea; -} //end of the function AAS_AllocTmpArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeTmpArea(tmp_area_t *tmparea) -{ - if (tmparea->l_next) tmparea->l_next->l_prev = tmparea->l_prev; - if (tmparea->l_prev) tmparea->l_prev->l_next = tmparea->l_next; - else tmpaasworld.areas = tmparea->l_next; - if (tmparea->settings) FreeMemory(tmparea->settings); - FreeMemory(tmparea); - tmpaasworld.numareas--; -} //end of the function AAS_FreeTmpArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_node_t *AAS_AllocTmpNode(void) -{ - tmp_nodebuf_t *nodebuf; - - if (!tmpaasworld.nodebuffer || - tmpaasworld.nodebuffer->numnodes >= NODEBUF_SIZE) - { - nodebuf = (tmp_nodebuf_t *) GetClearedMemory(sizeof(tmp_nodebuf_t)); - nodebuf->next = tmpaasworld.nodebuffer; - nodebuf->numnodes = 0; - tmpaasworld.nodebuffer = nodebuf; - } //end if - tmpaasworld.numnodes++; - return &tmpaasworld.nodebuffer->nodes[tmpaasworld.nodebuffer->numnodes++]; -} //end of the function AAS_AllocTmpNode -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeTmpNode(tmp_node_t *tmpnode) -{ - tmpaasworld.numnodes--; -} //end of the function AAS_FreeTmpNode -//=========================================================================== -// returns true if the face is a gap from the given side -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_GapFace(tmp_face_t *tmpface, int side) -{ - vec3_t invgravity; - - //if the face is a solid or ground face it can't be a gap - if (tmpface->faceflags & (FACE_GROUND | FACE_SOLID)) return 0; - - VectorCopy(cfg.phys_gravitydirection, invgravity); - VectorInverse(invgravity); - - return (DotProduct(invgravity, mapplanes[tmpface->planenum ^ side].normal) > cfg.phys_maxsteepness); -} //end of the function AAS_GapFace -//=========================================================================== -// returns true if the face is a ground face -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_GroundFace(tmp_face_t *tmpface) -{ - vec3_t invgravity; - - //must be a solid face - if (!(tmpface->faceflags & FACE_SOLID)) return 0; - - VectorCopy(cfg.phys_gravitydirection, invgravity); - VectorInverse(invgravity); - - return (DotProduct(invgravity, mapplanes[tmpface->planenum].normal) > cfg.phys_maxsteepness); -} //end of the function AAS_GroundFace -//=========================================================================== -// adds the side of a face to an area -// -// side : 0 = front side -// 1 = back side -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_AddFaceSideToArea(tmp_face_t *tmpface, int side, tmp_area_t *tmparea) -{ - int tmpfaceside; - - if (side) - { - if (tmpface->backarea) Error("AAS_AddFaceSideToArea: already a back area\n"); - } //end if - else - { - if (tmpface->frontarea) Error("AAS_AddFaceSideToArea: already a front area\n"); - } //end else - - if (side) tmpface->backarea = tmparea; - else tmpface->frontarea = tmparea; - - if (tmparea->tmpfaces) - { - tmpfaceside = tmparea->tmpfaces->frontarea != tmparea; - tmparea->tmpfaces->prev[tmpfaceside] = tmpface; - } //end if - tmpface->next[side] = tmparea->tmpfaces; - tmpface->prev[side] = NULL; - tmparea->tmpfaces = tmpface; -} //end of the function AAS_AddFaceSideToArea -//=========================================================================== -// remove (a side of) a face from an area -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_RemoveFaceFromArea(tmp_face_t *tmpface, tmp_area_t *tmparea) -{ - int side, prevside, nextside; - - if (tmpface->frontarea != tmparea && - tmpface->backarea != tmparea) - { - Error("AAS_RemoveFaceFromArea: face not part of the area"); - } //end if - side = tmpface->frontarea != tmparea; - if (tmpface->prev[side]) - { - prevside = tmpface->prev[side]->frontarea != tmparea; - tmpface->prev[side]->next[prevside] = tmpface->next[side]; - } //end if - else - { - tmparea->tmpfaces = tmpface->next[side]; - } //end else - if (tmpface->next[side]) - { - nextside = tmpface->next[side]->frontarea != tmparea; - tmpface->next[side]->prev[nextside] = tmpface->prev[side]; - } //end if - //remove the area number from the face depending on the side - if (side) tmpface->backarea = NULL; - else tmpface->frontarea = NULL; - tmpface->prev[side] = NULL; - tmpface->next[side] = NULL; -} //end of the function AAS_RemoveFaceFromArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CheckArea(tmp_area_t *tmparea) -{ - int side; - tmp_face_t *face; - plane_t *plane; - vec3_t wcenter, acenter = {0, 0, 0}; - vec3_t normal; - float n, dist; - - if (tmparea->invalid) Log_Print("AAS_CheckArea: invalid area\n"); - for (n = 0, face = tmparea->tmpfaces; face; face = face->next[side]) - { - //side of the face the area is on - side = face->frontarea != tmparea; - WindingCenter(face->winding, wcenter); - VectorAdd(acenter, wcenter, acenter); - n++; - } //end for - n = 1 / n; - VectorScale(acenter, n, acenter); - for (face = tmparea->tmpfaces; face; face = face->next[side]) - { - //side of the face the area is on - side = face->frontarea != tmparea; - -#ifdef L_DEBUG - if (WindingError(face->winding)) - { - Log_Write("AAS_CheckArea: area %d face %d: %s\r\n", tmparea->areanum, - face->num, WindingErrorString()); - } //end if -#endif - - plane = &mapplanes[face->planenum ^ side]; - - if (DotProduct(plane->normal, acenter) - plane->dist < 0) - { - Log_Print("AAS_CheckArea: area %d face %d is flipped\n", tmparea->areanum, face->num); - Log_Print("AAS_CheckArea: area %d center is %f %f %f\n", tmparea->areanum, acenter[0], acenter[1], acenter[2]); - } //end if - //check if the winding plane is the same as the face plane - WindingPlane(face->winding, normal, &dist); - plane = &mapplanes[face->planenum]; -#ifdef L_DEBUG - if (fabs(dist - plane->dist) > 0.4 || - fabs(normal[0] - plane->normal[0]) > 0.0001 || - fabs(normal[1] - plane->normal[1]) > 0.0001 || - fabs(normal[2] - plane->normal[2]) > 0.0001) - { - Log_Write("AAS_CheckArea: area %d face %d winding plane unequal to face plane\r\n", - tmparea->areanum, face->num); - } //end if -#endif - } //end for -} //end of the function AAS_CheckArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CheckFaceWindingPlane(tmp_face_t *face) -{ - float dist, sign1, sign2; - vec3_t normal; - plane_t *plane; - winding_t *w; - - //check if the winding plane is the same as the face plane - WindingPlane(face->winding, normal, &dist); - plane = &mapplanes[face->planenum]; - // - sign1 = DotProduct(plane->normal, normal); - // - if (fabs(dist - plane->dist) > 0.4 || - fabs(normal[0] - plane->normal[0]) > 0.0001 || - fabs(normal[1] - plane->normal[1]) > 0.0001 || - fabs(normal[2] - plane->normal[2]) > 0.0001) - { - VectorInverse(normal); - dist = -dist; - if (fabs(dist - plane->dist) > 0.4 || - fabs(normal[0] - plane->normal[0]) > 0.0001 || - fabs(normal[1] - plane->normal[1]) > 0.0001 || - fabs(normal[2] - plane->normal[2]) > 0.0001) - { - Log_Write("AAS_CheckFaceWindingPlane: face %d winding plane unequal to face plane\r\n", - face->num); - // - sign2 = DotProduct(plane->normal, normal); - if ((sign1 < 0 && sign2 > 0) || - (sign1 > 0 && sign2 < 0)) - { - Log_Write("AAS_CheckFaceWindingPlane: face %d winding reversed\r\n", - face->num); - w = face->winding; - face->winding = ReverseWinding(w); - FreeWinding(w); - } //end if - } //end if - else - { - Log_Write("AAS_CheckFaceWindingPlane: face %d winding reversed\r\n", - face->num); - w = face->winding; - face->winding = ReverseWinding(w); - FreeWinding(w); - } //end else - } //end if -} //end of the function AAS_CheckFaceWindingPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CheckAreaWindingPlanes(void) -{ - int side; - tmp_area_t *tmparea; - tmp_face_t *face; - - Log_Write("AAS_CheckAreaWindingPlanes:\r\n"); - for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next) - { - if (tmparea->invalid) continue; - for (face = tmparea->tmpfaces; face; face = face->next[side]) - { - side = face->frontarea != tmparea; - AAS_CheckFaceWindingPlane(face); - } //end for - } //end for -} //end of the function AAS_CheckAreaWindingPlanes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FlipAreaFaces(tmp_area_t *tmparea) -{ - int side; - tmp_face_t *face; - plane_t *plane; - vec3_t wcenter, acenter = {0, 0, 0}; - //winding_t *w; - float n; - - for (n = 0, face = tmparea->tmpfaces; face; face = face->next[side]) - { - if (!face->frontarea) Error("face %d has no front area\n", face->num); - //side of the face the area is on - side = face->frontarea != tmparea; - WindingCenter(face->winding, wcenter); - VectorAdd(acenter, wcenter, acenter); - n++; - } //end for - n = 1 / n; - VectorScale(acenter, n, acenter); - for (face = tmparea->tmpfaces; face; face = face->next[side]) - { - //side of the face the area is on - side = face->frontarea != tmparea; - - plane = &mapplanes[face->planenum ^ side]; - - if (DotProduct(plane->normal, acenter) - plane->dist < 0) - { - Log_Print("area %d face %d flipped: front area %d, back area %d\n", tmparea->areanum, face->num, - face->frontarea ? face->frontarea->areanum : 0, - face->backarea ? face->backarea->areanum : 0); - /* - face->planenum = face->planenum ^ 1; - w = face->winding; - face->winding = ReverseWinding(w); - FreeWinding(w); - */ - } //end if -#ifdef L_DEBUG - { - float dist; - vec3_t normal; - - //check if the winding plane is the same as the face plane - WindingPlane(face->winding, normal, &dist); - plane = &mapplanes[face->planenum]; - if (fabs(dist - plane->dist) > 0.4 || - fabs(normal[0] - plane->normal[0]) > 0.0001 || - fabs(normal[1] - plane->normal[1]) > 0.0001 || - fabs(normal[2] - plane->normal[2]) > 0.0001) - { - Log_Write("area %d face %d winding plane unequal to face plane\r\n", - tmparea->areanum, face->num); - } //end if - } -#endif - } //end for -} //end of the function AAS_FlipAreaFaces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_RemoveAreaFaceColinearPoints(void) -{ - int side; - tmp_face_t *face; - tmp_area_t *tmparea; - - //FIXME: loop over the faces instead of area->faces - for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next) - { - for (face = tmparea->tmpfaces; face; face = face->next[side]) - { - side = face->frontarea != tmparea; - RemoveColinearPoints(face->winding); -// RemoveEqualPoints(face->winding, 0.1); - } //end for - } //end for -} //end of the function AAS_RemoveAreaFaceColinearPoints -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_RemoveTinyFaces(void) -{ - int side, num; - tmp_face_t *face, *nextface; - tmp_area_t *tmparea; - - //FIXME: loop over the faces instead of area->faces - Log_Write("AAS_RemoveTinyFaces\r\n"); - num = 0; - for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next) - { - for (face = tmparea->tmpfaces; face; face = nextface) - { - side = face->frontarea != tmparea; - nextface = face->next[side]; - // - if (WindingArea(face->winding) < 1) - { - if (face->frontarea) AAS_RemoveFaceFromArea(face, face->frontarea); - if (face->backarea) AAS_RemoveFaceFromArea(face, face->backarea); - AAS_FreeTmpFace(face); - //Log_Write("area %d face %d is tiny\r\n", tmparea->areanum, face->num); - num++; - } //end if - } //end for - } //end for - Log_Write("%d tiny faces removed\r\n", num); -} //end of the function AAS_RemoveTinyFaces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CreateAreaSettings(void) -{ - int i, flags, side, numgrounded, numladderareas, numliquidareas; - tmp_face_t *face; - tmp_area_t *tmparea; - - numgrounded = 0; - numladderareas = 0; - numliquidareas = 0; - Log_Write("AAS_CreateAreaSettings\r\n"); - i = 0; - qprintf("%6d areas provided with settings", i); - for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next) - { - //if the area is invalid there no need to create settings for it - if (tmparea->invalid) continue; - - tmparea->settings = (tmp_areasettings_t *) GetClearedMemory(sizeof(tmp_areasettings_t)); - tmparea->settings->contents = tmparea->contents; - tmparea->settings->modelnum = tmparea->modelnum; - flags = 0; - for (face = tmparea->tmpfaces; face; face = face->next[side]) - { - side = face->frontarea != tmparea; - flags |= face->faceflags; - } //end for - tmparea->settings->areaflags = 0; - if (flags & FACE_GROUND) - { - tmparea->settings->areaflags |= AREA_GROUNDED; - numgrounded++; - } //end if - if (flags & FACE_LADDER) - { - tmparea->settings->areaflags |= AREA_LADDER; - numladderareas++; - } //end if - if (tmparea->contents & (AREACONTENTS_WATER | - AREACONTENTS_SLIME | - AREACONTENTS_LAVA)) - { - tmparea->settings->areaflags |= AREA_LIQUID; - numliquidareas++; - } //end if - //presence type of the area - tmparea->settings->presencetype = tmparea->presencetype; - // - qprintf("\r%6d", ++i); - } //end for - qprintf("\n"); -#ifdef AASINFO - Log_Print("%6d grounded areas\n", numgrounded); - Log_Print("%6d ladder areas\n", numladderareas); - Log_Print("%6d liquid areas\n", numliquidareas); -#endif //AASINFO -} //end of the function AAS_CreateAreaSettings -//=========================================================================== -// create a tmp AAS area from a leaf node -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_node_t *AAS_CreateArea(node_t *node) -{ - int pside; - int areafaceflags; - portal_t *p; - tmp_face_t *tmpface; - tmp_area_t *tmparea; - tmp_node_t *tmpnode; - - //create an area from this leaf - tmparea = AAS_AllocTmpArea(); - tmparea->tmpfaces = NULL; - //clear the area face flags - areafaceflags = 0; - //make aas faces from the portals - for (p = node->portals; p; p = p->next[pside]) - { - pside = (p->nodes[1] == node); - //don't create faces from very small portals -// if (WindingArea(p->winding) < 1) continue; - //if there's already a face created for this portal - if (p->tmpface) - { - //add the back side of the face to the area - AAS_AddFaceSideToArea(p->tmpface, 1, tmparea); - } //end if - else - { - tmpface = AAS_AllocTmpFace(); - //set the face pointer at the portal so we can see from - //the portal there's a face created for it - p->tmpface = tmpface; - //FIXME: test this change - //tmpface->planenum = (p->planenum & ~1) | pside; - tmpface->planenum = p->planenum ^ pside; - if (pside) tmpface->winding = ReverseWinding(p->winding); - else tmpface->winding = CopyWinding(p->winding); -#ifdef L_DEBUG - // - AAS_CheckFaceWindingPlane(tmpface); -#endif //L_DEBUG - //if there's solid at the other side of the portal - if (p->nodes[!pside]->contents & (CONTENTS_SOLID | CONTENTS_PLAYERCLIP)) - { - tmpface->faceflags |= FACE_SOLID; - } //end if - //else there is no solid at the other side and if there - //is a liquid at this side - else if (node->contents & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) - { - tmpface->faceflags |= FACE_LIQUID; - //if there's no liquid at the other side - if (!(p->nodes[!pside]->contents & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))) - { - tmpface->faceflags |= FACE_LIQUIDSURFACE; - } //end if - } //end else - //if there's ladder contents at other side of the portal - if ((p->nodes[pside]->contents & CONTENTS_LADDER) || - (p->nodes[!pside]->contents & CONTENTS_LADDER)) - { - - //NOTE: doesn't have to be solid at the other side because - // when standing one can use a crouch area (which is not solid) - // as a ladder - // imagine a ladder one can walk underthrough, - // under the ladder against the ladder is a crouch area - // the (vertical) sides of this crouch area area also used as - // ladder sides when standing (not crouched) - tmpface->faceflags |= FACE_LADDER; - } //end if - //if it is possible to stand on the face - if (AAS_GroundFace(tmpface)) - { - tmpface->faceflags |= FACE_GROUND; - } //end if - // - areafaceflags |= tmpface->faceflags; - //no aas face number yet (zero is a dummy in the aasworld faces) - tmpface->aasfacenum = 0; - //add the front side of the face to the area - AAS_AddFaceSideToArea(tmpface, 0, tmparea); - } //end else - } //end for - qprintf("\r%6d", tmparea->areanum); - //presence type in the area - tmparea->presencetype = ~node->expansionbboxes & cfg.allpresencetypes; - // - tmparea->contents = 0; - if (node->contents & CONTENTS_CLUSTERPORTAL) tmparea->contents |= AREACONTENTS_CLUSTERPORTAL; - if (node->contents & CONTENTS_MOVER) tmparea->contents |= AREACONTENTS_MOVER; - if (node->contents & CONTENTS_TELEPORTER) tmparea->contents |= AREACONTENTS_TELEPORTER; - if (node->contents & CONTENTS_JUMPPAD) tmparea->contents |= AREACONTENTS_JUMPPAD; - if (node->contents & CONTENTS_DONOTENTER) tmparea->contents |= AREACONTENTS_DONOTENTER; - if (node->contents & CONTENTS_WATER) tmparea->contents |= AREACONTENTS_WATER; - if (node->contents & CONTENTS_LAVA) tmparea->contents |= AREACONTENTS_LAVA; - if (node->contents & CONTENTS_SLIME) tmparea->contents |= AREACONTENTS_SLIME; - if (node->contents & CONTENTS_NOTTEAM1) tmparea->contents |= AREACONTENTS_NOTTEAM1; - if (node->contents & CONTENTS_NOTTEAM2) tmparea->contents |= AREACONTENTS_NOTTEAM2; - - //store the bsp model that's inside this node - tmparea->modelnum = node->modelnum; - //sorta check for flipped area faces (remove??) - AAS_FlipAreaFaces(tmparea); - //check if the area is ok (remove??) - AAS_CheckArea(tmparea); - // - tmpnode = AAS_AllocTmpNode(); - tmpnode->planenum = 0; - tmpnode->children[0] = 0; - tmpnode->children[1] = 0; - tmpnode->tmparea = tmparea; - // - return tmpnode; -} //end of the function AAS_CreateArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_node_t *AAS_CreateAreas_r(node_t *node) -{ - tmp_node_t *tmpnode; - - //recurse down to leafs - if (node->planenum != PLANENUM_LEAF) - { - //the first tmp node is a dummy - tmpnode = AAS_AllocTmpNode(); - tmpnode->planenum = node->planenum; - tmpnode->children[0] = AAS_CreateAreas_r(node->children[0]); - tmpnode->children[1] = AAS_CreateAreas_r(node->children[1]); - return tmpnode; - } //end if - //areas won't be created for solid leafs - if (node->contents & CONTENTS_SOLID) - { - //just return zero for a solid leaf (in tmp AAS NULL is a solid leaf) - return NULL; - } //end if - - return AAS_CreateArea(node); -} //end of the function AAS_CreateAreas_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CreateAreas(node_t *node) -{ - Log_Write("AAS_CreateAreas\r\n"); - qprintf("%6d areas created", 0); - tmpaasworld.nodes = AAS_CreateAreas_r(node); - qprintf("\n"); - Log_Write("%6d areas created\r\n", tmpaasworld.numareas); -} //end of the function AAS_CreateAreas -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_PrintNumGroundFaces(void) -{ - tmp_face_t *tmpface; - int numgroundfaces = 0; - - for (tmpface = tmpaasworld.faces; tmpface; tmpface = tmpface->l_next) - { - if (tmpface->faceflags & FACE_GROUND) - { - numgroundfaces++; - } //end if - } //end for - qprintf("%6d ground faces\n", numgroundfaces); -} //end of the function AAS_PrintNumGroundFaces -//=========================================================================== -// checks the number of shared faces between the given two areas -// since areas are convex they should only have ONE shared face -// however due to crappy face merging there are sometimes several -// shared faces -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CheckAreaSharedFaces(tmp_area_t *tmparea1, tmp_area_t *tmparea2) -{ - int numsharedfaces, side; - tmp_face_t *face1, *sharedface; - - if (tmparea1->invalid || tmparea2->invalid) return; - - sharedface = NULL; - numsharedfaces = 0; - for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side]) - { - side = face1->frontarea != tmparea1; - if (face1->backarea == tmparea2 || face1->frontarea == tmparea2) - { - sharedface = face1; - numsharedfaces++; - } //end if - } //end if - if (!sharedface) return; - //the areas should only have one shared face - if (numsharedfaces > 1) - { - Log_Write("---- tmp area %d and %d have %d shared faces\r\n", - tmparea1->areanum, tmparea2->areanum, numsharedfaces); - for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side]) - { - side = face1->frontarea != tmparea1; - if (face1->backarea == tmparea2 || face1->frontarea == tmparea2) - { - Log_Write("face %d, planenum = %d, face->frontarea = %d face->backarea = %d\r\n", - face1->num, face1->planenum, face1->frontarea->areanum, face1->backarea->areanum); - } //end if - } //end if - } //end if -} //end of the function AAS_CheckAreaSharedFaces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CheckSharedFaces(void) -{ - tmp_area_t *tmparea1, *tmparea2; - - for (tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next) - { - for (tmparea2 = tmpaasworld.areas; tmparea2; tmparea2 = tmparea2->l_next) - { - if (tmparea1 == tmparea2) continue; - AAS_CheckAreaSharedFaces(tmparea1, tmparea2); - } //end for - } //end for -} //end of the function AAS_CheckSharedFaces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FlipFace(tmp_face_t *face) -{ - tmp_area_t *frontarea, *backarea; - winding_t *w; - - frontarea = face->frontarea; - backarea = face->backarea; - //must have an area at both sides before flipping is allowed - if (!frontarea || !backarea) return; - //flip the face winding - w = face->winding; - face->winding = ReverseWinding(w); - FreeWinding(w); - //flip the face plane - face->planenum ^= 1; - //flip the face areas - AAS_RemoveFaceFromArea(face, frontarea); - AAS_RemoveFaceFromArea(face, backarea); - AAS_AddFaceSideToArea(face, 1, frontarea); - AAS_AddFaceSideToArea(face, 0, backarea); -} //end of the function AAS_FlipFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -/* -void AAS_FlipAreaSharedFaces(tmp_area_t *tmparea1, tmp_area_t *tmparea2) -{ - int numsharedfaces, side, area1facing, area2facing; - tmp_face_t *face1, *sharedface; - - if (tmparea1->invalid || tmparea2->invalid) return; - - sharedface = NULL; - numsharedfaces = 0; - area1facing = 0; //number of shared faces facing towards area 1 - area2facing = 0; //number of shared faces facing towards area 2 - for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side]) - { - side = face1->frontarea != tmparea1; - if (face1->backarea == tmparea2 || face1->frontarea == tmparea2) - { - sharedface = face1; - numsharedfaces++; - if (face1->frontarea == tmparea1) area1facing++; - else area2facing++; - } //end if - } //end if - if (!sharedface) return; - //if there's only one shared face - if (numsharedfaces <= 1) return; - //if all the shared faces are facing to the same area - if (numsharedfaces == area1facing || numsharedfaces == area2facing) return; - // - do - { - for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side]) - { - side = face1->frontarea != tmparea1; - if (face1->backarea == tmparea2 || face1->frontarea == tmparea2) - { - if (face1->frontarea != tmparea1) - { - AAS_FlipFace(face1); - break; - } //end if - } //end if - } //end for - } while(face1); -} //end of the function AAS_FlipAreaSharedFaces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FlipSharedFaces(void) -{ - int i; - tmp_area_t *tmparea1, *tmparea2; - - i = 0; - qprintf("%6d areas checked for shared face flipping", i); - for (tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next) - { - if (tmparea1->invalid) continue; - for (tmparea2 = tmpaasworld.areas; tmparea2; tmparea2 = tmparea2->l_next) - { - if (tmparea2->invalid) continue; - if (tmparea1 == tmparea2) continue; - AAS_FlipAreaSharedFaces(tmparea1, tmparea2); - } //end for - qprintf("\r%6d", ++i); - } //end for - Log_Print("\r%6d areas checked for shared face flipping\n", i); -} //end of the function AAS_FlipSharedFaces -*/ -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FlipSharedFaces(void) -{ - int i, side1, side2; - tmp_area_t *tmparea1; - tmp_face_t *face1, *face2; - - i = 0; - qprintf("%6d areas checked for shared face flipping", i); - for (tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next) - { - if (tmparea1->invalid) continue; - for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side1]) - { - side1 = face1->frontarea != tmparea1; - if (!face1->frontarea || !face1->backarea) continue; - // - for (face2 = face1->next[side1]; face2; face2 = face2->next[side2]) - { - side2 = face2->frontarea != tmparea1; - if (!face2->frontarea || !face2->backarea) continue; - // - if (face1->frontarea == face2->backarea && - face1->backarea == face2->frontarea) - { - AAS_FlipFace(face2); - } //end if - //recheck side - side2 = face2->frontarea != tmparea1; - } //end for - } //end for - qprintf("\r%6d", ++i); - } //end for - qprintf("\n"); - Log_Write("%6d areas checked for shared face flipping\r\n", i); -} //end of the function AAS_FlipSharedFaces -//=========================================================================== -// creates an .AAS file with the given name -// a MAP should be loaded before calling this -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_Create(char *aasfile) -{ - entity_t *e; - tree_t *tree; - double start_time; - - //for a possible leak file - strcpy(source, aasfile); - StripExtension(source); - //the time started - start_time = I_FloatTime(); - //set the default number of threads (depends on number of processors) - ThreadSetDefault(); - //set the global entity number to the world model - entity_num = 0; - //the world entity - e = &entities[entity_num]; - //process the whole world - tree = ProcessWorldBrushes(e->firstbrush, e->firstbrush + e->numbrushes); - //if the conversion is cancelled - if (cancelconversion) - { - Tree_Free(tree); - return; - } //end if - //display BSP tree creation time - Log_Print("BSP tree created in %5.0f seconds\n", I_FloatTime() - start_time); - //prune the bsp tree - Tree_PruneNodes(tree->headnode); - //if the conversion is cancelled - if (cancelconversion) - { - Tree_Free(tree); - return; - } //end if - //create the tree portals - MakeTreePortals(tree); - //if the conversion is cancelled - if (cancelconversion) - { - Tree_Free(tree); - return; - } //end if - //Marks all nodes that can be reached by entites - if (FloodEntities(tree)) - { - //fill out nodes that can't be reached - FillOutside(tree->headnode); - } //end if - else - { - LeakFile(tree); - Error("**** leaked ****\n"); - return; - } //end else - //create AAS from the BSP tree - //========================================== - //initialize tmp aas - AAS_InitTmpAAS(); - //create the convex areas from the leaves - AAS_CreateAreas(tree->headnode); - //free the BSP tree because it isn't used anymore - if (freetree) Tree_Free(tree); - //try to merge area faces - AAS_MergeAreaFaces(); - //do gravitational subdivision - AAS_GravitationalSubdivision(); - //merge faces if possible - AAS_MergeAreaFaces(); - AAS_RemoveAreaFaceColinearPoints(); - //merge areas if possible - AAS_MergeAreas(); - //NOTE: prune nodes directly after area merging - AAS_PruneNodes(); - //flip shared faces so they are all facing to the same area - AAS_FlipSharedFaces(); - AAS_RemoveAreaFaceColinearPoints(); - //merge faces if possible - AAS_MergeAreaFaces(); - //merge area faces in the same plane - AAS_MergeAreaPlaneFaces(); - //do ladder subdivision - AAS_LadderSubdivision(); - //FIXME: melting is buggy - AAS_MeltAreaFaceWindings(); - //remove tiny faces - AAS_RemoveTinyFaces(); - //create area settings - AAS_CreateAreaSettings(); - //check if the winding plane is equal to the face plane - //AAS_CheckAreaWindingPlanes(); - // - //AAS_CheckSharedFaces(); - //========================================== - //if the conversion is cancelled - if (cancelconversion) - { - Tree_Free(tree); - AAS_FreeTmpAAS(); - return; - } //end if - //store the created AAS stuff in the AAS file format and write the file - AAS_StoreFile(aasfile); - //free the temporary AAS memory - AAS_FreeTmpAAS(); - //display creation time - Log_Print("\nAAS created in %5.0f seconds\n", I_FloatTime() - start_time); -} //end of the function AAS_Create diff --git a/tools/quake3/bspc/aas_create.h b/tools/quake3/bspc/aas_create.h deleted file mode 100644 index 7f41f02d..00000000 --- a/tools/quake3/bspc/aas_create.h +++ /dev/null @@ -1,136 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#define AREA_PORTAL 1 - -//temporary AAS face -typedef struct tmp_face_s -{ - int num; //face number - int planenum; //number of the plane the face is in - winding_t *winding; //winding of the face - struct tmp_area_s *frontarea; //area at the front of the face - struct tmp_area_s *backarea; //area at the back of the face - int faceflags; //flags of this face - int aasfacenum; //the number of the aas face used for this face - //double link list pointers for front and back area - struct tmp_face_s *prev[2], *next[2]; - //links in the list with faces - struct tmp_face_s *l_prev, *l_next; -} tmp_face_t; - -//temporary AAS area settings -typedef struct tmp_areasettings_s -{ - //could also add all kind of statistic fields - int contents; //contents of the area - int modelnum; //bsp model inside this area - int areaflags; //area flags - int presencetype; //how a bot can be present in this area - int numreachableareas; //number of reachable areas from this one - int firstreachablearea; //first reachable area in the reachable area index -} tmp_areasettings_t; - -//temporary AAS area -typedef struct tmp_area_s -{ - int areanum; //number of the area - struct tmp_face_s *tmpfaces; //the faces of the area - int presencetype; //presence type of the area - int contents; //area contents - int modelnum; //bsp model inside this area - int invalid; //true if the area is invalid - tmp_areasettings_t *settings; //area settings - struct tmp_area_s *mergedarea; //points to the new area after merging - //when mergedarea != 0 the area has only the - //seperating face of the merged areas - int aasareanum; //number of the aas area created for this tmp area - //links in the list with areas - struct tmp_area_s *l_prev, *l_next; -} tmp_area_t; - -//temporary AAS node -typedef struct tmp_node_s -{ - int planenum; //node plane number - struct tmp_area_s *tmparea; //points to an area if this node is an area - struct tmp_node_s *children[2]; //child nodes of this node -} tmp_node_t; - -#define NODEBUF_SIZE 128 -//node buffer -typedef struct tmp_nodebuf_s -{ - int numnodes; - struct tmp_nodebuf_s *next; - tmp_node_t nodes[NODEBUF_SIZE]; -} tmp_nodebuf_t; - -//the whole temorary AAS -typedef struct tmp_aas_s -{ - //faces - int numfaces; - int facenum; - tmp_face_t *faces; - //areas - int numareas; - int areanum; - tmp_area_t *areas; - //area settings - int numareasettings; - tmp_areasettings_t *areasettings; - //nodes - int numnodes; - tmp_node_t *nodes; - //node buffer - tmp_nodebuf_t *nodebuffer; -} tmp_aas_t; - -extern tmp_aas_t tmpaasworld; - -//creates a .AAS file with the given name from an already loaded map -void AAS_Create(char *aasfile); -//adds a face side to an area -void AAS_AddFaceSideToArea(tmp_face_t *tmpface, int side, tmp_area_t *tmparea); -//remvoes a face from an area -void AAS_RemoveFaceFromArea(tmp_face_t *tmpface, tmp_area_t *tmparea); -//allocate a tmp face -tmp_face_t *AAS_AllocTmpFace(void); -//free the tmp face -void AAS_FreeTmpFace(tmp_face_t *tmpface); -//allocate a tmp area -tmp_area_t *AAS_AllocTmpArea(void); -//free a tmp area -void AAS_FreeTmpArea(tmp_area_t *tmparea); -//allocate a tmp node -tmp_node_t *AAS_AllocTmpNode(void); -//free a tmp node -void AAS_FreeTmpNode(tmp_node_t *node); -//checks if an area is ok -void AAS_CheckArea(tmp_area_t *tmparea); -//flips the area faces where needed -void AAS_FlipAreaFaces(tmp_area_t *tmparea); -//returns true if the face is a gap seen from the given side -int AAS_GapFace(tmp_face_t *tmpface, int side); -//returns true if the face is a ground face -int AAS_GroundFace(tmp_face_t *tmpface); diff --git a/tools/quake3/bspc/aas_edgemelting.c b/tools/quake3/bspc/aas_edgemelting.c deleted file mode 100644 index 0cd67018..00000000 --- a/tools/quake3/bspc/aas_edgemelting.c +++ /dev/null @@ -1,108 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "botlib/aasfile.h" -#include "aas_create.h" - -//=========================================================================== -// try to melt the windings of the two faces -// FIXME: this is buggy -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_MeltFaceWinding(tmp_face_t *face1, tmp_face_t *face2) -{ - int i, n; - int splits = 0; - winding_t *w2, *neww; - plane_t *plane1; - -#ifdef DEBUG - if (!face1->winding) Error("face1 %d without winding", face1->num); - if (!face2->winding) Error("face2 %d without winding", face2->num); -#endif //DEBUG - w2 = face2->winding; - plane1 = &mapplanes[face1->planenum]; - for (i = 0; i < w2->numpoints; i++) - { - if (PointOnWinding(face1->winding, plane1->normal, plane1->dist, w2->p[i], &n)) - { - neww = AddWindingPoint(face1->winding, w2->p[i], n); - FreeWinding(face1->winding); - face1->winding = neww; - - splits++; - } //end if - } //end for - return splits; -} //end of the function AAS_MeltFaceWinding -//=========================================================================== -// melt the windings of the area faces -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_MeltFaceWindingsOfArea(tmp_area_t *tmparea) -{ - int side1, side2, num_windingsplits = 0; - tmp_face_t *face1, *face2; - - for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1]) - { - side1 = face1->frontarea != tmparea; - for (face2 = tmparea->tmpfaces; face2; face2 = face2->next[side2]) - { - side2 = face2->frontarea != tmparea; - if (face1 == face2) continue; - num_windingsplits += AAS_MeltFaceWinding(face1, face2); - } //end for - } //end for - return num_windingsplits; -} //end of the function AAS_MeltFaceWindingsOfArea -//=========================================================================== -// melt the windings of the faces of all areas -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_MeltAreaFaceWindings(void) -{ - tmp_area_t *tmparea; - int num_windingsplits = 0; - - Log_Write("AAS_MeltAreaFaceWindings\r\n"); - qprintf("%6d edges melted", num_windingsplits); - //NOTE: first convex area (zero) is a dummy - for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next) - { - num_windingsplits += AAS_MeltFaceWindingsOfArea(tmparea); - qprintf("\r%6d", num_windingsplits); - } //end for - qprintf("\n"); - Log_Write("%6d edges melted\r\n", num_windingsplits); -} //end of the function AAS_MeltAreaFaceWindings - diff --git a/tools/quake3/bspc/aas_edgemelting.h b/tools/quake3/bspc/aas_edgemelting.h deleted file mode 100644 index a296cb6d..00000000 --- a/tools/quake3/bspc/aas_edgemelting.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -void AAS_MeltAreaFaceWindings(void); - diff --git a/tools/quake3/bspc/aas_facemerging.c b/tools/quake3/bspc/aas_facemerging.c deleted file mode 100644 index b6ac76d7..00000000 --- a/tools/quake3/bspc/aas_facemerging.c +++ /dev/null @@ -1,282 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "botlib/aasfile.h" -#include "aas_create.h" - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_TryMergeFaces(tmp_face_t *face1, tmp_face_t *face2) -{ - winding_t *neww; - -#ifdef DEBUG - if (!face1->winding) Error("face1 %d without winding", face1->num); - if (!face2->winding) Error("face2 %d without winding", face2->num); -#endif //DEBUG - // - if (face1->faceflags != face2->faceflags) return false; - //NOTE: if the front or back area is zero this doesn't mean there's - //a real area. It means there's solid at that side of the face - //if both faces have the same front area - if (face1->frontarea == face2->frontarea) - { - //if both faces have the same back area - if (face1->backarea == face2->backarea) - { - //if the faces are in the same plane - if (face1->planenum == face2->planenum) - { - //if they have both a front and a back area (no solid on either side) - if (face1->frontarea && face1->backarea) - { - neww = MergeWindings(face1->winding, face2->winding, - mapplanes[face1->planenum].normal); - } //end if - else - { - //this function is to be found in l_poly.c - neww = TryMergeWinding(face1->winding, face2->winding, - mapplanes[face1->planenum].normal); - } //end else - if (neww) - { - FreeWinding(face1->winding); - face1->winding = neww; - if (face2->frontarea) AAS_RemoveFaceFromArea(face2, face2->frontarea); - if (face2->backarea) AAS_RemoveFaceFromArea(face2, face2->backarea); - AAS_FreeTmpFace(face2); - return true; - } //end if - } //end if - else if ((face1->planenum & ~1) == (face2->planenum & ~1)) - { - Log_Write("face %d and %d, same front and back area but flipped planes\r\n", - face1->num, face2->num); - } //end if - } //end if - } //end if - return false; -} //end of the function AAS_TryMergeFaces -/* -int AAS_TryMergeFaces(tmp_face_t *face1, tmp_face_t *face2) -{ - winding_t *neww; - -#ifdef DEBUG - if (!face1->winding) Error("face1 %d without winding", face1->num); - if (!face2->winding) Error("face2 %d without winding", face2->num); -#endif //DEBUG - //if the faces are in the same plane - if ((face1->planenum & ~1) != (face2->planenum & ~1)) return false; -// if (face1->planenum != face2->planenum) return false; - //NOTE: if the front or back area is zero this doesn't mean there's - //a real area. It means there's solid at that side of the face - //if both faces have the same front area - if (face1->frontarea != face2->frontarea || - face1->backarea != face2->backarea) - { - if (!face1->frontarea || !face1->backarea || - !face2->frontarea || !face2->backarea) return false; - else if (face1->frontarea != face2->backarea || - face1->backarea != face2->frontarea) return false; -// return false; - } //end if - //this function is to be found in l_poly.c - neww = TryMergeWinding(face1->winding, face2->winding, - mapplanes[face1->planenum].normal); - if (!neww) return false; - // - FreeWinding(face1->winding); - face1->winding = neww; - //remove face2 - if (face2->frontarea) - AAS_RemoveFaceFromArea(face2, &tmpaasworld.areas[face2->frontarea]); - if (face2->backarea) - AAS_RemoveFaceFromArea(face2, &tmpaasworld.areas[face2->backarea]); - return true; -} //end of the function AAS_TryMergeFaces*/ -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_MergeAreaFaces(void) -{ - int num_facemerges = 0; - int side1, side2, restart; - tmp_area_t *tmparea, *lasttmparea; - tmp_face_t *face1, *face2; - - Log_Write("AAS_MergeAreaFaces\r\n"); - qprintf("%6d face merges", num_facemerges); - //NOTE: first convex area is a dummy - lasttmparea = tmpaasworld.areas; - for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next) - { - restart = false; - // - if (tmparea->invalid) continue; - // - for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1]) - { - side1 = face1->frontarea != tmparea; - for (face2 = face1->next[side1]; face2; face2 = face2->next[side2]) - { - side2 = face2->frontarea != tmparea; - //if succesfully merged - if (AAS_TryMergeFaces(face1, face2)) - { - //start over again after merging two faces - restart = true; - num_facemerges++; - qprintf("\r%6d", num_facemerges); - AAS_CheckArea(tmparea); - break; - } //end if - } //end for - if (restart) - { - tmparea = lasttmparea; - break; - } //end if - } //end for - lasttmparea = tmparea; - } //end for - qprintf("\n"); - Log_Write("%6d face merges\r\n", num_facemerges); -} //end of the function AAS_MergeAreaFaces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_MergePlaneFaces(tmp_area_t *tmparea, int planenum) -{ - tmp_face_t *face1, *face2, *nextface2; - winding_t *neww; - int side1, side2; - - for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1]) - { - side1 = face1->frontarea != tmparea; - if (face1->planenum != planenum) continue; - // - for (face2 = face1->next[side1]; face2; face2 = nextface2) - { - side2 = face2->frontarea != tmparea; - nextface2 = face2->next[side2]; - // - if ((face2->planenum & ~1) != (planenum & ~1)) continue; - // - neww = MergeWindings(face1->winding, face2->winding, - mapplanes[face1->planenum].normal); - FreeWinding(face1->winding); - face1->winding = neww; - if (face2->frontarea) AAS_RemoveFaceFromArea(face2, face2->frontarea); - if (face2->backarea) AAS_RemoveFaceFromArea(face2, face2->backarea); - AAS_FreeTmpFace(face2); - // - nextface2 = face1->next[side1]; - } //end for - } //end for -} //end of the function AAS_MergePlaneFaces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_CanMergePlaneFaces(tmp_area_t *tmparea, int planenum) -{ - tmp_area_t *frontarea, *backarea; - tmp_face_t *face1; - int side1, merge, faceflags; - - frontarea = backarea = NULL; - merge = false; - for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1]) - { - side1 = face1->frontarea != tmparea; - if ((face1->planenum & ~1) != (planenum & ~1)) continue; - if (!frontarea && !backarea) - { - frontarea = face1->frontarea; - backarea = face1->backarea; - faceflags = face1->faceflags; - } //end if - else - { - if (frontarea != face1->frontarea) return false; - if (backarea != face1->backarea) return false; - if (faceflags != face1->faceflags) return false; - merge = true; - } //end else - } //end for - return merge; -} //end of the function AAS_CanMergePlaneFaces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_MergeAreaPlaneFaces(void) -{ - int num_facemerges = 0; - int side1; - tmp_area_t *tmparea, *nexttmparea; - tmp_face_t *face1; - - Log_Write("AAS_MergePlaneFaces\r\n"); - qprintf("%6d plane face merges", num_facemerges); - //NOTE: first convex area is a dummy - for (tmparea = tmpaasworld.areas; tmparea; tmparea = nexttmparea) - { - nexttmparea = tmparea->l_next; - // - if (tmparea->invalid) continue; - // - for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1]) - { - side1 = face1->frontarea != tmparea; - // - if (AAS_CanMergePlaneFaces(tmparea, face1->planenum)) - { - AAS_MergePlaneFaces(tmparea, face1->planenum); - nexttmparea = tmparea; - num_facemerges++; - qprintf("\r%6d", num_facemerges); - break; - } //end if - } //end for - } //end for - qprintf("\n"); - Log_Write("%6d plane face merges\r\n", num_facemerges); -} //end of the function AAS_MergeAreaPlaneFaces diff --git a/tools/quake3/bspc/aas_facemerging.h b/tools/quake3/bspc/aas_facemerging.h deleted file mode 100644 index b2e02e53..00000000 --- a/tools/quake3/bspc/aas_facemerging.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -void AAS_MergeAreaFaces(void); -void AAS_MergeAreaPlaneFaces(void); diff --git a/tools/quake3/bspc/aas_file.c b/tools/quake3/bspc/aas_file.c deleted file mode 100644 index 9e745d6c..00000000 --- a/tools/quake3/bspc/aas_file.c +++ /dev/null @@ -1,549 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "botlib/aasfile.h" -#include "aas_file.h" -#include "aas_store.h" -#include "aas_create.h" - -#define AAS_Error Error - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_SwapAASData(void) -{ - int i, j; - //bounding boxes - for (i = 0; i < aasworld.numbboxes; i++) - { - aasworld.bboxes[i].presencetype = LittleLong(aasworld.bboxes[i].presencetype); - aasworld.bboxes[i].flags = LittleLong(aasworld.bboxes[i].flags); - for (j = 0; j < 3; j++) - { - aasworld.bboxes[i].mins[j] = LittleLong(aasworld.bboxes[i].mins[j]); - aasworld.bboxes[i].maxs[j] = LittleLong(aasworld.bboxes[i].maxs[j]); - } //end for - } //end for - //vertexes - for (i = 0; i < aasworld.numvertexes; i++) - { - for (j = 0; j < 3; j++) - aasworld.vertexes[i][j] = LittleFloat(aasworld.vertexes[i][j]); - } //end for - //planes - for (i = 0; i < aasworld.numplanes; i++) - { - for (j = 0; j < 3; j++) - aasworld.planes[i].normal[j] = LittleFloat(aasworld.planes[i].normal[j]); - aasworld.planes[i].dist = LittleFloat(aasworld.planes[i].dist); - aasworld.planes[i].type = LittleLong(aasworld.planes[i].type); - } //end for - //edges - for (i = 0; i < aasworld.numedges; i++) - { - aasworld.edges[i].v[0] = LittleLong(aasworld.edges[i].v[0]); - aasworld.edges[i].v[1] = LittleLong(aasworld.edges[i].v[1]); - } //end for - //edgeindex - for (i = 0; i < aasworld.edgeindexsize; i++) - { - aasworld.edgeindex[i] = LittleLong(aasworld.edgeindex[i]); - } //end for - //faces - for (i = 0; i < aasworld.numfaces; i++) - { - aasworld.faces[i].planenum = LittleLong(aasworld.faces[i].planenum); - aasworld.faces[i].faceflags = LittleLong(aasworld.faces[i].faceflags); - aasworld.faces[i].numedges = LittleLong(aasworld.faces[i].numedges); - aasworld.faces[i].firstedge = LittleLong(aasworld.faces[i].firstedge); - aasworld.faces[i].frontarea = LittleLong(aasworld.faces[i].frontarea); - aasworld.faces[i].backarea = LittleLong(aasworld.faces[i].backarea); - } //end for - //face index - for (i = 0; i < aasworld.faceindexsize; i++) - { - aasworld.faceindex[i] = LittleLong(aasworld.faceindex[i]); - } //end for - //convex areas - for (i = 0; i < aasworld.numareas; i++) - { - aasworld.areas[i].areanum = LittleLong(aasworld.areas[i].areanum); - aasworld.areas[i].numfaces = LittleLong(aasworld.areas[i].numfaces); - aasworld.areas[i].firstface = LittleLong(aasworld.areas[i].firstface); - for (j = 0; j < 3; j++) - { - aasworld.areas[i].mins[j] = LittleFloat(aasworld.areas[i].mins[j]); - aasworld.areas[i].maxs[j] = LittleFloat(aasworld.areas[i].maxs[j]); - aasworld.areas[i].center[j] = LittleFloat(aasworld.areas[i].center[j]); - } //end for - } //end for - //area settings - for (i = 0; i < aasworld.numareasettings; i++) - { - aasworld.areasettings[i].contents = LittleLong(aasworld.areasettings[i].contents); - aasworld.areasettings[i].areaflags = LittleLong(aasworld.areasettings[i].areaflags); - aasworld.areasettings[i].presencetype = LittleLong(aasworld.areasettings[i].presencetype); - aasworld.areasettings[i].cluster = LittleLong(aasworld.areasettings[i].cluster); - aasworld.areasettings[i].clusterareanum = LittleLong(aasworld.areasettings[i].clusterareanum); - aasworld.areasettings[i].numreachableareas = LittleLong(aasworld.areasettings[i].numreachableareas); - aasworld.areasettings[i].firstreachablearea = LittleLong(aasworld.areasettings[i].firstreachablearea); - } //end for - //area reachability - for (i = 0; i < aasworld.reachabilitysize; i++) - { - aasworld.reachability[i].areanum = LittleLong(aasworld.reachability[i].areanum); - aasworld.reachability[i].facenum = LittleLong(aasworld.reachability[i].facenum); - aasworld.reachability[i].edgenum = LittleLong(aasworld.reachability[i].edgenum); - for (j = 0; j < 3; j++) - { - aasworld.reachability[i].start[j] = LittleFloat(aasworld.reachability[i].start[j]); - aasworld.reachability[i].end[j] = LittleFloat(aasworld.reachability[i].end[j]); - } //end for - aasworld.reachability[i].traveltype = LittleLong(aasworld.reachability[i].traveltype); - aasworld.reachability[i].traveltime = LittleShort(aasworld.reachability[i].traveltime); - } //end for - //nodes - for (i = 0; i < aasworld.numnodes; i++) - { - aasworld.nodes[i].planenum = LittleLong(aasworld.nodes[i].planenum); - aasworld.nodes[i].children[0] = LittleLong(aasworld.nodes[i].children[0]); - aasworld.nodes[i].children[1] = LittleLong(aasworld.nodes[i].children[1]); - } //end for - //cluster portals - for (i = 0; i < aasworld.numportals; i++) - { - aasworld.portals[i].areanum = LittleLong(aasworld.portals[i].areanum); - aasworld.portals[i].frontcluster = LittleLong(aasworld.portals[i].frontcluster); - aasworld.portals[i].backcluster = LittleLong(aasworld.portals[i].backcluster); - aasworld.portals[i].clusterareanum[0] = LittleLong(aasworld.portals[i].clusterareanum[0]); - aasworld.portals[i].clusterareanum[1] = LittleLong(aasworld.portals[i].clusterareanum[1]); - } //end for - //cluster portal index - for (i = 0; i < aasworld.portalindexsize; i++) - { - aasworld.portalindex[i] = LittleLong(aasworld.portalindex[i]); - } //end for - //cluster - for (i = 0; i < aasworld.numclusters; i++) - { - aasworld.clusters[i].numareas = LittleLong(aasworld.clusters[i].numareas); - aasworld.clusters[i].numportals = LittleLong(aasworld.clusters[i].numportals); - aasworld.clusters[i].firstportal = LittleLong(aasworld.clusters[i].firstportal); - } //end for -} //end of the function AAS_SwapAASData -//=========================================================================== -// dump the current loaded aas file -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DumpAASData(void) -{ - /* - if (aasworld.vertexes) FreeMemory(aasworld.vertexes); - aasworld.vertexes = NULL; - if (aasworld.planes) FreeMemory(aasworld.planes); - aasworld.planes = NULL; - if (aasworld.edges) FreeMemory(aasworld.edges); - aasworld.edges = NULL; - if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex); - aasworld.edgeindex = NULL; - if (aasworld.faces) FreeMemory(aasworld.faces); - aasworld.faces = NULL; - if (aasworld.faceindex) FreeMemory(aasworld.faceindex); - aasworld.faceindex = NULL; - if (aasworld.areas) FreeMemory(aasworld.areas); - aasworld.areas = NULL; - if (aasworld.areasettings) FreeMemory(aasworld.areasettings); - aasworld.areasettings = NULL; - if (aasworld.reachability) FreeMemory(aasworld.reachability); - aasworld.reachability = NULL; - */ - aasworld.loaded = false; -} //end of the function AAS_DumpAASData -//=========================================================================== -// allocate memory and read a lump of a AAS file -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *AAS_LoadAASLump(FILE *fp, int offset, int length, void *buf) -{ - if (!length) - { - printf("lump size 0\n"); - return buf; - } //end if - //seek to the data - if (fseek(fp, offset, SEEK_SET)) - { - AAS_Error("can't seek to lump\n"); - AAS_DumpAASData(); - fclose(fp); - return 0; - } //end if - //allocate memory - if (!buf) buf = (void *) GetClearedMemory(length); - //read the data - if (fread((char *) buf, 1, length, fp) != length) - { - AAS_Error("can't read lump\n"); - FreeMemory(buf); - AAS_DumpAASData(); - fclose(fp); - return NULL; - } //end if - return buf; -} //end of the function AAS_LoadAASLump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DData(unsigned char *data, int size) -{ - int i; - - for (i = 0; i < size; i++) - { - data[i] ^= (unsigned char) i * 119; - } //end for -} //end of the function AAS_DData -//=========================================================================== -// load an aas file -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_LoadAASFile(char *filename, int fpoffset, int fplength) -{ - FILE *fp; - aas_header_t header; - int offset, length; - - //dump current loaded aas file - AAS_DumpAASData(); - //open the file - fp = fopen(filename, "rb"); - if (!fp) - { - AAS_Error("can't open %s\n", filename); - return false; - } //end if - //seek to the correct position (in the pak file) - if (fseek(fp, fpoffset, SEEK_SET)) - { - AAS_Error("can't seek to file %s\n"); - fclose(fp); - return false; - } //end if - //read the header - if (fread(&header, sizeof(aas_header_t), 1, fp) != 1) - { - AAS_Error("can't read header of file %s\n", filename); - fclose(fp); - return false; - } //end if - //check header identification - header.ident = LittleLong(header.ident); - if (header.ident != AASID) - { - AAS_Error("%s is not an AAS file\n", filename); - fclose(fp); - return false; - } //end if - //check the version - header.version = LittleLong(header.version); - if (header.version != AASVERSION_OLD && header.version != AASVERSION) - { - AAS_Error("%s is version %i, not %i\n", filename, header.version, AASVERSION); - fclose(fp); - return false; - } //end if - // - if (header.version == AASVERSION) - { - AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8); - } //end if - aasworld.bspchecksum = LittleLong(header.bspchecksum); - //load the lumps: - //bounding boxes - offset = fpoffset + LittleLong(header.lumps[AASLUMP_BBOXES].fileofs); - length = LittleLong(header.lumps[AASLUMP_BBOXES].filelen); - aasworld.bboxes = (aas_bbox_t *) AAS_LoadAASLump(fp, offset, length, aasworld.bboxes); - if (!aasworld.bboxes) return false; - aasworld.numbboxes = length / sizeof(aas_bbox_t); - //vertexes - offset = fpoffset + LittleLong(header.lumps[AASLUMP_VERTEXES].fileofs); - length = LittleLong(header.lumps[AASLUMP_VERTEXES].filelen); - aasworld.vertexes = (aas_vertex_t *) AAS_LoadAASLump(fp, offset, length, aasworld.vertexes); - if (!aasworld.vertexes) return false; - aasworld.numvertexes = length / sizeof(aas_vertex_t); - //planes - offset = fpoffset + LittleLong(header.lumps[AASLUMP_PLANES].fileofs); - length = LittleLong(header.lumps[AASLUMP_PLANES].filelen); - aasworld.planes = (aas_plane_t *) AAS_LoadAASLump(fp, offset, length, aasworld.planes); - if (!aasworld.planes) return false; - aasworld.numplanes = length / sizeof(aas_plane_t); - //edges - offset = fpoffset + LittleLong(header.lumps[AASLUMP_EDGES].fileofs); - length = LittleLong(header.lumps[AASLUMP_EDGES].filelen); - aasworld.edges = (aas_edge_t *) AAS_LoadAASLump(fp, offset, length, aasworld.edges); - if (!aasworld.edges) return false; - aasworld.numedges = length / sizeof(aas_edge_t); - //edgeindex - offset = fpoffset + LittleLong(header.lumps[AASLUMP_EDGEINDEX].fileofs); - length = LittleLong(header.lumps[AASLUMP_EDGEINDEX].filelen); - aasworld.edgeindex = (aas_edgeindex_t *) AAS_LoadAASLump(fp, offset, length, aasworld.edgeindex); - if (!aasworld.edgeindex) return false; - aasworld.edgeindexsize = length / sizeof(aas_edgeindex_t); - //faces - offset = fpoffset + LittleLong(header.lumps[AASLUMP_FACES].fileofs); - length = LittleLong(header.lumps[AASLUMP_FACES].filelen); - aasworld.faces = (aas_face_t *) AAS_LoadAASLump(fp, offset, length, aasworld.faces); - if (!aasworld.faces) return false; - aasworld.numfaces = length / sizeof(aas_face_t); - //faceindex - offset = fpoffset + LittleLong(header.lumps[AASLUMP_FACEINDEX].fileofs); - length = LittleLong(header.lumps[AASLUMP_FACEINDEX].filelen); - aasworld.faceindex = (aas_faceindex_t *) AAS_LoadAASLump(fp, offset, length, aasworld.faceindex); - if (!aasworld.faceindex) return false; - aasworld.faceindexsize = length / sizeof(int); - //convex areas - offset = fpoffset + LittleLong(header.lumps[AASLUMP_AREAS].fileofs); - length = LittleLong(header.lumps[AASLUMP_AREAS].filelen); - aasworld.areas = (aas_area_t *) AAS_LoadAASLump(fp, offset, length, aasworld.areas); - if (!aasworld.areas) return false; - aasworld.numareas = length / sizeof(aas_area_t); - //area settings - offset = fpoffset + LittleLong(header.lumps[AASLUMP_AREASETTINGS].fileofs); - length = LittleLong(header.lumps[AASLUMP_AREASETTINGS].filelen); - aasworld.areasettings = (aas_areasettings_t *) AAS_LoadAASLump(fp, offset, length, aasworld.areasettings); - if (!aasworld.areasettings) return false; - aasworld.numareasettings = length / sizeof(aas_areasettings_t); - //reachability list - offset = fpoffset + LittleLong(header.lumps[AASLUMP_REACHABILITY].fileofs); - length = LittleLong(header.lumps[AASLUMP_REACHABILITY].filelen); - aasworld.reachability = (aas_reachability_t *) AAS_LoadAASLump(fp, offset, length, aasworld.reachability); - if (length && !aasworld.reachability) return false; - aasworld.reachabilitysize = length / sizeof(aas_reachability_t); - //nodes - offset = fpoffset + LittleLong(header.lumps[AASLUMP_NODES].fileofs); - length = LittleLong(header.lumps[AASLUMP_NODES].filelen); - aasworld.nodes = (aas_node_t *) AAS_LoadAASLump(fp, offset, length, aasworld.nodes); - if (!aasworld.nodes) return false; - aasworld.numnodes = length / sizeof(aas_node_t); - //cluster portals - offset = fpoffset + LittleLong(header.lumps[AASLUMP_PORTALS].fileofs); - length = LittleLong(header.lumps[AASLUMP_PORTALS].filelen); - aasworld.portals = (aas_portal_t *) AAS_LoadAASLump(fp, offset, length, aasworld.portals); - if (length && !aasworld.portals) return false; - aasworld.numportals = length / sizeof(aas_portal_t); - //cluster portal index - offset = fpoffset + LittleLong(header.lumps[AASLUMP_PORTALINDEX].fileofs); - length = LittleLong(header.lumps[AASLUMP_PORTALINDEX].filelen); - aasworld.portalindex = (aas_portalindex_t *) AAS_LoadAASLump(fp, offset, length, aasworld.portalindex); - if (length && !aasworld.portalindex) return false; - aasworld.portalindexsize = length / sizeof(aas_portalindex_t); - //clusters - offset = fpoffset + LittleLong(header.lumps[AASLUMP_CLUSTERS].fileofs); - length = LittleLong(header.lumps[AASLUMP_CLUSTERS].filelen); - aasworld.clusters = (aas_cluster_t *) AAS_LoadAASLump(fp, offset, length, aasworld.clusters); - if (length && !aasworld.clusters) return false; - aasworld.numclusters = length / sizeof(aas_cluster_t); - //swap everything - AAS_SwapAASData(); - //aas file is loaded - aasworld.loaded = true; - //close the file - fclose(fp); - return true; -} //end of the function AAS_LoadAASFile -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_WriteAASLump(FILE *fp, aas_header_t *h, int lumpnum, void *data, int length) -{ - aas_lump_t *lump; - - lump = &h->lumps[lumpnum]; - - lump->fileofs = LittleLong(ftell(fp)); - lump->filelen = LittleLong(length); - - if (length > 0) - { - if (fwrite(data, length, 1, fp) < 1) - { - Log_Print("error writing lump %s\n", lumpnum); - fclose(fp); - return false; - } //end if - } //end if - return true; -} //end of the function AAS_WriteAASLump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShowNumReachabilities(int tt, char *name) -{ - int i, num; - - num = 0; - for (i = 0; i < aasworld.reachabilitysize; i++) - { - if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == tt) - num++; - } //end for - Log_Print("%6d %s\n", num, name); -} //end of the function AAS_ShowNumReachabilities -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShowTotals(void) -{ - Log_Print("numvertexes = %d\r\n", aasworld.numvertexes); - Log_Print("numplanes = %d\r\n", aasworld.numplanes); - Log_Print("numedges = %d\r\n", aasworld.numedges); - Log_Print("edgeindexsize = %d\r\n", aasworld.edgeindexsize); - Log_Print("numfaces = %d\r\n", aasworld.numfaces); - Log_Print("faceindexsize = %d\r\n", aasworld.faceindexsize); - Log_Print("numareas = %d\r\n", aasworld.numareas); - Log_Print("numareasettings = %d\r\n", aasworld.numareasettings); - Log_Print("reachabilitysize = %d\r\n", aasworld.reachabilitysize); - Log_Print("numnodes = %d\r\n", aasworld.numnodes); - Log_Print("numportals = %d\r\n", aasworld.numportals); - Log_Print("portalindexsize = %d\r\n", aasworld.portalindexsize); - Log_Print("numclusters = %d\r\n", aasworld.numclusters); - AAS_ShowNumReachabilities(TRAVEL_WALK, "walk"); - AAS_ShowNumReachabilities(TRAVEL_CROUCH, "crouch"); - AAS_ShowNumReachabilities(TRAVEL_BARRIERJUMP, "barrier jump"); - AAS_ShowNumReachabilities(TRAVEL_JUMP, "jump"); - AAS_ShowNumReachabilities(TRAVEL_LADDER, "ladder"); - AAS_ShowNumReachabilities(TRAVEL_WALKOFFLEDGE, "walk off ledge"); - AAS_ShowNumReachabilities(TRAVEL_SWIM, "swim"); - AAS_ShowNumReachabilities(TRAVEL_WATERJUMP, "water jump"); - AAS_ShowNumReachabilities(TRAVEL_TELEPORT, "teleport"); - AAS_ShowNumReachabilities(TRAVEL_ELEVATOR, "elevator"); - AAS_ShowNumReachabilities(TRAVEL_ROCKETJUMP, "rocket jump"); - AAS_ShowNumReachabilities(TRAVEL_BFGJUMP, "bfg jump"); - AAS_ShowNumReachabilities(TRAVEL_GRAPPLEHOOK, "grapple hook"); - AAS_ShowNumReachabilities(TRAVEL_DOUBLEJUMP, "double jump"); - AAS_ShowNumReachabilities(TRAVEL_RAMPJUMP, "ramp jump"); - AAS_ShowNumReachabilities(TRAVEL_STRAFEJUMP, "strafe jump"); - AAS_ShowNumReachabilities(TRAVEL_JUMPPAD, "jump pad"); - AAS_ShowNumReachabilities(TRAVEL_FUNCBOB, "func bob"); -} //end of the function AAS_ShowTotals -//=========================================================================== -// aas data is useless after writing to file because it is byte swapped -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_WriteAASFile(char *filename) -{ - aas_header_t header; - FILE *fp; - - Log_Print("writing %s\n", filename); - AAS_ShowTotals(); - //swap the aas data - AAS_SwapAASData(); - //initialize the file header - memset(&header, 0, sizeof(aas_header_t)); - header.ident = LittleLong(AASID); - header.version = LittleLong(AASVERSION); - header.bspchecksum = LittleLong(aasworld.bspchecksum); - //open a new file - fp = fopen(filename, "wb"); - if (!fp) - { - Log_Print("error opening %s\n", filename); - return false; - } //end if - //write the header - if (fwrite(&header, sizeof(aas_header_t), 1, fp) < 1) - { - fclose(fp); - return false; - } //end if - //add the data lumps to the file - if (!AAS_WriteAASLump(fp, &header, AASLUMP_BBOXES, aasworld.bboxes, - aasworld.numbboxes * sizeof(aas_bbox_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_VERTEXES, aasworld.vertexes, - aasworld.numvertexes * sizeof(aas_vertex_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_PLANES, aasworld.planes, - aasworld.numplanes * sizeof(aas_plane_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGES, aasworld.edges, - aasworld.numedges * sizeof(aas_edge_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGEINDEX, aasworld.edgeindex, - aasworld.edgeindexsize * sizeof(aas_edgeindex_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACES, aasworld.faces, - aasworld.numfaces * sizeof(aas_face_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACEINDEX, aasworld.faceindex, - aasworld.faceindexsize * sizeof(aas_faceindex_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREAS, aasworld.areas, - aasworld.numareas * sizeof(aas_area_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREASETTINGS, aasworld.areasettings, - aasworld.numareasettings * sizeof(aas_areasettings_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_REACHABILITY, aasworld.reachability, - aasworld.reachabilitysize * sizeof(aas_reachability_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_NODES, aasworld.nodes, - aasworld.numnodes * sizeof(aas_node_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALS, aasworld.portals, - aasworld.numportals * sizeof(aas_portal_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALINDEX, aasworld.portalindex, - aasworld.portalindexsize * sizeof(aas_portalindex_t))) return false; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_CLUSTERS, aasworld.clusters, - aasworld.numclusters * sizeof(aas_cluster_t))) return false; - //rewrite the header with the added lumps - fseek(fp, 0, SEEK_SET); - AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8); - if (fwrite(&header, sizeof(aas_header_t), 1, fp) < 1) - { - fclose(fp); - return false; - } //end if - //close the file - fclose(fp); - return true; -} //end of the function AAS_WriteAASFile - diff --git a/tools/quake3/bspc/aas_file.h b/tools/quake3/bspc/aas_file.h deleted file mode 100644 index d3e15c27..00000000 --- a/tools/quake3/bspc/aas_file.h +++ /dev/null @@ -1,25 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -qboolean AAS_WriteAASFile(char *filename); -qboolean AAS_LoadAASFile(char *filename, int fpoffset, int fplength); - diff --git a/tools/quake3/bspc/aas_gsubdiv.c b/tools/quake3/bspc/aas_gsubdiv.c deleted file mode 100644 index d3cf2223..00000000 --- a/tools/quake3/bspc/aas_gsubdiv.c +++ /dev/null @@ -1,656 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "botlib/aasfile.h" -#include "aas_create.h" -#include "aas_store.h" -#include "aas_cfg.h" - -#define FACECLIP_EPSILON 0.2 -#define FACE_EPSILON 1.0 - -int numgravitationalsubdivisions = 0; -int numladdersubdivisions = 0; - -//NOTE: only do gravitational subdivision BEFORE area merging!!!!!!! -// because the bsp tree isn't refreshes like with ladder subdivision - -//=========================================================================== -// NOTE: the original face is invalid after splitting -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_SplitFace(tmp_face_t *face, vec3_t normal, float dist, - tmp_face_t **frontface, tmp_face_t **backface) -{ - winding_t *frontw, *backw; - - // - *frontface = *backface = NULL; - - ClipWindingEpsilon(face->winding, normal, dist, FACECLIP_EPSILON, &frontw, &backw); - -#ifdef DEBUG - // - if (frontw) - { - if (WindingIsTiny(frontw)) - { - Log_Write("AAS_SplitFace: tiny back face\r\n"); - FreeWinding(frontw); - frontw = NULL; - } //end if - } //end if - if (backw) - { - if (WindingIsTiny(backw)) - { - Log_Write("AAS_SplitFace: tiny back face\r\n"); - FreeWinding(backw); - backw = NULL; - } //end if - } //end if -#endif //DEBUG - //if the winding was split - if (frontw) - { - //check bounds - (*frontface) = AAS_AllocTmpFace(); - (*frontface)->planenum = face->planenum; - (*frontface)->winding = frontw; - (*frontface)->faceflags = face->faceflags; - } //end if - if (backw) - { - //check bounds - (*backface) = AAS_AllocTmpFace(); - (*backface)->planenum = face->planenum; - (*backface)->winding = backw; - (*backface)->faceflags = face->faceflags; - } //end if -} //end of the function AAS_SplitFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -winding_t *AAS_SplitWinding(tmp_area_t *tmparea, int planenum) -{ - tmp_face_t *face; - plane_t *plane; - int side; - winding_t *splitwinding; - - // - plane = &mapplanes[planenum]; - //create a split winding, first base winding for plane - splitwinding = BaseWindingForPlane(plane->normal, plane->dist); - //chop with all the faces of the area - for (face = tmparea->tmpfaces; face && splitwinding; face = face->next[side]) - { - //side of the face the original area was on - side = face->frontarea != tmparea; - plane = &mapplanes[face->planenum ^ side]; - ChopWindingInPlace(&splitwinding, plane->normal, plane->dist, 0); // PLANESIDE_EPSILON); - } //end for - return splitwinding; -} //end of the function AAS_SplitWinding -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_TestSplitPlane(tmp_area_t *tmparea, vec3_t normal, float dist, - int *facesplits, int *groundsplits, int *epsilonfaces) -{ - int j, side, front, back, planenum; - float d, d_front, d_back; - tmp_face_t *face; - winding_t *w; - - *facesplits = *groundsplits = *epsilonfaces = 0; - - planenum = FindFloatPlane(normal, dist); - - w = AAS_SplitWinding(tmparea, planenum); - if (!w) return false; - FreeWinding(w); - // - for (face = tmparea->tmpfaces; face; face = face->next[side]) - { - //side of the face the area is on - side = face->frontarea != tmparea; - - if ((face->planenum & ~1) == (planenum & ~1)) - { - Log_Print("AAS_TestSplitPlane: tried face plane as splitter\n"); - return false; - } //end if - w = face->winding; - //reset distance at front and back side of plane - d_front = d_back = 0; - //reset front and back flags - front = back = 0; - for (j = 0; j < w->numpoints; j++) - { - d = DotProduct(w->p[j], normal) - dist; - if (d > d_front) d_front = d; - if (d < d_back) d_back = d; - - if (d > 0.4) // PLANESIDE_EPSILON) - front = 1; - if (d < -0.4) // PLANESIDE_EPSILON) - back = 1; - } //end for - //check for an epsilon face - if ( (d_front > FACECLIP_EPSILON && d_front < FACE_EPSILON) - || (d_back < -FACECLIP_EPSILON && d_back > -FACE_EPSILON) ) - { - (*epsilonfaces)++; - } //end if - //if the face has points at both sides of the plane - if (front && back) - { - (*facesplits)++; - if (face->faceflags & FACE_GROUND) - { - (*groundsplits)++; - } //end if - } //end if - } //end for - return true; -} //end of the function AAS_TestSplitPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_SplitArea(tmp_area_t *tmparea, int planenum, tmp_area_t **frontarea, tmp_area_t **backarea) -{ - int side; - tmp_area_t *facefrontarea, *facebackarea, *faceotherarea; - tmp_face_t *face, *frontface, *backface, *splitface, *nextface; - winding_t *splitwinding; - plane_t *splitplane; - -/* -#ifdef AW_DEBUG - int facesplits, groundsplits, epsilonface; - Log_Print("\n----------------------\n"); - Log_Print("splitting area %d\n", areanum); - Log_Print("with normal = \'%f %f %f\', dist = %f\n", normal[0], normal[1], normal[2], dist); - AAS_TestSplitPlane(areanum, normal, dist, - &facesplits, &groundsplits, &epsilonface); - Log_Print("face splits = %d\nground splits = %d\n", facesplits, groundsplits); - if (epsilonface) Log_Print("aaahh epsilon face\n"); -#endif //AW_DEBUG*/ - //the original area - - AAS_FlipAreaFaces(tmparea); - AAS_CheckArea(tmparea); - // - splitplane = &mapplanes[planenum]; -/* //create a split winding, first base winding for plane - splitwinding = BaseWindingForPlane(splitplane->normal, splitplane->dist); - //chop with all the faces of the area - for (face = tmparea->tmpfaces; face && splitwinding; face = face->next[side]) - { - //side of the face the original area was on - side = face->frontarea != tmparea->areanum; - plane = &mapplanes[face->planenum ^ side]; - ChopWindingInPlace(&splitwinding, plane->normal, plane->dist, 0); // PLANESIDE_EPSILON); - } //end for*/ - splitwinding = AAS_SplitWinding(tmparea, planenum); - if (!splitwinding) - { -/* -#ifdef DEBUG - AAS_TestSplitPlane(areanum, normal, dist, - &facesplits, &groundsplits, &epsilonface); - Log_Print("\nface splits = %d\nground splits = %d\n", facesplits, groundsplits); - if (epsilonface) Log_Print("aaahh epsilon face\n"); -#endif //DEBUG*/ - Error("AAS_SplitArea: no split winding when splitting area %d\n", tmparea->areanum); - } //end if - //create a split face - splitface = AAS_AllocTmpFace(); - //get the map plane - splitface->planenum = planenum; - //store the split winding - splitface->winding = splitwinding; - //the new front area - (*frontarea) = AAS_AllocTmpArea(); - (*frontarea)->presencetype = tmparea->presencetype; - (*frontarea)->contents = tmparea->contents; - (*frontarea)->modelnum = tmparea->modelnum; - (*frontarea)->tmpfaces = NULL; - //the new back area - (*backarea) = AAS_AllocTmpArea(); - (*backarea)->presencetype = tmparea->presencetype; - (*backarea)->contents = tmparea->contents; - (*backarea)->modelnum = tmparea->modelnum; - (*backarea)->tmpfaces = NULL; - //add the split face to the new areas - AAS_AddFaceSideToArea(splitface, 0, (*frontarea)); - AAS_AddFaceSideToArea(splitface, 1, (*backarea)); - - //split all the faces of the original area - for (face = tmparea->tmpfaces; face; face = nextface) - { - //side of the face the original area was on - side = face->frontarea != tmparea; - //next face of the original area - nextface = face->next[side]; - //front area of the face - facefrontarea = face->frontarea; - //back area of the face - facebackarea = face->backarea; - //remove the face from both the front and back areas - if (facefrontarea) AAS_RemoveFaceFromArea(face, facefrontarea); - if (facebackarea) AAS_RemoveFaceFromArea(face, facebackarea); - //split the face - AAS_SplitFace(face, splitplane->normal, splitplane->dist, &frontface, &backface); - //free the original face - AAS_FreeTmpFace(face); - //get the number of the area at the other side of the face - if (side) faceotherarea = facefrontarea; - else faceotherarea = facebackarea; - //if there is an area at the other side of the original face - if (faceotherarea) - { - if (frontface) AAS_AddFaceSideToArea(frontface, !side, faceotherarea); - if (backface) AAS_AddFaceSideToArea(backface, !side, faceotherarea); - } //end if - //add the front and back part left after splitting the original face to the new areas - if (frontface) AAS_AddFaceSideToArea(frontface, side, (*frontarea)); - if (backface) AAS_AddFaceSideToArea(backface, side, (*backarea)); - } //end for - - if (!(*frontarea)->tmpfaces) Log_Print("AAS_SplitArea: front area without faces\n"); - if (!(*backarea)->tmpfaces) Log_Print("AAS_SplitArea: back area without faces\n"); - - tmparea->invalid = true; -/* -#ifdef AW_DEBUG - for (i = 0, face = frontarea->tmpfaces; face; face = face->next[side]) - { - side = face->frontarea != frontarea->areanum; - i++; - } //end for - Log_Print("created front area %d with %d faces\n", frontarea->areanum, i); - - for (i = 0, face = backarea->tmpfaces; face; face = face->next[side]) - { - side = face->frontarea != backarea->areanum; - i++; - } //end for - Log_Print("created back area %d with %d faces\n", backarea->areanum, i); -#endif //AW_DEBUG*/ - - AAS_FlipAreaFaces((*frontarea)); - AAS_FlipAreaFaces((*backarea)); - // - AAS_CheckArea((*frontarea)); - AAS_CheckArea((*backarea)); -} //end of the function AAS_SplitArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_FindBestAreaSplitPlane(tmp_area_t *tmparea, vec3_t normal, float *dist) -{ - int side1, side2; - int foundsplitter, facesplits, groundsplits, epsilonfaces, bestepsilonfaces; - float bestvalue, value; - tmp_face_t *face1, *face2; - vec3_t tmpnormal, invgravity; - float tmpdist; - - //get inverse of gravity direction - VectorCopy(cfg.phys_gravitydirection, invgravity); - VectorInverse(invgravity); - - foundsplitter = false; - bestvalue = -999999; - bestepsilonfaces = 0; - // -#ifdef AW_DEBUG - Log_Print("finding split plane for area %d\n", tmparea->areanum); -#endif //AW_DEBUG - for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1]) - { - //side of the face the area is on - side1 = face1->frontarea != tmparea; - // - if (WindingIsTiny(face1->winding)) - { - Log_Write("gsubdiv: area %d has a tiny winding\r\n", tmparea->areanum); - continue; - } //end if - //if the face isn't a gap or ground there's no split edge - if (!(face1->faceflags & FACE_GROUND) && !AAS_GapFace(face1, side1)) continue; - // - for (face2 = face1->next[side1]; face2; face2 = face2->next[side2]) - { - //side of the face the area is on - side2 = face2->frontarea != tmparea; - // - if (WindingIsTiny(face1->winding)) - { - Log_Write("gsubdiv: area %d has a tiny winding\r\n", tmparea->areanum); - continue; - } //end if - //if the face isn't a gap or ground there's no split edge - if (!(face2->faceflags & FACE_GROUND) && !AAS_GapFace(face2, side2)) continue; - //only split between gaps and ground - if (!(((face1->faceflags & FACE_GROUND) && AAS_GapFace(face2, side2)) || - ((face2->faceflags & FACE_GROUND) && AAS_GapFace(face1, side1)))) continue; - //find a plane seperating the windings of the faces - if (!FindPlaneSeperatingWindings(face1->winding, face2->winding, invgravity, - tmpnormal, &tmpdist)) continue; -#ifdef AW_DEBUG - Log_Print("normal = \'%f %f %f\', dist = %f\n", - tmpnormal[0], tmpnormal[1], tmpnormal[2], tmpdist); -#endif //AW_DEBUG - //get metrics for this vertical plane - if (!AAS_TestSplitPlane(tmparea, tmpnormal, tmpdist, - &facesplits, &groundsplits, &epsilonfaces)) - { - continue; - } //end if -#ifdef AW_DEBUG - Log_Print("face splits = %d\nground splits = %d\n", - facesplits, groundsplits); -#endif //AW_DEBUG - value = 100 - facesplits - 2 * groundsplits; - //avoid epsilon faces - value += epsilonfaces * -1000; - if (value > bestvalue) - { - VectorCopy(tmpnormal, normal); - *dist = tmpdist; - bestvalue = value; - bestepsilonfaces = epsilonfaces; - foundsplitter = true; - } //end if - } //end for - } //end for - if (bestepsilonfaces) - { - Log_Write("found %d epsilon faces trying to split area %d\r\n", - epsilonfaces, tmparea->areanum); - } //end else - return foundsplitter; -} //end of the function AAS_FindBestAreaSplitPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_node_t *AAS_SubdivideArea_r(tmp_node_t *tmpnode) -{ - int planenum; - tmp_area_t *frontarea, *backarea; - tmp_node_t *tmpnode1, *tmpnode2; - vec3_t normal; - float dist; - - if (AAS_FindBestAreaSplitPlane(tmpnode->tmparea, normal, &dist)) - { - qprintf("\r%6d", ++numgravitationalsubdivisions); - // - planenum = FindFloatPlane(normal, dist); - //split the area - AAS_SplitArea(tmpnode->tmparea, planenum, &frontarea, &backarea); - // - tmpnode->tmparea = NULL; - tmpnode->planenum = FindFloatPlane(normal, dist); - // - tmpnode1 = AAS_AllocTmpNode(); - tmpnode1->planenum = 0; - tmpnode1->tmparea = frontarea; - // - tmpnode2 = AAS_AllocTmpNode(); - tmpnode2->planenum = 0; - tmpnode2->tmparea = backarea; - //subdivide the areas created by splitting recursively - tmpnode->children[0] = AAS_SubdivideArea_r(tmpnode1); - tmpnode->children[1] = AAS_SubdivideArea_r(tmpnode2); - } //end if - return tmpnode; -} //end of the function AAS_SubdivideArea_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_node_t *AAS_GravitationalSubdivision_r(tmp_node_t *tmpnode) -{ - //if this is a solid leaf - if (!tmpnode) return NULL; - //negative so it's an area - if (tmpnode->tmparea) return AAS_SubdivideArea_r(tmpnode); - //do the children recursively - tmpnode->children[0] = AAS_GravitationalSubdivision_r(tmpnode->children[0]); - tmpnode->children[1] = AAS_GravitationalSubdivision_r(tmpnode->children[1]); - return tmpnode; -} //end of the function AAS_GravitationalSubdivision_r -//=========================================================================== -// NOTE: merge faces and melt edges first -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_GravitationalSubdivision(void) -{ - Log_Write("AAS_GravitationalSubdivision\r\n"); - numgravitationalsubdivisions = 0; - qprintf("%6i gravitational subdivisions", numgravitationalsubdivisions); - //start with the head node - AAS_GravitationalSubdivision_r(tmpaasworld.nodes); - qprintf("\n"); - Log_Write("%6i gravitational subdivisions\r\n", numgravitationalsubdivisions); -} //end of the function AAS_GravitationalSubdivision -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_node_t *AAS_RefreshLadderSubdividedTree_r(tmp_node_t *tmpnode, tmp_area_t *tmparea, - tmp_node_t *tmpnode1, tmp_node_t *tmpnode2, int planenum) -{ - //if this is a solid leaf - if (!tmpnode) return NULL; - //negative so it's an area - if (tmpnode->tmparea) - { - if (tmpnode->tmparea == tmparea) - { - tmpnode->tmparea = NULL; - tmpnode->planenum = planenum; - tmpnode->children[0] = tmpnode1; - tmpnode->children[1] = tmpnode2; - } //end if - return tmpnode; - } //end if - //do the children recursively - tmpnode->children[0] = AAS_RefreshLadderSubdividedTree_r(tmpnode->children[0], - tmparea, tmpnode1, tmpnode2, planenum); - tmpnode->children[1] = AAS_RefreshLadderSubdividedTree_r(tmpnode->children[1], - tmparea, tmpnode1, tmpnode2, planenum); - return tmpnode; -} //end of the function AAS_RefreshLadderSubdividedTree_r -//=========================================================================== -// find an area with ladder faces and ground faces that are not connected -// split the area with a horizontal plane at the lowest vertex of all -// ladder faces in the area -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_node_t *AAS_LadderSubdivideArea_r(tmp_node_t *tmpnode) -{ - int side1, i, planenum; - int foundladderface, foundgroundface; - float dist; - tmp_area_t *tmparea, *frontarea, *backarea; - tmp_face_t *face1; - tmp_node_t *tmpnode1, *tmpnode2; - vec3_t lowestpoint, normal = {0, 0, 1}; - plane_t *plane; - winding_t *w; - - tmparea = tmpnode->tmparea; - //skip areas with a liquid - if (tmparea->contents & (AREACONTENTS_WATER - | AREACONTENTS_LAVA - | AREACONTENTS_SLIME)) return tmpnode; - //must be possible to stand in the area - if (!(tmparea->presencetype & PRESENCE_NORMAL)) return tmpnode; - // - foundladderface = false; - foundgroundface = false; - lowestpoint[2] = 99999; - // - for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1]) - { - //side of the face the area is on - side1 = face1->frontarea != tmparea; - //if the face is a ladder face - if (face1->faceflags & FACE_LADDER) - { - plane = &mapplanes[face1->planenum]; - //the ladder face plane should be pretty much vertical - if (DotProduct(plane->normal, normal) > -0.1) - { - foundladderface = true; - //find lowest point - for (i = 0; i < face1->winding->numpoints; i++) - { - if (face1->winding->p[i][2] < lowestpoint[2]) - { - VectorCopy(face1->winding->p[i], lowestpoint); - } //end if - } //end for - } //end if - } //end if - else if (face1->faceflags & FACE_GROUND) - { - foundgroundface = true; - } //end else if - } //end for - // - if ((!foundladderface) || (!foundgroundface)) return tmpnode; - // - for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1]) - { - //side of the face the area is on - side1 = face1->frontarea != tmparea; - //if the face isn't a ground face - if (!(face1->faceflags & FACE_GROUND)) continue; - //the ground plane - plane = &mapplanes[face1->planenum]; - //get the difference between the ground plane and the lowest point - dist = DotProduct(plane->normal, lowestpoint) - plane->dist; - //if the lowest point is very near one of the ground planes - if (dist > -1 && dist < 1) - { - return tmpnode; - } //end if - } //end for - // - dist = DotProduct(normal, lowestpoint); - planenum = FindFloatPlane(normal, dist); - // - w = AAS_SplitWinding(tmparea, planenum); - if (!w) return tmpnode; - FreeWinding(w); - //split the area with a horizontal plane through the lowest point - qprintf("\r%6d", ++numladdersubdivisions); - // - AAS_SplitArea(tmparea, planenum, &frontarea, &backarea); - // - tmpnode->tmparea = NULL; - tmpnode->planenum = planenum; - // - tmpnode1 = AAS_AllocTmpNode(); - tmpnode1->planenum = 0; - tmpnode1->tmparea = frontarea; - // - tmpnode2 = AAS_AllocTmpNode(); - tmpnode2->planenum = 0; - tmpnode2->tmparea = backarea; - //subdivide the areas created by splitting recursively - tmpnode->children[0] = AAS_LadderSubdivideArea_r(tmpnode1); - tmpnode->children[1] = AAS_LadderSubdivideArea_r(tmpnode2); - //refresh the tree - AAS_RefreshLadderSubdividedTree_r(tmpaasworld.nodes, tmparea, tmpnode1, tmpnode2, planenum); - // - return tmpnode; -} //end of the function AAS_LadderSubdivideArea_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_node_t *AAS_LadderSubdivision_r(tmp_node_t *tmpnode) -{ - //if this is a solid leaf - if (!tmpnode) return 0; - //negative so it's an area - if (tmpnode->tmparea) return AAS_LadderSubdivideArea_r(tmpnode); - //do the children recursively - tmpnode->children[0] = AAS_LadderSubdivision_r(tmpnode->children[0]); - tmpnode->children[1] = AAS_LadderSubdivision_r(tmpnode->children[1]); - return tmpnode; -} //end of the function AAS_LadderSubdivision_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_LadderSubdivision(void) -{ - Log_Write("AAS_LadderSubdivision\r\n"); - numladdersubdivisions = 0; - qprintf("%6i ladder subdivisions", numladdersubdivisions); - //start with the head node - AAS_LadderSubdivision_r(tmpaasworld.nodes); - // - qprintf("\n"); - Log_Write("%6i ladder subdivisions\r\n", numladdersubdivisions); -} //end of the function AAS_LadderSubdivision diff --git a/tools/quake3/bspc/aas_gsubdiv.h b/tools/quake3/bspc/aas_gsubdiv.h deleted file mode 100644 index bb2b0950..00000000 --- a/tools/quake3/bspc/aas_gsubdiv.h +++ /dev/null @@ -1,25 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -//works with the global tmpaasworld -void AAS_GravitationalSubdivision(void); -void AAS_LadderSubdivision(void); diff --git a/tools/quake3/bspc/aas_map.c b/tools/quake3/bspc/aas_map.c deleted file mode 100644 index 5fb49a3e..00000000 --- a/tools/quake3/bspc/aas_map.c +++ /dev/null @@ -1,849 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "l_mem.h" -#include "botlib/aasfile.h" //aas_bbox_t -#include "aas_store.h" //AAS_MAX_BBOXES -#include "aas_cfg.h" -#include "qcommon/surfaceflags.h" - -#define SPAWNFLAG_NOT_EASY 0x00000100 -#define SPAWNFLAG_NOT_MEDIUM 0x00000200 -#define SPAWNFLAG_NOT_HARD 0x00000400 -#define SPAWNFLAG_NOT_DEATHMATCH 0x00000800 -#define SPAWNFLAG_NOT_COOP 0x00001000 - -#define STATE_TOP 0 -#define STATE_BOTTOM 1 -#define STATE_UP 2 -#define STATE_DOWN 3 - -#define DOOR_START_OPEN 1 -#define DOOR_REVERSE 2 -#define DOOR_CRUSHER 4 -#define DOOR_NOMONSTER 8 -#define DOOR_TOGGLE 32 -#define DOOR_X_AXIS 64 -#define DOOR_Y_AXIS 128 - -#define BBOX_NORMAL_EPSILON 0.0001 - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -vec_t BoxOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs, int side) -{ - vec3_t v1, v2; - int i; - - if (side) - { - for (i = 0; i < 3; i++) - { - if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = maxs[i]; - else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = mins[i]; - else v1[i] = 0; - } //end for - } //end if - else - { - for (i = 0; i < 3; i++) - { - if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = mins[i]; - else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = maxs[i]; - else v1[i] = 0; - } //end for - } //end else - VectorCopy(normal, v2); - VectorInverse(v2); - return DotProduct(v1, v2); -} //end of the function BoxOriginDistanceFromPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -vec_t CapsuleOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs) -{ - float offset_up, offset_down, width, radius; - - width = maxs[0] - mins[0]; - // if the box is less high then it is wide - if (maxs[2] - mins[2] < width) { - width = maxs[2] - mins[2]; - } - radius = width * 0.5; - // offset to upper and lower sphere - offset_up = maxs[2] - radius; - offset_down = -mins[2] - radius; - - // if normal points upward - if ( normal[2] > 0 ) { - // touches lower sphere first - return normal[2] * offset_down + radius; - } - else { - // touched upper sphere first - return -normal[2] * offset_up + radius; - } -} -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ExpandMapBrush(mapbrush_t *brush, vec3_t mins, vec3_t maxs) -{ - int sn; - float dist; - side_t *s; - plane_t *plane; - - for (sn = 0; sn < brush->numsides; sn++) - { - s = brush->original_sides + sn; - plane = &mapplanes[s->planenum]; - dist = plane->dist; - if (capsule_collision) { - dist += CapsuleOriginDistanceFromPlane(plane->normal, mins, maxs); - } - else { - dist += BoxOriginDistanceFromPlane(plane->normal, mins, maxs, 0); - } - s->planenum = FindFloatPlane(plane->normal, dist); - //the side isn't a bevel after expanding - s->flags &= ~SFL_BEVEL; - //don't skip the surface - s->surf &= ~SURF_SKIP; - //make sure the texinfo is not TEXINFO_NODE - //when player clip contents brushes are read from the bsp tree - //they have the texinfo field set to TEXINFO_NODE - //s->texinfo = 0; - } //end for -} //end of the function AAS_ExpandMapBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_SetTexinfo(mapbrush_t *brush) -{ - int n; - side_t *side; - - if (brush->contents & (CONTENTS_LADDER - | CONTENTS_AREAPORTAL - | CONTENTS_CLUSTERPORTAL - | CONTENTS_TELEPORTER - | CONTENTS_JUMPPAD - | CONTENTS_DONOTENTER - | CONTENTS_WATER - | CONTENTS_LAVA - | CONTENTS_SLIME - | CONTENTS_WINDOW - | CONTENTS_PLAYERCLIP)) - { - //we just set texinfo to 0 because these brush sides MUST be used as - //bsp splitters textured or not textured - for (n = 0; n < brush->numsides; n++) - { - side = brush->original_sides + n; - //side->flags |= SFL_TEXTURED|SFL_VISIBLE; - side->texinfo = 0; - } //end for - } //end if - else - { - //only use brush sides as splitters if they are textured - //texinfo of non-textured sides will be set to TEXINFO_NODE - for (n = 0; n < brush->numsides; n++) - { - side = brush->original_sides + n; - //don't use side as splitter (set texinfo to TEXINFO_NODE) if not textured - if (side->flags & (SFL_TEXTURED|SFL_BEVEL)) side->texinfo = 0; - else side->texinfo = TEXINFO_NODE; - } //end for - } //end else -} //end of the function AAS_SetTexinfo -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeBrushWindings(mapbrush_t *brush) -{ - int n; - side_t *side; - // - for (n = 0; n < brush->numsides; n++) - { - side = brush->original_sides + n; - // - if (side->winding) FreeWinding(side->winding); - } //end for -} //end of the function FreeBrushWindings -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_AddMapBrushSide(mapbrush_t *brush, int planenum) -{ - side_t *side; - // - if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES) - Error ("MAX_MAPFILE_BRUSHSIDES"); - // - side = brush->original_sides + brush->numsides; - side->original = NULL; - side->winding = NULL; - side->contents = brush->contents; - side->flags &= ~(SFL_BEVEL|SFL_VISIBLE); - side->surf = 0; - side->planenum = planenum; - side->texinfo = 0; - // - nummapbrushsides++; - brush->numsides++; -} //end of the function AAS_AddMapBrushSide -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FixMapBrush(mapbrush_t *brush) -{ - int i, j, planenum; - float dist; - winding_t *w; - plane_t *plane, *plane1, *plane2; - side_t *side; - vec3_t normal; - - //calculate the brush bounds - ClearBounds(brush->mins, brush->maxs); - for (i = 0; i < brush->numsides; i++) - { - plane = &mapplanes[brush->original_sides[i].planenum]; - w = BaseWindingForPlane(plane->normal, plane->dist); - for (j = 0; j < brush->numsides && w; j++) - { - if (i == j) continue; - //there are no brush bevels marked but who cares :) - if (brush->original_sides[j].flags & SFL_BEVEL) continue; - plane = &mapplanes[brush->original_sides[j].planenum^1]; - ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); - } //end for - - side = &brush->original_sides[i]; - side->winding = w; - if (w) - { - for (j = 0; j < w->numpoints; j++) - { - AddPointToBounds(w->p[j], brush->mins, brush->maxs); - } //end for - } //end if - } //end for - // - for (i = 0; i < brush->numsides; i++) - { - for (j = 0; j < brush->numsides; j++) - { - if (i == j) continue; - plane1 = &mapplanes[brush->original_sides[i].planenum]; - plane2 = &mapplanes[brush->original_sides[j].planenum]; - if (WindingsNonConvex(brush->original_sides[i].winding, - brush->original_sides[j].winding, - plane1->normal, plane2->normal, - plane1->dist, plane2->dist)) - { - Log_Print("non convex brush"); - } //end if - } //end for - } //end for - - //NOW close the fucking brush!! - for (i = 0; i < 3; i++) - { - if (brush->mins[i] < -MAX_MAP_BOUNDS) - { - VectorClear(normal); - normal[i] = -1; - dist = MAX_MAP_BOUNDS - 10; - planenum = FindFloatPlane(normal, dist); - // - Log_Print("mins out of range: added extra brush side\n"); - AAS_AddMapBrushSide(brush, planenum); - } //end if - if (brush->maxs[i] > MAX_MAP_BOUNDS) - { - VectorClear(normal); - normal[i] = 1; - dist = MAX_MAP_BOUNDS - 10; - planenum = FindFloatPlane(normal, dist); - // - Log_Print("maxs out of range: added extra brush side\n"); - AAS_AddMapBrushSide(brush, planenum); - } //end if - if (brush->mins[i] > MAX_MAP_BOUNDS || brush->maxs[i] < -MAX_MAP_BOUNDS) - { - Log_Print("entity %i, brush %i: no visible sides on brush\n", brush->entitynum, brush->brushnum); - } //end if - } //end for - //free all the windings - FreeBrushWindings(brush); -} //end of the function AAS_FixMapBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_MakeBrushWindings(mapbrush_t *ob) -{ - int i, j; - winding_t *w; - side_t *side; - plane_t *plane, *plane1, *plane2; - - ClearBounds (ob->mins, ob->maxs); - - for (i = 0; i < ob->numsides; i++) - { - plane = &mapplanes[ob->original_sides[i].planenum]; - w = BaseWindingForPlane(plane->normal, plane->dist); - for (j = 0; j numsides && w; j++) - { - if (i == j) continue; - if (ob->original_sides[j].flags & SFL_BEVEL) continue; - plane = &mapplanes[ob->original_sides[j].planenum^1]; - ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); - } - - side = &ob->original_sides[i]; - side->winding = w; - if (w) - { - side->flags |= SFL_VISIBLE; - for (j = 0; j < w->numpoints; j++) - AddPointToBounds (w->p[j], ob->mins, ob->maxs); - } - } - //check if the brush is convex - for (i = 0; i < ob->numsides; i++) - { - for (j = 0; j < ob->numsides; j++) - { - if (i == j) continue; - plane1 = &mapplanes[ob->original_sides[i].planenum]; - plane2 = &mapplanes[ob->original_sides[j].planenum]; - if (WindingsNonConvex(ob->original_sides[i].winding, - ob->original_sides[j].winding, - plane1->normal, plane2->normal, - plane1->dist, plane2->dist)) - { - Log_Print("non convex brush"); - } //end if - } //end for - } //end for - //check for out of bound brushes - for (i = 0; i < 3; i++) - { - //IDBUG: all the indexes into the mins and maxs were zero (not using i) - if (ob->mins[i] < -MAX_MAP_BOUNDS || ob->maxs[i] > MAX_MAP_BOUNDS) - { - Log_Print("entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum); - Log_Print("ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, ob->mins[i], i, ob->maxs[i]); - ob->numsides = 0; //remove the brush - break; - } //end if - if (ob->mins[i] > MAX_MAP_BOUNDS || ob->maxs[i] < -MAX_MAP_BOUNDS) - { - Log_Print("entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum); - Log_Print("ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, ob->mins[i], i, ob->maxs[i]); - ob->numsides = 0; //remove the brush - break; - } //end if - } //end for - return true; -} //end of the function AAS_MakeBrushWindings -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -mapbrush_t *AAS_CopyMapBrush(mapbrush_t *brush, entity_t *mapent) -{ - int n; - mapbrush_t *newbrush; - side_t *side, *newside; - - if (nummapbrushes >= MAX_MAPFILE_BRUSHES) - Error ("MAX_MAPFILE_BRUSHES"); - - newbrush = &mapbrushes[nummapbrushes]; - newbrush->original_sides = &brushsides[nummapbrushsides]; - newbrush->entitynum = brush->entitynum; - newbrush->brushnum = nummapbrushes - mapent->firstbrush; - newbrush->numsides = brush->numsides; - newbrush->contents = brush->contents; - - //copy the sides - for (n = 0; n < brush->numsides; n++) - { - if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES) - Error ("MAX_MAPFILE_BRUSHSIDES"); - side = brush->original_sides + n; - - newside = newbrush->original_sides + n; - newside->original = NULL; - newside->winding = NULL; - newside->contents = side->contents; - newside->flags = side->flags; - newside->surf = side->surf; - newside->planenum = side->planenum; - newside->texinfo = side->texinfo; - nummapbrushsides++; - } //end for - // - nummapbrushes++; - mapent->numbrushes++; - return newbrush; -} //end of the function AAS_CopyMapBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int mark_entities[MAX_MAP_ENTITIES]; - -int AAS_AlwaysTriggered_r(char *targetname) -{ - int i; - - if (!strlen(targetname)) { - return false; - } - // - for (i = 0; i < num_entities; i++) { - // if the entity will activate the given targetname - if ( !strcmp(targetname, ValueForKey(&entities[i], "target")) ) { - // if this activator is present in deathmatch - if (!(atoi(ValueForKey(&entities[i], "spawnflags")) & SPAWNFLAG_NOT_DEATHMATCH)) { - // if it is a trigger_always entity - if (!strcmp("trigger_always", ValueForKey(&entities[i], "classname"))) { - return true; - } - // check for possible trigger_always entities activating this entity - if ( mark_entities[i] ) { - Warning( "entity %d, classname %s has recursive targetname %s\n", i, - ValueForKey(&entities[i], "classname"), targetname ); - return false; - } - mark_entities[i] = true; - if ( AAS_AlwaysTriggered_r(ValueForKey(&entities[i], "targetname")) ) { - return true; - } - } - } - } - return false; -} - -int AAS_AlwaysTriggered(char *targetname) { - memset( mark_entities, 0, sizeof(mark_entities) ); - return AAS_AlwaysTriggered_r( targetname ); -} -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_ValidEntity(entity_t *mapent) -{ - int i; - char target[1024]; - - //all world brushes are used for AAS - if (mapent == &entities[0]) - { - return true; - } //end if - //some of the func_wall brushes are also used for AAS - else if (!strcmp("func_wall", ValueForKey(mapent, "classname"))) - { - //Log_Print("found func_wall entity %d\n", mapent - entities); - //if the func wall is used in deathmatch - if (!(atoi(ValueForKey(mapent, "spawnflags")) & SPAWNFLAG_NOT_DEATHMATCH)) - { - //Log_Print("func_wall USED in deathmatch mode %d\n", atoi(ValueForKey(mapent, "spawnflags"))); - return true; - } //end if - } //end else if - else if (!strcmp("func_door_rotating", ValueForKey(mapent, "classname"))) - { - //if the func_door_rotating is present in deathmatch - if (!(atoi(ValueForKey(mapent, "spawnflags")) & SPAWNFLAG_NOT_DEATHMATCH)) - { - //if the func_door_rotating is always activated in deathmatch - if (AAS_AlwaysTriggered(ValueForKey(mapent, "targetname"))) - { - //Log_Print("found func_door_rotating in deathmatch\ntargetname %s\n", ValueForKey(mapent, "targetname")); - return true; - } //end if - } //end if - } //end else if - else if (!strcmp("trigger_hurt", ValueForKey(mapent, "classname"))) - { - //"dmg" is the damage, for instance: "dmg" "666" - return true; - } //end else if - else if (!strcmp("trigger_push", ValueForKey(mapent, "classname"))) - { - return true; - } //end else if - else if (!strcmp("trigger_multiple", ValueForKey(mapent, "classname"))) - { - //find out if the trigger_multiple is pointing to a target_teleporter - strcpy(target, ValueForKey(mapent, "target")); - for (i = 0; i < num_entities; i++) - { - //if the entity will activate the given targetname - if (!strcmp(target, ValueForKey(&entities[i], "targetname"))) - { - if (!strcmp("target_teleporter", ValueForKey(&entities[i], "classname"))) - { - return true; - } //end if - } //end if - } //end for - } //end else if - else if (!strcmp("trigger_teleport", ValueForKey(mapent, "classname"))) - { - return true; - } //end else if - else if (!strcmp("func_static", ValueForKey(mapent, "classname"))) - { - //FIXME: easy/medium/hard/deathmatch specific? - return true; - } //end else if - else if (!strcmp("func_door", ValueForKey(mapent, "classname"))) - { - return true; - } //end else if - return false; -} //end of the function AAS_ValidEntity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_TransformPlane(int planenum, vec3_t origin, vec3_t angles) -{ - float newdist, matrix[3][3]; - vec3_t normal; - - //rotate the node plane - VectorCopy(mapplanes[planenum].normal, normal); - CreateRotationMatrix(angles, matrix); - RotatePoint(normal, matrix); - newdist = mapplanes[planenum].dist + DotProduct(normal, origin); - return FindFloatPlane(normal, newdist); -} //end of the function AAS_TransformPlane -//=========================================================================== -// this function sets the func_rotating_door in it's final position -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_PositionFuncRotatingBrush(entity_t *mapent, mapbrush_t *brush) -{ - int spawnflags, i; - float distance; - vec3_t movedir, angles, pos1, pos2; - side_t *s; - - spawnflags = FloatForKey(mapent, "spawnflags"); - VectorClear(movedir); - if (spawnflags & DOOR_X_AXIS) - movedir[2] = 1.0; //roll - else if (spawnflags & DOOR_Y_AXIS) - movedir[0] = 1.0; //pitch - else // Z_AXIS - movedir[1] = 1.0; //yaw - - // check for reverse rotation - if (spawnflags & DOOR_REVERSE) - VectorInverse(movedir); - - distance = FloatForKey(mapent, "distance"); - if (!distance) distance = 90; - - GetVectorForKey(mapent, "angles", angles); - VectorCopy(angles, pos1); - VectorMA(angles, -distance, movedir, pos2); - // if it starts open, switch the positions - if (spawnflags & DOOR_START_OPEN) - { - VectorCopy(pos2, angles); - VectorCopy(pos1, pos2); - VectorCopy(angles, pos1); - VectorInverse(movedir); - } //end if - // - for (i = 0; i < brush->numsides; i++) - { - s = &brush->original_sides[i]; - s->planenum = AAS_TransformPlane(s->planenum, mapent->origin, pos2); - } //end for - // - FreeBrushWindings(brush); - AAS_MakeBrushWindings(brush); - AddBrushBevels(brush); - FreeBrushWindings(brush); -} //end of the function AAS_PositionFuncRotatingBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_PositionBrush(entity_t *mapent, mapbrush_t *brush) -{ - side_t *s; - float newdist; - int i, notteam; - char *model; - - if (!strcmp(ValueForKey(mapent, "classname"), "func_door_rotating")) - { - AAS_PositionFuncRotatingBrush(mapent, brush); - } //end if - else - { - if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2]) - { - for (i = 0; i < brush->numsides; i++) - { - s = &brush->original_sides[i]; - newdist = mapplanes[s->planenum].dist + - DotProduct(mapplanes[s->planenum].normal, mapent->origin); - s->planenum = FindFloatPlane(mapplanes[s->planenum].normal, newdist); - } //end for - } //end if - //if it's a trigger hurt - if (!strcmp("trigger_hurt", ValueForKey(mapent, "classname"))) - { - notteam = FloatForKey(mapent, "bot_notteam"); - if ( notteam == 1 ) { - brush->contents |= CONTENTS_NOTTEAM1; - } - else if ( notteam == 2 ) { - brush->contents |= CONTENTS_NOTTEAM2; - } - else { - // always avoid so set lava contents - brush->contents |= CONTENTS_LAVA; - } - } //end if - // - else if (!strcmp("trigger_push", ValueForKey(mapent, "classname"))) - { - //set the jumppad contents - brush->contents = CONTENTS_JUMPPAD; - //Log_Print("found trigger_push brush\n"); - } //end if - // - else if (!strcmp("trigger_multiple", ValueForKey(mapent, "classname"))) - { - //set teleporter contents - brush->contents = CONTENTS_TELEPORTER; - //Log_Print("found trigger_multiple teleporter brush\n"); - } //end if - // - else if (!strcmp("trigger_teleport", ValueForKey(mapent, "classname"))) - { - //set teleporter contents - brush->contents = CONTENTS_TELEPORTER; - //Log_Print("found trigger_teleport teleporter brush\n"); - } //end if - else if (!strcmp("func_door", ValueForKey(mapent, "classname"))) - { - //set mover contents - brush->contents = CONTENTS_MOVER; - //get the model number - model = ValueForKey(mapent, "model"); - brush->modelnum = atoi(model+1); - } //end if - } //end else -} //end of the function AAS_PositionBrush -//=========================================================================== -// uses the global cfg_t cfg -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CreateMapBrushes(mapbrush_t *brush, entity_t *mapent, int addbevels) -{ - int i; - //side_t *s; - mapbrush_t *bboxbrushes[16]; - - //if the brushes are not from an entity used for AAS - if (!AAS_ValidEntity(mapent)) - { - nummapbrushsides -= brush->numsides; - brush->numsides = 0; - return; - } //end if - // - AAS_PositionBrush(mapent, brush); - //from all normal solid brushes only the textured brush sides will - //be used as bsp splitters, so set the right texinfo reference here - AAS_SetTexinfo(brush); - //remove contents detail flag, otherwise player clip contents won't be - //bsped correctly for AAS! - brush->contents &= ~CONTENTS_DETAIL; - //if the brush has contents area portal it should be the only contents - if (brush->contents & (CONTENTS_AREAPORTAL|CONTENTS_CLUSTERPORTAL)) - { - brush->contents = CONTENTS_CLUSTERPORTAL; - brush->leafnum = -1; - } //end if - //window and playerclip are used for player clipping, make them solid - if (brush->contents & (CONTENTS_WINDOW | CONTENTS_PLAYERCLIP)) - { - // - brush->contents &= ~(CONTENTS_WINDOW | CONTENTS_PLAYERCLIP); - brush->contents |= CONTENTS_SOLID; - brush->leafnum = -1; - } //end if - // - if (brush->contents & CONTENTS_BOTCLIP) - { - brush->contents = CONTENTS_SOLID; - brush->leafnum = -1; - } //end if - // - //Log_Write("brush %d contents = ", brush->brushnum); - //PrintContents(brush->contents); - //Log_Write("\r\n"); - //if not one of the following brushes then the brush is NOT used for AAS - if (!(brush->contents & (CONTENTS_SOLID - | CONTENTS_LADDER - | CONTENTS_CLUSTERPORTAL - | CONTENTS_DONOTENTER - | CONTENTS_TELEPORTER - | CONTENTS_JUMPPAD - | CONTENTS_WATER - | CONTENTS_LAVA - | CONTENTS_SLIME - | CONTENTS_MOVER - ))) - { - nummapbrushsides -= brush->numsides; - brush->numsides = 0; - return; - } //end if - //fix the map brush - //AAS_FixMapBrush(brush); - //if brush bevels should be added (for real map brushes, not bsp map brushes) - if (addbevels) - { - //NOTE: we first have to get the mins and maxs of the brush before - // creating the brush bevels... the mins and maxs are used to - // create them. so we call MakeBrushWindings to get the mins - // and maxs and then after creating the bevels we free the - // windings because they are created for all sides (including - // bevels) a little later - AAS_MakeBrushWindings(brush); - AddBrushBevels(brush); - FreeBrushWindings(brush); - } //end if - //NOTE: add the brush to the WORLD entity!!! - mapent = &entities[0]; - //there's at least one new brush for now - nummapbrushes++; - mapent->numbrushes++; - //liquid brushes are expanded for the maximum possible bounding box - if (brush->contents & (CONTENTS_WATER - | CONTENTS_LAVA - | CONTENTS_SLIME - | CONTENTS_TELEPORTER - | CONTENTS_JUMPPAD - | CONTENTS_DONOTENTER - | CONTENTS_MOVER - )) - { - brush->expansionbbox = 0; - //NOTE: the first bounding box is the max - //FIXME: use max bounding box created from all bboxes - AAS_ExpandMapBrush(brush, cfg.bboxes[0].mins, cfg.bboxes[0].maxs); - AAS_MakeBrushWindings(brush); - } //end if - //area portal brushes are NOT expanded - else if (brush->contents & CONTENTS_CLUSTERPORTAL) - { - brush->expansionbbox = 0; - //NOTE: the first bounding box is the max - //FIXME: use max bounding box created from all bboxes - AAS_ExpandMapBrush(brush, cfg.bboxes[0].mins, cfg.bboxes[0].maxs); - AAS_MakeBrushWindings(brush); - } //end if - //all solid brushes are expanded for all bounding boxes - else if (brush->contents & (CONTENTS_SOLID - | CONTENTS_LADDER - )) - { - //brush for the first bounding box - bboxbrushes[0] = brush; - //make a copy for the other bounding boxes - for (i = 1; i < cfg.numbboxes; i++) - { - bboxbrushes[i] = AAS_CopyMapBrush(brush, mapent); - } //end for - //expand every brush for it's bounding box and create windings - for (i = 0; i < cfg.numbboxes; i++) - { - AAS_ExpandMapBrush(bboxbrushes[i], cfg.bboxes[i].mins, cfg.bboxes[i].maxs); - bboxbrushes[i]->expansionbbox = cfg.bboxes[i].presencetype; - AAS_MakeBrushWindings(bboxbrushes[i]); - } //end for - } //end else -} //end of the function AAS_CreateMapBrushes diff --git a/tools/quake3/bspc/aas_map.h b/tools/quake3/bspc/aas_map.h deleted file mode 100644 index 181f7119..00000000 --- a/tools/quake3/bspc/aas_map.h +++ /dev/null @@ -1,23 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -void AAS_CreateMapBrushes(mapbrush_t *brush, entity_t *mapent, int addbevels); diff --git a/tools/quake3/bspc/aas_prunenodes.c b/tools/quake3/bspc/aas_prunenodes.c deleted file mode 100644 index 522fa3d0..00000000 --- a/tools/quake3/bspc/aas_prunenodes.c +++ /dev/null @@ -1,89 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "botlib/aasfile.h" -#include "aas_create.h" - -int c_numprunes; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tmp_node_t *AAS_PruneNodes_r(tmp_node_t *tmpnode) -{ - tmp_area_t *tmparea1, *tmparea2; - - //if it is a solid leaf - if (!tmpnode) return NULL; - // - if (tmpnode->tmparea) return tmpnode; - //process the children first - tmpnode->children[0] = AAS_PruneNodes_r(tmpnode->children[0]); - tmpnode->children[1] = AAS_PruneNodes_r(tmpnode->children[1]); - //if both children are areas - if (tmpnode->children[0] && tmpnode->children[1] && - tmpnode->children[0]->tmparea && tmpnode->children[1]->tmparea) - { - tmparea1 = tmpnode->children[0]->tmparea; - while(tmparea1->mergedarea) tmparea1 = tmparea1->mergedarea; - - tmparea2 = tmpnode->children[1]->tmparea; - while(tmparea2->mergedarea) tmparea2 = tmparea2->mergedarea; - - if (tmparea1 == tmparea2) - { - c_numprunes++; - tmpnode->tmparea = tmparea1; - tmpnode->planenum = 0; - AAS_FreeTmpNode(tmpnode->children[0]); - AAS_FreeTmpNode(tmpnode->children[1]); - tmpnode->children[0] = NULL; - tmpnode->children[1] = NULL; - return tmpnode; - } //end if - } //end if - //if both solid leafs - if (!tmpnode->children[0] && !tmpnode->children[1]) - { - c_numprunes++; - AAS_FreeTmpNode(tmpnode); - return NULL; - } //end if - // - return tmpnode; -} //end of the function AAS_PruneNodes_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_PruneNodes(void) -{ - Log_Write("AAS_PruneNodes\r\n"); - AAS_PruneNodes_r(tmpaasworld.nodes); - Log_Print("%6d nodes pruned\r\n", c_numprunes); -} //end of the function AAS_PruneNodes diff --git a/tools/quake3/bspc/aas_prunenodes.h b/tools/quake3/bspc/aas_prunenodes.h deleted file mode 100644 index 9433864f..00000000 --- a/tools/quake3/bspc/aas_prunenodes.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -void AAS_PruneNodes(void); - diff --git a/tools/quake3/bspc/aas_store.c b/tools/quake3/bspc/aas_store.c deleted file mode 100644 index 17556b89..00000000 --- a/tools/quake3/bspc/aas_store.c +++ /dev/null @@ -1,1082 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "botlib/aasfile.h" -#include "aas_file.h" -#include "aas_store.h" -#include "aas_create.h" -#include "aas_cfg.h" - - -//#define NOTHREEVERTEXFACES - -#define STOREPLANESDOUBLE - -#define VERTEX_EPSILON 0.1 //NOTE: changed from 0.5 -#define DIST_EPSILON 0.05 //NOTE: changed from 0.9 -#define NORMAL_EPSILON 0.0001 //NOTE: changed from 0.005 -#define INTEGRAL_EPSILON 0.01 - -#define VERTEX_HASHING -#define VERTEX_HASH_SHIFT 7 -#define VERTEX_HASH_SIZE ((MAX_MAP_BOUNDS>>(VERTEX_HASH_SHIFT-1))+1) //was 64 -// -#define PLANE_HASHING -#define PLANE_HASH_SIZE 1024 //must be power of 2 -// -#define EDGE_HASHING -#define EDGE_HASH_SIZE 1024 //must be power of 2 - -aas_t aasworld; - -//vertex hash -int *aas_vertexchain; // the next vertex in a hash chain -int aas_hashverts[VERTEX_HASH_SIZE*VERTEX_HASH_SIZE]; // a vertex number, or 0 for no verts -//plane hash -int *aas_planechain; -int aas_hashplanes[PLANE_HASH_SIZE]; -//edge hash -int *aas_edgechain; -int aas_hashedges[EDGE_HASH_SIZE]; - -int allocatedaasmem = 0; - -int groundfacesonly = false;//true; -// -typedef struct max_aas_s -{ - int max_bboxes; - int max_vertexes; - int max_planes; - int max_edges; - int max_edgeindexsize; - int max_faces; - int max_faceindexsize; - int max_areas; - int max_areasettings; - int max_reachabilitysize; - int max_nodes; - int max_portals; - int max_portalindexsize; - int max_clusters; -} max_aas_t; -//maximums of everything -max_aas_t max_aas; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_CountTmpNodes(tmp_node_t *tmpnode) -{ - if (!tmpnode) return 0; - return AAS_CountTmpNodes(tmpnode->children[0]) + - AAS_CountTmpNodes(tmpnode->children[1]) + 1; -} //end of the function AAS_CountTmpNodes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitMaxAAS(void) -{ - int numfaces, numpoints, numareas; - tmp_face_t *f; - tmp_area_t *a; - - numpoints = 0; - numfaces = 0; - for (f = tmpaasworld.faces; f; f = f->l_next) - { - numfaces++; - if (f->winding) numpoints += f->winding->numpoints; - } //end for - // - numareas = 0; - for (a = tmpaasworld.areas; a; a = a->l_next) - { - numareas++; - } //end for - max_aas.max_bboxes = AAS_MAX_BBOXES; - max_aas.max_vertexes = numpoints + 1; - max_aas.max_planes = nummapplanes; - max_aas.max_edges = numpoints + 1; - max_aas.max_edgeindexsize = (numpoints + 1) * 3; - max_aas.max_faces = numfaces + 10; - max_aas.max_faceindexsize = (numfaces + 10) * 2; - max_aas.max_areas = numareas + 10; - max_aas.max_areasettings = numareas + 10; - max_aas.max_reachabilitysize = 0; - max_aas.max_nodes = AAS_CountTmpNodes(tmpaasworld.nodes) + 10; - max_aas.max_portals = 0; - max_aas.max_portalindexsize = 0; - max_aas.max_clusters = 0; -} //end of the function AAS_InitMaxAAS -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_AllocMaxAAS(void) -{ - int i; - - AAS_InitMaxAAS(); - //bounding boxes - aasworld.numbboxes = 0; - aasworld.bboxes = (aas_bbox_t *) GetClearedMemory(max_aas.max_bboxes * sizeof(aas_bbox_t)); - allocatedaasmem += max_aas.max_bboxes * sizeof(aas_bbox_t); - //vertexes - aasworld.numvertexes = 0; - aasworld.vertexes = (aas_vertex_t *) GetClearedMemory(max_aas.max_vertexes * sizeof(aas_vertex_t)); - allocatedaasmem += max_aas.max_vertexes * sizeof(aas_vertex_t); - //planes - aasworld.numplanes = 0; - aasworld.planes = (aas_plane_t *) GetClearedMemory(max_aas.max_planes * sizeof(aas_plane_t)); - allocatedaasmem += max_aas.max_planes * sizeof(aas_plane_t); - //edges - aasworld.numedges = 0; - aasworld.edges = (aas_edge_t *) GetClearedMemory(max_aas.max_edges * sizeof(aas_edge_t)); - allocatedaasmem += max_aas.max_edges * sizeof(aas_edge_t); - //edge index - aasworld.edgeindexsize = 0; - aasworld.edgeindex = (aas_edgeindex_t *) GetClearedMemory(max_aas.max_edgeindexsize * sizeof(aas_edgeindex_t)); - allocatedaasmem += max_aas.max_edgeindexsize * sizeof(aas_edgeindex_t); - //faces - aasworld.numfaces = 0; - aasworld.faces = (aas_face_t *) GetClearedMemory(max_aas.max_faces * sizeof(aas_face_t)); - allocatedaasmem += max_aas.max_faces * sizeof(aas_face_t); - //face index - aasworld.faceindexsize = 0; - aasworld.faceindex = (aas_faceindex_t *) GetClearedMemory(max_aas.max_faceindexsize * sizeof(aas_faceindex_t)); - allocatedaasmem += max_aas.max_faceindexsize * sizeof(aas_faceindex_t); - //convex areas - aasworld.numareas = 0; - aasworld.areas = (aas_area_t *) GetClearedMemory(max_aas.max_areas * sizeof(aas_area_t)); - allocatedaasmem += max_aas.max_areas * sizeof(aas_area_t); - //convex area settings - aasworld.numareasettings = 0; - aasworld.areasettings = (aas_areasettings_t *) GetClearedMemory(max_aas.max_areasettings * sizeof(aas_areasettings_t)); - allocatedaasmem += max_aas.max_areasettings * sizeof(aas_areasettings_t); - //reachablity list - aasworld.reachabilitysize = 0; - aasworld.reachability = (aas_reachability_t *) GetClearedMemory(max_aas.max_reachabilitysize * sizeof(aas_reachability_t)); - allocatedaasmem += max_aas.max_reachabilitysize * sizeof(aas_reachability_t); - //nodes of the bsp tree - aasworld.numnodes = 0; - aasworld.nodes = (aas_node_t *) GetClearedMemory(max_aas.max_nodes * sizeof(aas_node_t)); - allocatedaasmem += max_aas.max_nodes * sizeof(aas_node_t); - //cluster portals - aasworld.numportals = 0; - aasworld.portals = (aas_portal_t *) GetClearedMemory(max_aas.max_portals * sizeof(aas_portal_t)); - allocatedaasmem += max_aas.max_portals * sizeof(aas_portal_t); - //cluster portal index - aasworld.portalindexsize = 0; - aasworld.portalindex = (aas_portalindex_t *) GetClearedMemory(max_aas.max_portalindexsize * sizeof(aas_portalindex_t)); - allocatedaasmem += max_aas.max_portalindexsize * sizeof(aas_portalindex_t); - //cluster - aasworld.numclusters = 0; - aasworld.clusters = (aas_cluster_t *) GetClearedMemory(max_aas.max_clusters * sizeof(aas_cluster_t)); - allocatedaasmem += max_aas.max_clusters * sizeof(aas_cluster_t); - // - Log_Print("allocated "); - PrintMemorySize(allocatedaasmem); - Log_Print(" of AAS memory\n"); - //reset the has stuff - aas_vertexchain = (int *) GetClearedMemory(max_aas.max_vertexes * sizeof(int)); - aas_planechain = (int *) GetClearedMemory(max_aas.max_planes * sizeof(int)); - aas_edgechain = (int *) GetClearedMemory(max_aas.max_edges * sizeof(int)); - // - for (i = 0; i < max_aas.max_vertexes; i++) aas_vertexchain[i] = -1; - for (i = 0; i < VERTEX_HASH_SIZE * VERTEX_HASH_SIZE; i++) aas_hashverts[i] = -1; - // - for (i = 0; i < max_aas.max_planes; i++) aas_planechain[i] = -1; - for (i = 0; i < PLANE_HASH_SIZE; i++) aas_hashplanes[i] = -1; - // - for (i = 0; i < max_aas.max_edges; i++) aas_edgechain[i] = -1; - for (i = 0; i < EDGE_HASH_SIZE; i++) aas_hashedges[i] = -1; -} //end of the function AAS_AllocMaxAAS -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeMaxAAS(void) -{ - //bounding boxes - if (aasworld.bboxes) FreeMemory(aasworld.bboxes); - aasworld.bboxes = NULL; - aasworld.numbboxes = 0; - //vertexes - if (aasworld.vertexes) FreeMemory(aasworld.vertexes); - aasworld.vertexes = NULL; - aasworld.numvertexes = 0; - //planes - if (aasworld.planes) FreeMemory(aasworld.planes); - aasworld.planes = NULL; - aasworld.numplanes = 0; - //edges - if (aasworld.edges) FreeMemory(aasworld.edges); - aasworld.edges = NULL; - aasworld.numedges = 0; - //edge index - if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex); - aasworld.edgeindex = NULL; - aasworld.edgeindexsize = 0; - //faces - if (aasworld.faces) FreeMemory(aasworld.faces); - aasworld.faces = NULL; - aasworld.numfaces = 0; - //face index - if (aasworld.faceindex) FreeMemory(aasworld.faceindex); - aasworld.faceindex = NULL; - aasworld.faceindexsize = 0; - //convex areas - if (aasworld.areas) FreeMemory(aasworld.areas); - aasworld.areas = NULL; - aasworld.numareas = 0; - //convex area settings - if (aasworld.areasettings) FreeMemory(aasworld.areasettings); - aasworld.areasettings = NULL; - aasworld.numareasettings = 0; - //reachablity list - if (aasworld.reachability) FreeMemory(aasworld.reachability); - aasworld.reachability = NULL; - aasworld.reachabilitysize = 0; - //nodes of the bsp tree - if (aasworld.nodes) FreeMemory(aasworld.nodes); - aasworld.nodes = NULL; - aasworld.numnodes = 0; - //cluster portals - if (aasworld.portals) FreeMemory(aasworld.portals); - aasworld.portals = NULL; - aasworld.numportals = 0; - //cluster portal index - if (aasworld.portalindex) FreeMemory(aasworld.portalindex); - aasworld.portalindex = NULL; - aasworld.portalindexsize = 0; - //clusters - if (aasworld.clusters) FreeMemory(aasworld.clusters); - aasworld.clusters = NULL; - aasworld.numclusters = 0; - - Log_Print("freed "); - PrintMemorySize(allocatedaasmem); - Log_Print(" of AAS memory\n"); - allocatedaasmem = 0; - // - if (aas_vertexchain) FreeMemory(aas_vertexchain); - aas_vertexchain = NULL; - if (aas_planechain) FreeMemory(aas_planechain); - aas_planechain = NULL; - if (aas_edgechain) FreeMemory(aas_edgechain); - aas_edgechain = NULL; -} //end of the function AAS_FreeMaxAAS -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -unsigned AAS_HashVec(vec3_t vec) -{ - int x, y; - - x = (MAX_MAP_BOUNDS + (int)(vec[0]+0.5)) >> VERTEX_HASH_SHIFT; - y = (MAX_MAP_BOUNDS + (int)(vec[1]+0.5)) >> VERTEX_HASH_SHIFT; - - if (x < 0 || x >= VERTEX_HASH_SIZE || y < 0 || y >= VERTEX_HASH_SIZE) - { - Log_Print("WARNING! HashVec: point %f %f %f outside valid range\n", vec[0], vec[1], vec[2]); - Log_Print("This should never happen!\n"); - return -1; - } //end if - - return y*VERTEX_HASH_SIZE + x; -} //end of the function AAS_HashVec -//=========================================================================== -// returns true if the vertex was found in the list -// stores the vertex number in *vnum -// stores a new vertex if not stored already -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_GetVertex(vec3_t v, int *vnum) -{ - int i; -#ifndef VERTEX_HASHING - float diff; -#endif //VERTEX_HASHING - -#ifdef VERTEX_HASHING - int h, vn; - vec3_t vert; - - for (i = 0; i < 3; i++) - { - if ( fabs(v[i] - Q_rint(v[i])) < INTEGRAL_EPSILON) - vert[i] = Q_rint(v[i]); - else - vert[i] = v[i]; - } //end for - - h = AAS_HashVec(vert); - //if the vertex was outside the valid range - if (h == -1) - { - *vnum = -1; - return true; - } //end if - - for (vn = aas_hashverts[h]; vn >= 0; vn = aas_vertexchain[vn]) - { - if (fabs(aasworld.vertexes[vn][0] - vert[0]) < VERTEX_EPSILON - && fabs(aasworld.vertexes[vn][1] - vert[1]) < VERTEX_EPSILON - && fabs(aasworld.vertexes[vn][2] - vert[2]) < VERTEX_EPSILON) - { - *vnum = vn; - return true; - } //end if - } //end for -#else //VERTEX_HASHING - //check if the vertex is already stored - //stupid linear search - for (i = 0; i < aasworld.numvertexes; i++) - { - diff = vert[0] - aasworld.vertexes[i][0]; - if (diff < VERTEX_EPSILON && diff > -VERTEX_EPSILON) - { - diff = vert[1] - aasworld.vertexes[i][1]; - if (diff < VERTEX_EPSILON && diff > -VERTEX_EPSILON) - { - diff = vert[2] - aasworld.vertexes[i][2]; - if (diff < VERTEX_EPSILON && diff > -VERTEX_EPSILON) - { - *vnum = i; - return true; - } //end if - } //end if - } //end if - } //end for -#endif //VERTEX_HASHING - - if (aasworld.numvertexes >= max_aas.max_vertexes) - { - Error("AAS_MAX_VERTEXES = %d", max_aas.max_vertexes); - } //end if - VectorCopy(vert, aasworld.vertexes[aasworld.numvertexes]); - *vnum = aasworld.numvertexes; - -#ifdef VERTEX_HASHING - aas_vertexchain[aasworld.numvertexes] = aas_hashverts[h]; - aas_hashverts[h] = aasworld.numvertexes; -#endif //VERTEX_HASHING - - aasworld.numvertexes++; - return false; -} //end of the function AAS_GetVertex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -unsigned AAS_HashEdge(int v1, int v2) -{ - int vnum1, vnum2; - // - if (v1 < v2) - { - vnum1 = v1; - vnum2 = v2; - } //end if - else - { - vnum1 = v2; - vnum2 = v1; - } //end else - return (vnum1 + vnum2) & (EDGE_HASH_SIZE-1); -} //end of the function AAS_HashVec -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_AddEdgeToHash(int edgenum) -{ - int hash; - aas_edge_t *edge; - - edge = &aasworld.edges[edgenum]; - - hash = AAS_HashEdge(edge->v[0], edge->v[1]); - - aas_edgechain[edgenum] = aas_hashedges[hash]; - aas_hashedges[hash] = edgenum; -} //end of the function AAS_AddEdgeToHash -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_FindHashedEdge(int v1num, int v2num, int *edgenum) -{ - int e, hash; - aas_edge_t *edge; - - hash = AAS_HashEdge(v1num, v2num); - for (e = aas_hashedges[hash]; e >= 0; e = aas_edgechain[e]) - { - edge = &aasworld.edges[e]; - if (edge->v[0] == v1num) - { - if (edge->v[1] == v2num) - { - *edgenum = e; - return true; - } //end if - } //end if - else if (edge->v[1] == v1num) - { - if (edge->v[0] == v2num) - { - //negative for a reversed edge - *edgenum = -e; - return true; - } //end if - } //end else - } //end for - return false; -} //end of the function AAS_FindHashedPlane -//=========================================================================== -// returns true if the edge was found -// stores the edge number in *edgenum (negative if reversed edge) -// stores new edge if not stored already -// returns zero when the edge is degenerate -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_GetEdge(vec3_t v1, vec3_t v2, int *edgenum) -{ - int v1num, v2num; - qboolean found; - - //the first edge is a dummy - if (aasworld.numedges == 0) aasworld.numedges = 1; - - found = AAS_GetVertex(v1, &v1num); - found &= AAS_GetVertex(v2, &v2num); - //if one of the vertexes was outside the valid range - if (v1num == -1 || v2num == -1) - { - *edgenum = 0; - return true; - } //end if - //if both vertexes are the same or snapped onto each other - if (v1num == v2num) - { - *edgenum = 0; - return true; - } //end if - //if both vertexes where already stored - if (found) - { -#ifdef EDGE_HASHING - if (AAS_FindHashedEdge(v1num, v2num, edgenum)) return true; -#else - int i; - for (i = 1; i < aasworld.numedges; i++) - { - if (aasworld.edges[i].v[0] == v1num) - { - if (aasworld.edges[i].v[1] == v2num) - { - *edgenum = i; - return true; - } //end if - } //end if - else if (aasworld.edges[i].v[1] == v1num) - { - if (aasworld.edges[i].v[0] == v2num) - { - //negative for a reversed edge - *edgenum = -i; - return true; - } //end if - } //end else - } //end for -#endif //EDGE_HASHING - } //end if - if (aasworld.numedges >= max_aas.max_edges) - { - Error("AAS_MAX_EDGES = %d", max_aas.max_edges); - } //end if - aasworld.edges[aasworld.numedges].v[0] = v1num; - aasworld.edges[aasworld.numedges].v[1] = v2num; - *edgenum = aasworld.numedges; -#ifdef EDGE_HASHING - AAS_AddEdgeToHash(*edgenum); -#endif //EDGE_HASHING - aasworld.numedges++; - return false; -} //end of the function AAS_GetEdge -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_PlaneTypeForNormal(vec3_t normal) -{ - vec_t ax, ay, az; - - //NOTE: epsilon used - if ( (normal[0] >= 1.0 -NORMAL_EPSILON) || - (normal[0] <= -1.0 + NORMAL_EPSILON)) return PLANE_X; - if ( (normal[1] >= 1.0 -NORMAL_EPSILON) || - (normal[1] <= -1.0 + NORMAL_EPSILON)) return PLANE_Y; - if ( (normal[2] >= 1.0 -NORMAL_EPSILON) || - (normal[2] <= -1.0 + NORMAL_EPSILON)) return PLANE_Z; - - ax = fabs(normal[0]); - ay = fabs(normal[1]); - az = fabs(normal[2]); - - if (ax >= ay && ax >= az) return PLANE_ANYX; - if (ay >= ax && ay >= az) return PLANE_ANYY; - return PLANE_ANYZ; -} //end of the function AAS_PlaneTypeForNormal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_AddPlaneToHash(int planenum) -{ - int hash; - aas_plane_t *plane; - - plane = &aasworld.planes[planenum]; - - hash = (int)fabs(plane->dist) / 8; - hash &= (PLANE_HASH_SIZE-1); - - aas_planechain[planenum] = aas_hashplanes[hash]; - aas_hashplanes[hash] = planenum; -} //end of the function AAS_AddPlaneToHash -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_PlaneEqual(vec3_t normal, float dist, int planenum) -{ - float diff; - - diff = dist - aasworld.planes[planenum].dist; - if (diff > -DIST_EPSILON && diff < DIST_EPSILON) - { - diff = normal[0] - aasworld.planes[planenum].normal[0]; - if (diff > -NORMAL_EPSILON && diff < NORMAL_EPSILON) - { - diff = normal[1] - aasworld.planes[planenum].normal[1]; - if (diff > -NORMAL_EPSILON && diff < NORMAL_EPSILON) - { - diff = normal[2] - aasworld.planes[planenum].normal[2]; - if (diff > -NORMAL_EPSILON && diff < NORMAL_EPSILON) - { - return true; - } //end if - } //end if - } //end if - } //end if - return false; -} //end of the function AAS_PlaneEqual -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_FindPlane(vec3_t normal, float dist, int *planenum) -{ - int i; - - for (i = 0; i < aasworld.numplanes; i++) - { - if (AAS_PlaneEqual(normal, dist, i)) - { - *planenum = i; - return true; - } //end if - } //end for - return false; -} //end of the function AAS_FindPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_FindHashedPlane(vec3_t normal, float dist, int *planenum) -{ - int i, p; - aas_plane_t *plane; - int hash, h; - - hash = (int)fabs(dist) / 8; - hash &= (PLANE_HASH_SIZE-1); - - //search the border bins as well - for (i = -1; i <= 1; i++) - { - h = (hash+i)&(PLANE_HASH_SIZE-1); - for (p = aas_hashplanes[h]; p >= 0; p = aas_planechain[p]) - { - plane = &aasworld.planes[p]; - if (AAS_PlaneEqual(normal, dist, p)) - { - *planenum = p; - return true; - } //end if - } //end for - } //end for - return false; -} //end of the function AAS_FindHashedPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_GetPlane(vec3_t normal, vec_t dist, int *planenum) -{ - aas_plane_t *plane, temp; - - //if (AAS_FindPlane(normal, dist, planenum)) return true; - if (AAS_FindHashedPlane(normal, dist, planenum)) return true; - - if (aasworld.numplanes >= max_aas.max_planes-1) - { - Error("AAS_MAX_PLANES = %d", max_aas.max_planes); - } //end if - -#ifdef STOREPLANESDOUBLE - plane = &aasworld.planes[aasworld.numplanes]; - VectorCopy(normal, plane->normal); - plane->dist = dist; - plane->type = (plane+1)->type = PlaneTypeForNormal(plane->normal); - - VectorCopy(normal, (plane+1)->normal); - VectorNegate((plane+1)->normal, (plane+1)->normal); - (plane+1)->dist = -dist; - - aasworld.numplanes += 2; - - //allways put axial planes facing positive first - if (plane->type < 3) - { - if (plane->normal[0] < 0 || plane->normal[1] < 0 || plane->normal[2] < 0) - { - // flip order - temp = *plane; - *plane = *(plane+1); - *(plane+1) = temp; - *planenum = aasworld.numplanes - 1; - return false; - } //end if - } //end if - *planenum = aasworld.numplanes - 2; - //add the planes to the hash - AAS_AddPlaneToHash(aasworld.numplanes - 1); - AAS_AddPlaneToHash(aasworld.numplanes - 2); - return false; -#else - plane = &aasworld.planes[aasworld.numplanes]; - VectorCopy(normal, plane->normal); - plane->dist = dist; - plane->type = AAS_PlaneTypeForNormal(normal); - - *planenum = aasworld.numplanes; - aasworld.numplanes++; - //add the plane to the hash - AAS_AddPlaneToHash(aasworld.numplanes - 1); - return false; -#endif //STOREPLANESDOUBLE -} //end of the function AAS_GetPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_GetFace(winding_t *w, plane_t *p, int side, int *facenum) -{ - int edgenum, i, j; - aas_face_t *face; - - //face zero is a dummy, because of the face index with negative numbers - if (aasworld.numfaces == 0) aasworld.numfaces = 1; - - if (aasworld.numfaces >= max_aas.max_faces) - { - Error("AAS_MAX_FACES = %d", max_aas.max_faces); - } //end if - face = &aasworld.faces[aasworld.numfaces]; - AAS_GetPlane(p->normal, p->dist, &face->planenum); - face->faceflags = 0; - face->firstedge = aasworld.edgeindexsize; - face->frontarea = 0; - face->backarea = 0; - face->numedges = 0; - for (i = 0; i < w->numpoints; i++) - { - if (aasworld.edgeindexsize >= max_aas.max_edgeindexsize) - { - Error("AAS_MAX_EDGEINDEXSIZE = %d", max_aas.max_edgeindexsize); - } //end if - j = (i+1) % w->numpoints; - AAS_GetEdge(w->p[i], w->p[j], &edgenum); - //if the edge wasn't degenerate - if (edgenum) - { - aasworld.edgeindex[aasworld.edgeindexsize++] = edgenum; - face->numedges++; - } //end if - else if (verbose) - { - Log_Write("AAS_GetFace: face %d had degenerate edge %d-%d\r\n", - aasworld.numfaces, i, j); - } //end else - } //end for - if (face->numedges < 1 -#ifdef NOTHREEVERTEXFACES - || face->numedges < 3 -#endif //NOTHREEVERTEXFACES - ) - { - memset(&aasworld.faces[aasworld.numfaces], 0, sizeof(aas_face_t)); - Log_Write("AAS_GetFace: face %d was tiny\r\n", aasworld.numfaces); - return false; - } //end if - *facenum = aasworld.numfaces; - aasworld.numfaces++; - return true; -} //end of the function AAS_GetFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -/* -qboolean AAS_GetFace(winding_t *w, plane_t *p, int side, int *facenum) -{ - aas_edgeindex_t edges[1024]; - int planenum, numedges, i; - int j, edgenum; - qboolean foundplane, foundedges; - aas_face_t *face; - - //face zero is a dummy, because of the face index with negative numbers - if (aasworld.numfaces == 0) aasworld.numfaces = 1; - - foundplane = AAS_GetPlane(p->normal, p->dist, &planenum); - - foundedges = true; - numedges = w->numpoints; - for (i = 0; i < w->numpoints; i++) - { - if (i >= 1024) Error("AAS_GetFace: more than %d edges\n", 1024); - foundedges &= AAS_GetEdge(w->p[i], w->p[(i+1 >= w->numpoints ? 0 : i+1)], &edges[i]); - } //end for - - //FIXME: use portal number instead of a search - //if the plane and all edges already existed - if (foundplane && foundedges) - { - for (i = 0; i < aasworld.numfaces; i++) - { - face = &aasworld.faces[i]; - if (planenum == face->planenum) - { - if (numedges == face->numedges) - { - for (j = 0; j < numedges; j++) - { - edgenum = abs(aasworld.edgeindex[face->firstedge + j]); - if (abs(edges[i]) != edgenum) break; - } //end for - if (j == numedges) - { - //jippy found the face - *facenum = -i; - return true; - } //end if - } //end if - } //end if - } //end for - } //end if - if (aasworld.numfaces >= max_aas.max_faces) - { - Error("AAS_MAX_FACES = %d", max_aas.max_faces); - } //end if - face = &aasworld.faces[aasworld.numfaces]; - face->planenum = planenum; - face->faceflags = 0; - face->numedges = numedges; - face->firstedge = aasworld.edgeindexsize; - face->frontarea = 0; - face->backarea = 0; - for (i = 0; i < numedges; i++) - { - if (aasworld.edgeindexsize >= max_aas.max_edgeindexsize) - { - Error("AAS_MAX_EDGEINDEXSIZE = %d", max_aas.max_edgeindexsize); - } //end if - aasworld.edgeindex[aasworld.edgeindexsize++] = edges[i]; - } //end for - *facenum = aasworld.numfaces; - aasworld.numfaces++; - return false; -} //end of the function AAS_GetFace*/ -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_StoreAreaSettings(tmp_areasettings_t *tmpareasettings) -{ - aas_areasettings_t *areasettings; - - if (aasworld.numareasettings == 0) aasworld.numareasettings = 1; - areasettings = &aasworld.areasettings[aasworld.numareasettings++]; - areasettings->areaflags = tmpareasettings->areaflags; - areasettings->presencetype = tmpareasettings->presencetype; - areasettings->contents = tmpareasettings->contents; - if (tmpareasettings->modelnum > AREACONTENTS_MAXMODELNUM) - Log_Print("WARNING: more than %d mover models\n", AREACONTENTS_MAXMODELNUM); - areasettings->contents |= (tmpareasettings->modelnum & AREACONTENTS_MAXMODELNUM) << AREACONTENTS_MODELNUMSHIFT; -} //end of the function AAS_StoreAreaSettings -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_StoreArea(tmp_area_t *tmparea) -{ - int side, edgenum, i; - plane_t *plane; - tmp_face_t *tmpface; - aas_area_t *aasarea; - aas_edge_t *edge; - aas_face_t *aasface; - aas_faceindex_t aasfacenum; - vec3_t facecenter; - winding_t *w; - - //when the area is merged go to the merged area - //FIXME: this isn't necessary anymore because the tree - // is refreshed after area merging - while(tmparea->mergedarea) tmparea = tmparea->mergedarea; - // - if (tmparea->invalid) Error("AAS_StoreArea: tried to store invalid area"); - //if there is an aas area already stored for this tmp area - if (tmparea->aasareanum) return -tmparea->aasareanum; - // - if (aasworld.numareas >= max_aas.max_areas) - { - Error("AAS_MAX_AREAS = %d", max_aas.max_areas); - } //end if - //area zero is a dummy - if (aasworld.numareas == 0) aasworld.numareas = 1; - //create an area from this leaf - aasarea = &aasworld.areas[aasworld.numareas]; - aasarea->areanum = aasworld.numareas; - aasarea->numfaces = 0; - aasarea->firstface = aasworld.faceindexsize; - ClearBounds(aasarea->mins, aasarea->maxs); - VectorClear(aasarea->center); - // -// Log_Write("tmparea %d became aasarea %d\r\n", tmparea->areanum, aasarea->areanum); - //store the aas area number at the tmp area - tmparea->aasareanum = aasarea->areanum; - // - for (tmpface = tmparea->tmpfaces; tmpface; tmpface = tmpface->next[side]) - { - side = tmpface->frontarea != tmparea; - //if there's an aas face created for the tmp face already - if (tmpface->aasfacenum) - { - //we're at the back of the face so use a negative index - aasfacenum = -tmpface->aasfacenum; -#ifdef DEBUG - if (tmpface->aasfacenum < 0 || tmpface->aasfacenum > max_aas.max_faces) - { - Error("AAS_CreateTree_r: face number out of range"); - } //end if -#endif //DEBUG - aasface = &aasworld.faces[tmpface->aasfacenum]; - aasface->backarea = aasarea->areanum; - } //end if - else - { - plane = &mapplanes[tmpface->planenum ^ side]; - if (side) - { - w = tmpface->winding; - tmpface->winding = ReverseWinding(tmpface->winding); - } //end if - if (!AAS_GetFace(tmpface->winding, plane, 0, &aasfacenum)) continue; - if (side) - { - FreeWinding(tmpface->winding); - tmpface->winding = w; - } //end if - aasface = &aasworld.faces[aasfacenum]; - aasface->frontarea = aasarea->areanum; - aasface->backarea = 0; - aasface->faceflags = tmpface->faceflags; - //set the face number at the tmp face - tmpface->aasfacenum = aasfacenum; - } //end else - //add face points to the area bounds and - //calculate the face 'center' - VectorClear(facecenter); - for (edgenum = 0; edgenum < aasface->numedges; edgenum++) - { - edge = &aasworld.edges[abs(aasworld.edgeindex[aasface->firstedge + edgenum])]; - for (i = 0; i < 2; i++) - { - AddPointToBounds(aasworld.vertexes[edge->v[i]], aasarea->mins, aasarea->maxs); - VectorAdd(aasworld.vertexes[edge->v[i]], facecenter, facecenter); - } //end for - } //end for - VectorScale(facecenter, 1.0 / (aasface->numedges * 2.0), facecenter); - //add the face 'center' to the area 'center' - VectorAdd(aasarea->center, facecenter, aasarea->center); - // - if (aasworld.faceindexsize >= max_aas.max_faceindexsize) - { - Error("AAS_MAX_FACEINDEXSIZE = %d", max_aas.max_faceindexsize); - } //end if - aasworld.faceindex[aasworld.faceindexsize++] = aasfacenum; - aasarea->numfaces++; - } //end for - //if the area has no faces at all (return 0, = solid leaf) - if (!aasarea->numfaces) return 0; - // - VectorScale(aasarea->center, 1.0 / aasarea->numfaces, aasarea->center); - //Log_Write("area %d center %f %f %f\r\n", aasworld.numareas, - // aasarea->center[0], aasarea->center[1], aasarea->center[2]); - //store the area settings - AAS_StoreAreaSettings(tmparea->settings); - // - //Log_Write("tmp area %d became aas area %d\r\n", tmpareanum, aasarea->areanum); - qprintf("\r%6d", aasarea->areanum); - // - aasworld.numareas++; - return -(aasworld.numareas - 1); -} //end of the function AAS_StoreArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_StoreTree_r(tmp_node_t *tmpnode) -{ - int aasnodenum; - plane_t *plane; - aas_node_t *aasnode; - - //if it is a solid leaf - if (!tmpnode) return 0; - //negative so it's an area - if (tmpnode->tmparea) return AAS_StoreArea(tmpnode->tmparea); - //it's another node - //the first node is a dummy - if (aasworld.numnodes == 0) aasworld.numnodes = 1; - if (aasworld.numnodes >= max_aas.max_nodes) - { - Error("AAS_MAX_NODES = %d", max_aas.max_nodes); - } //end if - aasnodenum = aasworld.numnodes; - aasnode = &aasworld.nodes[aasworld.numnodes++]; - plane = &mapplanes[tmpnode->planenum]; - AAS_GetPlane(plane->normal, plane->dist, &aasnode->planenum); - aasnode->children[0] = AAS_StoreTree_r(tmpnode->children[0]); - aasnode->children[1] = AAS_StoreTree_r(tmpnode->children[1]); - return aasnodenum; -} //end of the function AAS_StoreTree_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_StoreBoundingBoxes(void) -{ - if (cfg.numbboxes > max_aas.max_bboxes) - { - Error("more than %d bounding boxes", max_aas.max_bboxes); - } //end if - aasworld.numbboxes = cfg.numbboxes; - memcpy(aasworld.bboxes, cfg.bboxes, cfg.numbboxes * sizeof(aas_bbox_t)); -} //end of the function AAS_StoreBoundingBoxes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_StoreFile(char *filename) -{ - AAS_AllocMaxAAS(); - - Log_Write("AAS_StoreFile\r\n"); - // - AAS_StoreBoundingBoxes(); - // - qprintf("%6d areas stored", 0); - //start with node 1 because node zero is a dummy - AAS_StoreTree_r(tmpaasworld.nodes); - qprintf("\n"); - Log_Write("%6d areas stored\r\n", aasworld.numareas); - aasworld.loaded = true; -} //end of the function AAS_StoreFile diff --git a/tools/quake3/bspc/aas_store.h b/tools/quake3/bspc/aas_store.h deleted file mode 100644 index 429c0cc8..00000000 --- a/tools/quake3/bspc/aas_store.h +++ /dev/null @@ -1,107 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#define AAS_MAX_BBOXES 5 -#define AAS_MAX_VERTEXES 512000 -#define AAS_MAX_PLANES 65536 -#define AAS_MAX_EDGES 512000 -#define AAS_MAX_EDGEINDEXSIZE 512000 -#define AAS_MAX_FACES 512000 -#define AAS_MAX_FACEINDEXSIZE 512000 -#define AAS_MAX_AREAS 65536 -#define AAS_MAX_AREASETTINGS 65536 -#define AAS_MAX_REACHABILITYSIZE 65536 -#define AAS_MAX_NODES 256000 -#define AAS_MAX_PORTALS 65536 -#define AAS_MAX_PORTALINDEXSIZE 65536 -#define AAS_MAX_CLUSTERS 65536 - -#define BSPCINCLUDE -#include "deps/botlib/be_aas.h" -#include "deps/botlib/be_aas_def.h" - -/* -typedef struct bspc_aas_s -{ - int loaded; - int initialized; //true when AAS has been initialized - int savefile; //set true when file should be saved - //bounding boxes - int numbboxes; - aas_bbox_t *bboxes; - //vertexes - int numvertexes; - aas_vertex_t *vertexes; - //planes - int numplanes; - aas_plane_t *planes; - //edges - int numedges; - aas_edge_t *edges; - //edge index - int edgeindexsize; - aas_edgeindex_t *edgeindex; - //faces - int numfaces; - aas_face_t *faces; - //face index - int faceindexsize; - aas_faceindex_t *faceindex; - //convex areas - int numareas; - aas_area_t *areas; - //convex area settings - int numareasettings; - aas_areasettings_t *areasettings; - //reachablity list - int reachabilitysize; - aas_reachability_t *reachability; - //nodes of the bsp tree - int numnodes; - aas_node_t *nodes; - //cluster portals - int numportals; - aas_portal_t *portals; - //cluster portal index - int portalindexsize; - aas_portalindex_t *portalindex; - //clusters - int numclusters; - aas_cluster_t *clusters; - // - int numreachabilityareas; - float reachabilitytime; -} bspc_aas_t; - -extern bspc_aas_t aasworld; -//*/ - -extern aas_t aasworld; - -//stores the AAS file from the temporary AAS -void AAS_StoreFile(char *filename); -//returns a number of the given plane -qboolean AAS_FindPlane(vec3_t normal, float dist, int *planenum); -//allocates the maximum AAS memory for storage -void AAS_AllocMaxAAS(void); -//frees the maximum AAS memory for storage -void AAS_FreeMaxAAS(void); diff --git a/tools/quake3/bspc/aasfile.h b/tools/quake3/bspc/aasfile.h deleted file mode 100644 index fc3dc778..00000000 --- a/tools/quake3/bspc/aasfile.h +++ /dev/null @@ -1,252 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - - -//NOTE: int = default signed -// default long - -#define AASID (('S'<<24)+('A'<<16)+('A'<<8)+'E') -#define AASVERSION_OLD 4 -#define AASVERSION 5 - -//presence types -#define PRESENCE_NONE 1 -#define PRESENCE_NORMAL 2 -#define PRESENCE_CROUCH 4 - -//travel types -#define MAX_TRAVELTYPES 32 -#define TRAVEL_INVALID 1 //temporary not possible -#define TRAVEL_WALK 2 //walking -#define TRAVEL_CROUCH 3 //crouching -#define TRAVEL_BARRIERJUMP 4 //jumping onto a barrier -#define TRAVEL_JUMP 5 //jumping -#define TRAVEL_LADDER 6 //climbing a ladder -#define TRAVEL_WALKOFFLEDGE 7 //walking of a ledge -#define TRAVEL_SWIM 8 //swimming -#define TRAVEL_WATERJUMP 9 //jump out of the water -#define TRAVEL_TELEPORT 10 //teleportation -#define TRAVEL_ELEVATOR 11 //travel by elevator -#define TRAVEL_ROCKETJUMP 12 //rocket jumping required for travel -#define TRAVEL_BFGJUMP 13 //bfg jumping required for travel -#define TRAVEL_GRAPPLEHOOK 14 //grappling hook required for travel -#define TRAVEL_DOUBLEJUMP 15 //double jump -#define TRAVEL_RAMPJUMP 16 //ramp jump -#define TRAVEL_STRAFEJUMP 17 //strafe jump -#define TRAVEL_JUMPPAD 18 //jump pad -#define TRAVEL_FUNCBOB 19 //func bob - -//face flags -#define FACE_SOLID 1 //just solid at the other side -#define FACE_LADDER 2 //ladder -#define FACE_GROUND 4 //standing on ground when in this face -#define FACE_GAP 8 //gap in the ground -#define FACE_LIQUID 16 -#define FACE_LIQUIDSURFACE 32 - -//area contents -#define AREACONTENTS_WATER 1 -#define AREACONTENTS_LAVA 2 -#define AREACONTENTS_SLIME 4 -#define AREACONTENTS_CLUSTERPORTAL 8 -#define AREACONTENTS_TELEPORTAL 16 -#define AREACONTENTS_ROUTEPORTAL 32 -#define AREACONTENTS_TELEPORTER 64 -#define AREACONTENTS_JUMPPAD 128 -#define AREACONTENTS_DONOTENTER 256 -#define AREACONTENTS_VIEWPORTAL 512 - -//area flags -#define AREA_GROUNDED 1 //bot can stand on the ground -#define AREA_LADDER 2 //area contains one or more ladder faces -#define AREA_LIQUID 4 //area contains a liquid - -//aas file header lumps -#define AAS_LUMPS 14 -#define AASLUMP_BBOXES 0 -#define AASLUMP_VERTEXES 1 -#define AASLUMP_PLANES 2 -#define AASLUMP_EDGES 3 -#define AASLUMP_EDGEINDEX 4 -#define AASLUMP_FACES 5 -#define AASLUMP_FACEINDEX 6 -#define AASLUMP_AREAS 7 -#define AASLUMP_AREASETTINGS 8 -#define AASLUMP_REACHABILITY 9 -#define AASLUMP_NODES 10 -#define AASLUMP_PORTALS 11 -#define AASLUMP_PORTALINDEX 12 -#define AASLUMP_CLUSTERS 13 - -//========== bounding box ========= - -//bounding box -typedef struct aas_bbox_s -{ - int presencetype; - int flags; - vec3_t mins, maxs; -} aas_bbox_t; - -//============ settings =========== - -//reachability to another area -typedef struct aas_reachability_s -{ - int areanum; //number of the reachable area - int facenum; //number of the face towards the other area - int edgenum; //number of the edge towards the other area - vec3_t start; //start point of inter area movement - vec3_t end; //end point of inter area movement - int traveltype; //type of travel required to get to the area - unsigned short int traveltime;//travel time of the inter area movement -} aas_reachability_t; - -//area settings -typedef struct aas_areasettings_s -{ - //could also add all kind of statistic fields - int contents; //contents of the convex area - int areaflags; //several area flags - int presencetype; //how a bot can be present in this convex area - int cluster; //cluster the area belongs to, if negative it's a portal - int clusterareanum; //number of the area in the cluster - int numreachableareas; //number of reachable areas from this one - int firstreachablearea; //first reachable area in the reachable area index -} aas_areasettings_t; - -//cluster portal -typedef struct aas_portal_s -{ - int areanum; //area that is the actual portal - int frontcluster; //cluster at front of portal - int backcluster; //cluster at back of portal - int clusterareanum[2]; //number of the area in the front and back cluster -} aas_portal_t; - -//cluster portal index -typedef int aas_portalindex_t; - -//cluster -typedef struct aas_cluster_s -{ - int numareas; //number of areas in the cluster - int numreachabilityareas; //number of areas with reachabilities - int numportals; //number of cluster portals - int firstportal; //first cluster portal in the index -} aas_cluster_t; - -//============ 3d definition ============ - -typedef vec3_t aas_vertex_t; - -//just a plane in the third dimension -typedef struct aas_plane_s -{ - vec3_t normal; //normal vector of the plane - float dist; //distance of the plane (normal vector * distance = point in plane) - int type; -} aas_plane_t; - -//edge -typedef struct aas_edge_s -{ - int v[2]; //numbers of the vertexes of this edge -} aas_edge_t; - -//edge index, negative if vertexes are reversed -typedef int aas_edgeindex_t; - -//a face bounds a convex area, often it will also seperate two convex areas -typedef struct aas_face_s -{ - int planenum; //number of the plane this face is in - int faceflags; //face flags (no use to create face settings for just this field) - int numedges; //number of edges in the boundary of the face - int firstedge; //first edge in the edge index - int frontarea; //convex area at the front of this face - int backarea; //convex area at the back of this face -} aas_face_t; - -//face index, stores a negative index if backside of face -typedef int aas_faceindex_t; - -//convex area with a boundary of faces -typedef struct aas_area_s -{ - int areanum; //number of this area - //3d definition - int numfaces; //number of faces used for the boundary of the convex area - int firstface; //first face in the face index used for the boundary of the convex area - vec3_t mins; //mins of the convex area - vec3_t maxs; //maxs of the convex area - vec3_t center; //'center' of the convex area -} aas_area_t; - -//nodes of the bsp tree -typedef struct aas_node_s -{ - int planenum; - int children[2]; //child nodes of this node, or convex areas as leaves when negative - //when a child is zero it's a solid leaf -} aas_node_t; - -//=========== aas file =============== - -//header lump -typedef struct -{ - int fileofs; - int filelen; -} aas_lump_t; - -//aas file header -typedef struct aas_header_s -{ - int ident; - int version; - int bspchecksum; - //data entries - aas_lump_t lumps[AAS_LUMPS]; -} aas_header_t; - - -//====== additional information ====== -/* - -- when a node child is a solid leaf the node child number is zero -- two adjacent areas (sharing a plane at opposite sides) share a face - this face is a portal between the areas -- when an area uses a face from the faceindex with a positive index - then the face plane normal points into the area -- the face edges are stored counter clockwise using the edgeindex -- two adjacent convex areas (sharing a face) only share One face - this is a simple result of the areas being convex -- the convex areas can't have a mixture of ground and gap faces - other mixtures of faces in one area are allowed -- areas with the AREACONTENTS_CLUSTERPORTAL in the settings have - cluster number zero -- edge zero is a dummy -- face zero is a dummy -- area zero is a dummy -- node zero is a dummy -*/ diff --git a/tools/quake3/bspc/be_aas_bspc.c b/tools/quake3/bspc/be_aas_bspc.c deleted file mode 100644 index 5743fe7e..00000000 --- a/tools/quake3/bspc/be_aas_bspc.c +++ /dev/null @@ -1,277 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qcommon/q_shared.h" -#include "l_log.h" -#include "l_qfiles.h" -#include "botlib/l_memory.h" -#include "botlib/l_script.h" -#include "botlib/l_precomp.h" -#include "botlib/l_struct.h" -#include "botlib/aasfile.h" -#include "botlib/botlib.h" -#include "botlib/be_aas.h" -#include "botlib/be_aas_def.h" -#include "qcommon/cm_public.h" -#include "be_aas_bspc.h" - -#include - -//#define BSPC - -extern botlib_import_t botimport; -extern qboolean capsule_collision; - -botlib_import_t botimport; -clipHandle_t worldmodel; - -void Error (char *error, ...); - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_Error(char *fmt, ...) -{ - va_list argptr; - char text[1024]; - - va_start(argptr, fmt); - vsprintf(text, fmt, argptr); - va_end(argptr); - - Error(text); -} //end of the function AAS_Error -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Sys_MilliSeconds(void) -{ - return clock() * 1000 / CLOCKS_PER_SEC; -} //end of the function Sys_MilliSeconds -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DebugLine(vec3_t start, vec3_t end, int color) -{ -} //end of the function AAS_DebugLine -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ClearShownDebugLines(void) -{ -} //end of the function AAS_ClearShownDebugLines -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *BotImport_BSPEntityData(void) -{ - return CM_EntityString(); -} //end of the function AAS_GetEntityData -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotImport_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) -{ - trace_t result; - - CM_BoxTrace(&result, start, end, mins, maxs, worldmodel, contentmask, capsule_collision); - - bsptrace->allsolid = result.allsolid; - bsptrace->contents = result.contents; - VectorCopy(result.endpos, bsptrace->endpos); - bsptrace->ent = result.entityNum; - bsptrace->fraction = result.fraction; - bsptrace->exp_dist = 0; - bsptrace->plane.dist = result.plane.dist; - VectorCopy(result.plane.normal, bsptrace->plane.normal); - bsptrace->plane.signbits = result.plane.signbits; - bsptrace->plane.type = result.plane.type; - bsptrace->sidenum = 0; - bsptrace->startsolid = result.startsolid; - bsptrace->surface.flags = result.surfaceFlags; -} //end of the function BotImport_Trace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotImport_PointContents(vec3_t p) -{ - return CM_PointContents(p, worldmodel); -} //end of the function BotImport_PointContents -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void *BotImport_GetMemory(int size) -{ - return GetMemory(size); -} //end of the function BotImport_GetMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotImport_Print(int type, char *fmt, ...) -{ - va_list argptr; - char buf[1024]; - - va_start(argptr, fmt); - vsnprintf(buf, sizeof(buf), fmt, argptr); - fputs(buf, stdout); - if (buf[0] != '\r') Log_Write(buf); - va_end(argptr); -} //end of the function BotImport_Print -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotImport_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t outmins, vec3_t outmaxs, vec3_t origin) -{ - clipHandle_t h; - vec3_t mins, maxs; - float max; - int i; - - h = CM_InlineModel(modelnum); - CM_ModelBounds(h, mins, maxs); - //if the model is rotated - if ((angles[0] || angles[1] || angles[2])) - { // expand for rotation - - max = RadiusFromBounds(mins, maxs); - for (i = 0; i < 3; i++) - { - mins[i] = (mins[i] + maxs[i]) * 0.5 - max; - maxs[i] = (mins[i] + maxs[i]) * 0.5 + max; - } //end for - } //end if - if (outmins) VectorCopy(mins, outmins); - if (outmaxs) VectorCopy(maxs, outmaxs); - if (origin) VectorClear(origin); -} //end of the function BotImport_BSPModelMinsMaxsOrigin -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Com_DPrintf(char *fmt, ...) -{ - va_list argptr; - char buf[1024]; - - va_start(argptr, fmt); - vsnprintf(buf, sizeof(buf), fmt, argptr); - fputs(buf, stdout); - if (buf[0] != '\r') Log_Write(buf); - va_end(argptr); -} //end of the function Com_DPrintf -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int COM_Compress( char *data_p ) { - return strlen(data_p); -} -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitBotImport(void) -{ - botimport.BSPEntityData = BotImport_BSPEntityData; - botimport.GetMemory = BotImport_GetMemory; - botimport.FreeMemory = FreeMemory; - botimport.Trace = BotImport_Trace; - botimport.PointContents = BotImport_PointContents; - botimport.Print = BotImport_Print; - botimport.BSPModelMinsMaxsOrigin = BotImport_BSPModelMinsMaxsOrigin; -} //end of the function AAS_InitBotImport -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CalcReachAndClusters(struct quakefile_s *qf) -{ - float time; - - Log_Print("loading collision map...\n"); - // - if (!qf->pakfile[0]) strcpy(qf->pakfile, qf->filename); - //load the map - CM_LoadMap((char *) qf, qfalse, &aasworld.bspchecksum); - //get a handle to the world model - worldmodel = CM_InlineModel(0); // 0 = world, 1 + are bmodels - //initialize bot import structure - AAS_InitBotImport(); - //load the BSP entity string - AAS_LoadBSPFile(); - //init physics settings - AAS_InitSettings(); - //initialize AAS link heap - AAS_InitAASLinkHeap(); - //initialize the AAS linked entities for the new map - AAS_InitAASLinkedEntities(); - //reset all reachabilities and clusters - aasworld.reachabilitysize = 0; - aasworld.numclusters = 0; - //set all view portals as cluster portals in case we re-calculate the reachabilities and clusters (with -reach) - AAS_SetViewPortalsAsClusterPortals(); - //calculate reachabilities - AAS_InitReachability(); - time = 0; - while(AAS_ContinueInitReachability(time)) time++; - //calculate clusters - AAS_InitClustering(); -} //end of the function AAS_CalcReachAndClusters diff --git a/tools/quake3/bspc/be_aas_bspc.h b/tools/quake3/bspc/be_aas_bspc.h deleted file mode 100644 index 97e2921b..00000000 --- a/tools/quake3/bspc/be_aas_bspc.h +++ /dev/null @@ -1,23 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -void AAS_CalcReachAndClusters(struct quakefile_s *qf); diff --git a/tools/quake3/bspc/brushbsp.c b/tools/quake3/bspc/brushbsp.c deleted file mode 100644 index 2276aa6d..00000000 --- a/tools/quake3/bspc/brushbsp.c +++ /dev/null @@ -1,1872 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "l_mem.h" -#include "botlib/aasfile.h" -#include "aas_store.h" -#include "aas_cfg.h" - -#include -#include - -/* -each side has a count of the other sides it splits - -the best split will be the one that minimizes the total split counts -of all remaining sides - -precalc side on plane table - -evaluate split side -{ -cost = 0 -for all sides - for all sides - get - if side splits side and splitside is on same child - cost++; -} -*/ - -int c_nodes; -int c_nonvis; -int c_active_brushes; -int c_solidleafnodes; -int c_totalsides; -int c_brushmemory; -int c_peak_brushmemory; -int c_nodememory; -int c_peak_totalbspmemory; - -// if a brush just barely pokes onto the other side, -// let it slide by without chopping -#define PLANESIDE_EPSILON 0.001 -//0.1 - -//#ifdef DEBUG -typedef struct cname_s -{ - int value; - char *name; -} cname_t; - -cname_t contentnames[] = -{ - {CONTENTS_SOLID,"CONTENTS_SOLID"}, - {CONTENTS_WINDOW,"CONTENTS_WINDOW"}, - {CONTENTS_AUX,"CONTENTS_AUX"}, - {CONTENTS_LAVA,"CONTENTS_LAVA"}, - {CONTENTS_SLIME,"CONTENTS_SLIME"}, - {CONTENTS_WATER,"CONTENTS_WATER"}, - {CONTENTS_MIST,"CONTENTS_MIST"}, - {LAST_VISIBLE_CONTENTS,"LAST_VISIBLE_CONTENTS"}, - - {CONTENTS_AREAPORTAL,"CONTENTS_AREAPORTAL"}, - {CONTENTS_PLAYERCLIP,"CONTENTS_PLAYERCLIP"}, - {CONTENTS_MONSTERCLIP,"CONTENTS_MONSTERCLIP"}, - {CONTENTS_CURRENT_0,"CONTENTS_CURRENT_0"}, - {CONTENTS_CURRENT_90,"CONTENTS_CURRENT_90"}, - {CONTENTS_CURRENT_180,"CONTENTS_CURRENT_180"}, - {CONTENTS_CURRENT_270,"CONTENTS_CURRENT_270"}, - {CONTENTS_CURRENT_UP,"CONTENTS_CURRENT_UP"}, - {CONTENTS_CURRENT_DOWN,"CONTENTS_CURRENT_DOWN"}, - {CONTENTS_ORIGIN,"CONTENTS_ORIGIN"}, - {CONTENTS_MONSTER,"CONTENTS_MONSTER"}, - {CONTENTS_DEADMONSTER,"CONTENTS_DEADMONSTER"}, - {CONTENTS_DETAIL,"CONTENTS_DETAIL"}, - {CONTENTS_Q2TRANSLUCENT,"CONTENTS_TRANSLUCENT"}, - {CONTENTS_LADDER,"CONTENTS_LADDER"}, - {0, 0} -}; - -void PrintContents(int contents) -{ - int i; - - for (i = 0; contentnames[i].value; i++) - { - if (contents & contentnames[i].value) - { - Log_Write("%s,", contentnames[i].name); - } //end if - } //end for -} //end of the function PrintContents - -//#endif - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ResetBrushBSP(void) -{ - c_nodes = 0; - c_nonvis = 0; - c_active_brushes = 0; - c_solidleafnodes = 0; - c_totalsides = 0; - c_brushmemory = 0; - c_peak_brushmemory = 0; - c_nodememory = 0; - c_peak_totalbspmemory = 0; -} //end of the function ResetBrushBSP -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FindBrushInTree (node_t *node, int brushnum) -{ - bspbrush_t *b; - - if (node->planenum == PLANENUM_LEAF) - { - for (b=node->brushlist ; b ; b=b->next) - if (b->original->brushnum == brushnum) - Log_Print ("here\n"); - return; - } - FindBrushInTree(node->children[0], brushnum); - FindBrushInTree(node->children[1], brushnum); -} //end of the function FindBrushInTree -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void DrawBrushList (bspbrush_t *brush, node_t *node) -{ - int i; - side_t *s; - - GLS_BeginScene (); - for ( ; brush ; brush=brush->next) - { - for (i=0 ; inumsides ; i++) - { - s = &brush->sides[i]; - if (!s->winding) - continue; - if (s->texinfo == TEXINFO_NODE) - GLS_Winding (s->winding, 1); - else if (!(s->flags & SFL_VISIBLE)) - GLS_Winding (s->winding, 2); - else - GLS_Winding (s->winding, 0); - } - } - GLS_EndScene (); -} //end of the function DrawBrushList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis) -{ - int i; - side_t *s; - FILE *f; - - qprintf ("writing %s\n", name); - f = SafeOpenWrite (name); - - for ( ; brush ; brush=brush->next) - { - for (i=0 ; inumsides ; i++) - { - s = &brush->sides[i]; - if (!s->winding) - continue; - if (onlyvis && !(s->flags & SFL_VISIBLE)) - continue; - OutputWinding (brush->sides[i].winding, f); - } - } - - fclose (f); -} //end of the function WriteBrushList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintBrush (bspbrush_t *brush) -{ - int i; - - printf ("brush: %p\n", brush); - for (i=0;inumsides ; i++) - { - pw(brush->sides[i].winding); - printf ("\n"); - } //end for -} //end of the function PrintBrush -//=========================================================================== -// Sets the mins/maxs based on the windings -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BoundBrush (bspbrush_t *brush) -{ - int i, j; - winding_t *w; - - ClearBounds (brush->mins, brush->maxs); - for (i=0 ; inumsides ; i++) - { - w = brush->sides[i].winding; - if (!w) - continue; - for (j=0 ; jnumpoints ; j++) - AddPointToBounds (w->p[j], brush->mins, brush->maxs); - } -} //end of the function BoundBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CreateBrushWindings (bspbrush_t *brush) -{ - int i, j; - winding_t *w; - side_t *side; - plane_t *plane; - - for (i=0 ; inumsides ; i++) - { - side = &brush->sides[i]; - plane = &mapplanes[side->planenum]; - w = BaseWindingForPlane (plane->normal, plane->dist); - for (j=0 ; jnumsides && w; j++) - { - if (i == j) - continue; - if (brush->sides[j].flags & SFL_BEVEL) - continue; - plane = &mapplanes[brush->sides[j].planenum^1]; - ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); - } - - side->winding = w; - } - - BoundBrush (brush); -} //end of the function CreateBrushWindings -//=========================================================================== -// Creates a new axial brush -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs) -{ - bspbrush_t *b; - int i; - vec3_t normal; - vec_t dist; - - b = AllocBrush (6); - b->numsides = 6; - for (i=0 ; i<3 ; i++) - { - VectorClear (normal); - normal[i] = 1; - dist = maxs[i]; - b->sides[i].planenum = FindFloatPlane (normal, dist); - - normal[i] = -1; - dist = -mins[i]; - b->sides[3+i].planenum = FindFloatPlane (normal, dist); - } - - CreateBrushWindings (b); - - return b; -} //end of the function BrushFromBounds -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BrushOutOfBounds(bspbrush_t *brush, vec3_t mins, vec3_t maxs, float epsilon) -{ - int i, j, n; - winding_t *w; - side_t *side; - - for (i = 0; i < brush->numsides; i++) - { - side = &brush->sides[i]; - w = side->winding; - for (j = 0; j < w->numpoints; j++) - { - for (n = 0; n < 3; n++) - { - if (w->p[j][n] < (mins[n] + epsilon) || w->p[j][n] > (maxs[n] - epsilon)) return true; - } //end for - } //end for - } //end for - return false; -} //end of the function BrushOutOfBounds -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -vec_t BrushVolume (bspbrush_t *brush) -{ - int i; - winding_t *w; - vec3_t corner; - vec_t d, area, volume; - plane_t *plane; - - if (!brush) return 0; - - // grab the first valid point as the corner - w = NULL; - for (i = 0; i < brush->numsides; i++) - { - w = brush->sides[i].winding; - if (w) break; - } //end for - if (!w) return 0; - VectorCopy (w->p[0], corner); - - // make tetrahedrons to all other faces - volume = 0; - for ( ; i < brush->numsides; i++) - { - w = brush->sides[i].winding; - if (!w) continue; - plane = &mapplanes[brush->sides[i].planenum]; - d = -(DotProduct (corner, plane->normal) - plane->dist); - area = WindingArea(w); - volume += d * area; - } //end for - - volume /= 3; - return volume; -} //end of the function BrushVolume -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int CountBrushList (bspbrush_t *brushes) -{ - int c; - - c = 0; - for ( ; brushes; brushes = brushes->next) c++; - return c; -} //end of the function CountBrushList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -node_t *AllocNode (void) -{ - node_t *node; - - node = GetMemory(sizeof(*node)); - memset (node, 0, sizeof(*node)); - if (numthreads == 1) - { - c_nodememory += MemorySize(node); - } //end if - return node; -} //end of the function AllocNode -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *AllocBrush (int numsides) -{ - bspbrush_t *bb; - size_t c; - - c = offsetof(bspbrush_t, sides[numsides]); - bb = GetMemory(c); - memset (bb, 0, c); - if (numthreads == 1) - { - c_active_brushes++; - c_brushmemory += MemorySize(bb); - if (c_brushmemory > c_peak_brushmemory) - c_peak_brushmemory = c_brushmemory; - } //end if - return bb; -} //end of the function AllocBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeBrush (bspbrush_t *brushes) -{ - int i; - - for (i=0 ; inumsides ; i++) - if (brushes->sides[i].winding) - FreeWinding(brushes->sides[i].winding); - if (numthreads == 1) - { - c_active_brushes--; - c_brushmemory -= MemorySize(brushes); - if (c_brushmemory < 0) c_brushmemory = 0; - } //end if - FreeMemory(brushes); -} //end of the function FreeBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeBrushList (bspbrush_t *brushes) -{ - bspbrush_t *next; - - for ( ; brushes; brushes = next) - { - next = brushes->next; - - FreeBrush(brushes); - } //end for -} //end of the function FreeBrushList -//=========================================================================== -// Duplicates the brush, the sides, and the windings -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *CopyBrush (bspbrush_t *brush) -{ - bspbrush_t *newbrush; - size_t size; - int i; - - size = offsetof(bspbrush_t, sides[brush->numsides]); - - newbrush = AllocBrush (brush->numsides); - memcpy (newbrush, brush, size); - - for (i=0 ; inumsides ; i++) - { - if (brush->sides[i].winding) - newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding); - } - - return newbrush; -} //end of the function CopyBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -node_t *PointInLeaf (node_t *node, vec3_t point) -{ - vec_t d; - plane_t *plane; - - while (node->planenum != PLANENUM_LEAF) - { - plane = &mapplanes[node->planenum]; - d = DotProduct (point, plane->normal) - plane->dist; - if (d > 0) - node = node->children[0]; - else - node = node->children[1]; - } - - return node; -} //end of the function PointInLeaf -//=========================================================================== -// Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#if 0 -int BoxOnPlaneSide (vec3_t mins, vec3_t maxs, plane_t *plane) -{ - int side; - int i; - vec3_t corners[2]; - vec_t dist1, dist2; - - // axial planes are easy - if (plane->type < 3) - { - side = 0; - if (maxs[plane->type] > plane->dist+PLANESIDE_EPSILON) - side |= PSIDE_FRONT; - if (mins[plane->type] < plane->dist-PLANESIDE_EPSILON) - side |= PSIDE_BACK; - return side; - } - - // create the proper leading and trailing verts for the box - - for (i=0 ; i<3 ; i++) - { - if (plane->normal[i] < 0) - { - corners[0][i] = mins[i]; - corners[1][i] = maxs[i]; - } - else - { - corners[1][i] = mins[i]; - corners[0][i] = maxs[i]; - } - } - - dist1 = DotProduct (plane->normal, corners[0]) - plane->dist; - dist2 = DotProduct (plane->normal, corners[1]) - plane->dist; - side = 0; - if (dist1 >= PLANESIDE_EPSILON) - side = PSIDE_FRONT; - if (dist2 < PLANESIDE_EPSILON) - side |= PSIDE_BACK; - - return side; -} -#else -int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, plane_t *p) -{ - float dist1, dist2; - int sides; - - // axial planes are easy - if (p->type < 3) - { - sides = 0; - if (emaxs[p->type] > p->dist+PLANESIDE_EPSILON) sides |= PSIDE_FRONT; - if (emins[p->type] < p->dist-PLANESIDE_EPSILON) sides |= PSIDE_BACK; - return sides; - } //end if - -// general case - switch (p->signbits) - { - case 0: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - break; - case 1: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - break; - case 2: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - break; - case 3: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - break; - case 4: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - break; - case 5: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - break; - case 6: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - break; - case 7: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - break; - default: - dist1 = dist2 = 0; // shut up compiler -// assert( 0 ); - break; - } - - sides = 0; - if (dist1 - p->dist >= PLANESIDE_EPSILON) sides = PSIDE_FRONT; - if (dist2 - p->dist < PLANESIDE_EPSILON) sides |= PSIDE_BACK; - -// assert(sides != 0); - - return sides; -} -#endif -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int QuickTestBrushToPlanenum (bspbrush_t *brush, int planenum, int *numsplits) -{ - int i, num; - plane_t *plane; - int s; - - *numsplits = 0; - - plane = &mapplanes[planenum]; - -#ifdef ME - //fast axial cases - if (plane->type < 3) - { - if (plane->dist + PLANESIDE_EPSILON < brush->mins[plane->type]) - return PSIDE_FRONT; - if (plane->dist - PLANESIDE_EPSILON > brush->maxs[plane->type]) - return PSIDE_BACK; - } //end if -#endif //ME*/ - - // if the brush actually uses the planenum, - // we can tell the side for sure - for (i = 0; i < brush->numsides; i++) - { - num = brush->sides[i].planenum; - if (num >= MAX_MAPFILE_PLANES) - Error ("bad planenum"); - if (num == planenum) - return PSIDE_BACK|PSIDE_FACING; - if (num == (planenum ^ 1) ) - return PSIDE_FRONT|PSIDE_FACING; - - } - - // box on plane side - s = BoxOnPlaneSide (brush->mins, brush->maxs, plane); - - // if both sides, count the visible faces split - if (s == PSIDE_BOTH) - { - *numsplits += 3; - } - - return s; -} //end of the function QuickTestBrushToPlanenum -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TestBrushToPlanenum (bspbrush_t *brush, int planenum, - int *numsplits, qboolean *hintsplit, int *epsilonbrush) -{ - int i, j, num; - plane_t *plane; - int s = 0; - winding_t *w; - vec_t d, d_front, d_back; - int front, back; - int type; - float dist; - - *numsplits = 0; - *hintsplit = false; - - plane = &mapplanes[planenum]; - -#ifdef ME - //fast axial cases - type = plane->type; - if (type < 3) - { - dist = plane->dist; - if (dist + PLANESIDE_EPSILON < brush->mins[type]) return PSIDE_FRONT; - if (dist - PLANESIDE_EPSILON > brush->maxs[type]) return PSIDE_BACK; - if (brush->mins[type] < dist - PLANESIDE_EPSILON && - brush->maxs[type] > dist + PLANESIDE_EPSILON) s = PSIDE_BOTH; - } //end if - - if (s != PSIDE_BOTH) -#endif //ME - { - // if the brush actually uses the planenum, - // we can tell the side for sure - for (i = 0; i < brush->numsides; i++) - { - num = brush->sides[i].planenum; - if (num >= MAX_MAPFILE_PLANES) Error ("bad planenum"); - if (num == planenum) - { - //we don't need to test this side plane again - brush->sides[i].flags |= SFL_TESTED; - return PSIDE_BACK|PSIDE_FACING; - } //end if - if (num == (planenum ^ 1) ) - { - //we don't need to test this side plane again - brush->sides[i].flags |= SFL_TESTED; - return PSIDE_FRONT|PSIDE_FACING; - } //end if - } //end for - - // box on plane side - s = BoxOnPlaneSide (brush->mins, brush->maxs, plane); - - if (s != PSIDE_BOTH) return s; - } //end if - - // if both sides, count the visible faces split - d_front = d_back = 0; - - for (i = 0; i < brush->numsides; i++) - { - if (brush->sides[i].texinfo == TEXINFO_NODE) - continue; // on node, don't worry about splits - if (!(brush->sides[i].flags & SFL_VISIBLE)) - continue; // we don't care about non-visible - w = brush->sides[i].winding; - if (!w) continue; - front = back = 0; - for (j = 0; j < w->numpoints; j++) - { - d = DotProduct(w->p[j], plane->normal) - plane->dist; - if (d > d_front) d_front = d; - if (d < d_back) d_back = d; - if (d > 0.1) // PLANESIDE_EPSILON) - front = 1; - if (d < -0.1) // PLANESIDE_EPSILON) - back = 1; - } //end for - if (front && back) - { - if ( !(brush->sides[i].surf & SURF_SKIP) ) - { - (*numsplits)++; - if (brush->sides[i].surf & SURF_HINT) - { - *hintsplit = true; - } //end if - } //end if - } //end if - } //end for - - if ( (d_front > 0.0 && d_front < 1.0) - || (d_back < 0.0 && d_back > -1.0) ) - (*epsilonbrush)++; - -#if 0 - if (*numsplits == 0) - { // didn't really need to be split - if (front) s = PSIDE_FRONT; - else if (back) s = PSIDE_BACK; - else s = 0; - } -#endif - - return s; -} //end of the function TestBrushToPlanenum -//=========================================================================== -// Returns true if the winding would be crunched out of -// existance by the vertex snapping. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#define EDGE_LENGTH 0.2 -qboolean WindingIsTiny (winding_t *w) -{ -#if 0 - if (WindingArea (w) < 1) - return true; - return false; -#else - int i, j; - vec_t len; - vec3_t delta; - int edges; - - edges = 0; - for (i=0 ; inumpoints ; i++) - { - j = i == w->numpoints - 1 ? 0 : i+1; - VectorSubtract (w->p[j], w->p[i], delta); - len = VectorLength (delta); - if (len > EDGE_LENGTH) - { - if (++edges == 3) - return false; - } - } - return true; -#endif -} //end of the function WindingIsTiny -//=========================================================================== -// Returns true if the winding still has one of the points -// from basewinding for plane -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WindingIsHuge (winding_t *w) -{ - int i, j; - - for (i=0 ; inumpoints ; i++) - { - for (j=0 ; j<3 ; j++) - if (w->p[i][j] < -BOGUS_RANGE+1 || w->p[i][j] > BOGUS_RANGE-1) - return true; - } - return false; -} //end of the function WindingIsHuge -//=========================================================================== -// creates a leaf out of the given nodes with the given brushes -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void LeafNode(node_t *node, bspbrush_t *brushes) -{ - bspbrush_t *b; - int i; - - node->side = NULL; - node->planenum = PLANENUM_LEAF; - node->contents = 0; - - for (b = brushes; b; b = b->next) - { - // if the brush is solid and all of its sides are on nodes, - // it eats everything - if (b->original->contents & CONTENTS_SOLID) - { - for (i=0 ; inumsides ; i++) - if (b->sides[i].texinfo != TEXINFO_NODE) - break; - if (i == b->numsides) - { - node->contents = CONTENTS_SOLID; - break; - } //end if - } //end if - node->contents |= b->original->contents; - } //end for - - if (create_aas) - { - node->expansionbboxes = 0; - node->contents = 0; - for (b = brushes; b; b = b->next) - { - node->expansionbboxes |= b->original->expansionbbox; - node->contents |= b->original->contents; - if (b->original->modelnum) - node->modelnum = b->original->modelnum; - } //end for - if (node->contents & CONTENTS_SOLID) - { - if (node->expansionbboxes != cfg.allpresencetypes) - { - node->contents &= ~CONTENTS_SOLID; - } //end if - } //end if - } //end if - - node->brushlist = brushes; -} //end of the function LeafNode -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CheckPlaneAgainstParents (int pnum, node_t *node) -{ - node_t *p; - - for (p = node->parent; p; p = p->parent) - { - if (p->planenum == pnum) Error("Tried parent"); - } //end for -} //end of the function CheckPlaneAgainstParants -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean CheckPlaneAgainstVolume (int pnum, node_t *node) -{ - bspbrush_t *front, *back; - qboolean good; - - SplitBrush (node->volume, pnum, &front, &back); - - good = (front && back); - - if (front) FreeBrush (front); - if (back) FreeBrush (back); - - return good; -} //end of the function CheckPlaneAgaintsVolume -//=========================================================================== -// Using a hueristic, choses one of the sides out of the brushlist -// to partition the brushes with. -// Returns NULL if there are no valid planes to split with.. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node) -{ - int value, bestvalue; - bspbrush_t *brush, *test; - side_t *side, *bestside; - int i, pass, numpasses; - int pnum; - int s; - int front, back, both, facing, splits; - int bsplits; - int bestsplits; - int epsilonbrush; - qboolean hintsplit = false; - - bestside = NULL; - bestvalue = -99999; - bestsplits = 0; - - // the search order goes: visible-structural, visible-detail, - // nonvisible-structural, nonvisible-detail. - // If any valid plane is available in a pass, no further - // passes will be tried. - numpasses = 2; - for (pass = 0; pass < numpasses; pass++) - { - for (brush = brushes; brush; brush = brush->next) - { - // only check detail the second pass -// if ( (pass & 1) && !(brush->original->contents & CONTENTS_DETAIL) ) -// continue; -// if ( !(pass & 1) && (brush->original->contents & CONTENTS_DETAIL) ) -// continue; - for (i = 0; i < brush->numsides; i++) - { - side = brush->sides + i; -// if (side->flags & SFL_BEVEL) -// continue; // never use a bevel as a spliter - if (!side->winding) - continue; // nothing visible, so it can't split - if (side->texinfo == TEXINFO_NODE) - continue; // allready a node splitter - if (side->flags & SFL_TESTED) - continue; // we allready have metrics for this plane -// if (side->surf & SURF_SKIP) -// continue; // skip surfaces are never chosen - -// if (!(side->flags & SFL_VISIBLE) && (pass < 2)) -// continue; // only check visible faces on first pass - - if ((side->flags & SFL_CURVE) && (pass < 1)) - continue; // only check curves the second pass - - pnum = side->planenum; - pnum &= ~1; // allways use positive facing plane - - CheckPlaneAgainstParents (pnum, node); - - if (!CheckPlaneAgainstVolume (pnum, node)) - continue; // would produce a tiny volume - - front = 0; - back = 0; - both = 0; - facing = 0; - splits = 0; - epsilonbrush = 0; - - //inner loop: optimize - for (test = brushes; test; test = test->next) - { - s = TestBrushToPlanenum (test, pnum, &bsplits, &hintsplit, &epsilonbrush); - - splits += bsplits; -// if (bsplits && (s&PSIDE_FACING) ) -// Error ("PSIDE_FACING with splits"); - - test->testside = s; - // - if (s & PSIDE_FACING) facing++; - if (s & PSIDE_FRONT) front++; - if (s & PSIDE_BACK) back++; - if (s == PSIDE_BOTH) both++; - } //end for - - // give a value estimate for using this plane - value = 5*facing - 5*splits - abs(front-back); -// value = -5*splits; -// value = 5*facing - 5*splits; - if (mapplanes[pnum].type < 3) - value+=5; // axial is better - - value -= epsilonbrush * 1000; // avoid! - - // never split a hint side except with another hint - if (hintsplit && !(side->surf & SURF_HINT) ) - value = -9999999; - - // save off the side test so we don't need - // to recalculate it when we actually seperate - // the brushes - if (value > bestvalue) - { - bestvalue = value; - bestside = side; - bestsplits = splits; - for (test = brushes; test ; test = test->next) - test->side = test->testside; - } //end if - } //end for - } //end for (brush = brushes; - - // if we found a good plane, don't bother trying any - // other passes - if (bestside) - { - if (pass > 1) - { - if (numthreads == 1) c_nonvis++; - } - if (pass > 0) node->detail_seperator = true; // not needed for vis - break; - } //end if - } //end for (pass = 0; - - // - // clear all the tested flags we set - // - for (brush = brushes ; brush ; brush=brush->next) - { - for (i = 0; i < brush->numsides; i++) - { - brush->sides[i].flags &= ~SFL_TESTED; - } //end for - } //end for - - return bestside; -} //end of the function SelectSplitSide -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane) -{ - int i, j; - winding_t *w; - vec_t d, max; - int side; - - max = 0; - side = PSIDE_FRONT; - for (i=0 ; inumsides ; i++) - { - w = brush->sides[i].winding; - if (!w) - continue; - for (j=0 ; jnumpoints ; j++) - { - d = DotProduct (w->p[j], plane->normal) - plane->dist; - if (d > max) - { - max = d; - side = PSIDE_FRONT; - } - if (-d > max) - { - max = -d; - side = PSIDE_BACK; - } - } - } - return side; -} //end of the function BrushMostlyOnSide -//=========================================================================== -// Generates two new brushes, leaving the original -// unchanged -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void SplitBrush (bspbrush_t *brush, int planenum, - bspbrush_t **front, bspbrush_t **back) -{ - bspbrush_t *b[2]; - int i, j; - winding_t *w, *cw[2], *midwinding; - plane_t *plane, *plane2; - side_t *s, *cs; - float d, d_front, d_back; - - *front = *back = NULL; - plane = &mapplanes[planenum]; - - // check all points - d_front = d_back = 0; - for (i=0 ; inumsides ; i++) - { - w = brush->sides[i].winding; - if (!w) - continue; - for (j=0 ; jnumpoints ; j++) - { - d = DotProduct (w->p[j], plane->normal) - plane->dist; - if (d > 0 && d > d_front) - d_front = d; - if (d < 0 && d < d_back) - d_back = d; - } - } - - if (d_front < 0.2) // PLANESIDE_EPSILON) - { // only on back - *back = CopyBrush (brush); - return; - } - if (d_back > -0.2) // PLANESIDE_EPSILON) - { // only on front - *front = CopyBrush (brush); - return; - } - - // create a new winding from the split plane - - w = BaseWindingForPlane (plane->normal, plane->dist); - for (i=0 ; inumsides && w ; i++) - { - plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; - ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON); - } - - if (!w || WindingIsTiny(w)) - { // the brush isn't really split - int side; - - side = BrushMostlyOnSide (brush, plane); - if (side == PSIDE_FRONT) - *front = CopyBrush (brush); - if (side == PSIDE_BACK) - *back = CopyBrush (brush); - //free a possible winding - if (w) FreeWinding(w); - return; - } - - if (WindingIsHuge (w)) - { - Log_Write("WARNING: huge winding\n"); - } - - midwinding = w; - - // split it for real - - for (i=0 ; i<2 ; i++) - { - b[i] = AllocBrush (brush->numsides+1); - b[i]->original = brush->original; - } - - // split all the current windings - - for (i=0 ; inumsides ; i++) - { - s = &brush->sides[i]; - w = s->winding; - if (!w) - continue; - ClipWindingEpsilon (w, plane->normal, plane->dist, - 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); - for (j=0 ; j<2 ; j++) - { - if (!cw[j]) - continue; -#if 0 - if (WindingIsTiny (cw[j])) - { - FreeWinding (cw[j]); - continue; - } -#endif - cs = &b[j]->sides[b[j]->numsides]; - b[j]->numsides++; - *cs = *s; -// cs->planenum = s->planenum; -// cs->texinfo = s->texinfo; -// cs->original = s->original; - cs->winding = cw[j]; - cs->flags &= ~SFL_TESTED; - } - } - - - // see if we have valid polygons on both sides - - for (i=0 ; i<2 ; i++) - { - BoundBrush (b[i]); - for (j=0 ; j<3 ; j++) - { - if (b[i]->mins[j] < -MAX_MAP_BOUNDS || b[i]->maxs[j] > MAX_MAP_BOUNDS) - { - Log_Write("bogus brush after clip"); - break; - } - } - - if (b[i]->numsides < 3 || j < 3) - { - FreeBrush (b[i]); - b[i] = NULL; - } - } - - if ( !(b[0] && b[1]) ) - { - if (!b[0] && !b[1]) - Log_Write("split removed brush\r\n"); - else - Log_Write("split not on both sides\r\n"); - if (b[0]) - { - FreeBrush (b[0]); - *front = CopyBrush (brush); - } - if (b[1]) - { - FreeBrush (b[1]); - *back = CopyBrush (brush); - } - return; - } - - // add the midwinding to both sides - for (i=0 ; i<2 ; i++) - { - cs = &b[i]->sides[b[i]->numsides]; - b[i]->numsides++; - - cs->planenum = planenum^i^1; - cs->texinfo = TEXINFO_NODE; //never use these sides as splitters - cs->flags &= ~SFL_VISIBLE; - cs->flags &= ~SFL_TESTED; - if (i==0) - cs->winding = CopyWinding (midwinding); - else - cs->winding = midwinding; - } - -{ - vec_t v1; - int i; - - for (i = 0; i < 2; i++) - { - v1 = BrushVolume (b[i]); - if (v1 < 1.0) - { - FreeBrush(b[i]); - b[i] = NULL; - //Log_Write("tiny volume after clip"); - } - } - if (!b[0] && !b[1]) - { - Log_Write("two tiny brushes\r\n"); - } //end if -} - - *front = b[0]; - *back = b[1]; -} //end of the function SplitBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void SplitBrushList (bspbrush_t *brushes, - node_t *node, bspbrush_t **front, bspbrush_t **back) -{ - bspbrush_t *brush, *newbrush, *newbrush2; - side_t *side; - int sides; - int i; - - *front = *back = NULL; - - for (brush = brushes; brush; brush = brush->next) - { - sides = brush->side; - - if (sides == PSIDE_BOTH) - { // split into two brushes - SplitBrush (brush, node->planenum, &newbrush, &newbrush2); - if (newbrush) - { - newbrush->next = *front; - *front = newbrush; - } //end if - if (newbrush2) - { - newbrush2->next = *back; - *back = newbrush2; - } //end if - continue; - } //end if - - newbrush = CopyBrush (brush); - - // if the planenum is actualy a part of the brush - // find the plane and flag it as used so it won't be tried - // as a splitter again - if (sides & PSIDE_FACING) - { - for (i=0 ; inumsides ; i++) - { - side = newbrush->sides + i; - if ( (side->planenum& ~1) == node->planenum) - side->texinfo = TEXINFO_NODE; - } //end for - } //end if - if (sides & PSIDE_FRONT) - { - newbrush->next = *front; - *front = newbrush; - continue; - } //end if - if (sides & PSIDE_BACK) - { - newbrush->next = *back; - *back = newbrush; - continue; - } //end if - } //end for -} //end of the function SplitBrushList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CheckBrushLists(bspbrush_t *brushlist1, bspbrush_t *brushlist2) -{ - bspbrush_t *brush1, *brush2; - - for (brush1 = brushlist1; brush1; brush1 = brush1->next) - { - for (brush2 = brushlist2; brush2; brush2 = brush2->next) - { - assert(brush1 != brush2); - } //end for - } //end for -} //end of the function CheckBrushLists -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int numrecurse = 0; - -node_t *BuildTree_r (node_t *node, bspbrush_t *brushes) -{ - node_t *newnode; - side_t *bestside; - int i, totalmem; - bspbrush_t *children[2]; - - qprintf("\r%6d", numrecurse); - numrecurse++; - - if (numthreads == 1) - { - totalmem = WindingMemory() + c_nodememory + c_brushmemory; - if (totalmem > c_peak_totalbspmemory) - c_peak_totalbspmemory = totalmem; - c_nodes++; - } //endif - - if (drawflag) - DrawBrushList(brushes, node); - - // find the best plane to use as a splitter - bestside = SelectSplitSide (brushes, node); - if (!bestside) - { - // leaf node - node->side = NULL; - node->planenum = -1; - LeafNode(node, brushes); - if (node->contents & CONTENTS_SOLID) c_solidleafnodes++; - if (create_aas) - { - //free up memory!!! - FreeBrushList(node->brushlist); - node->brushlist = NULL; - //free the node volume brush - if (node->volume) - { - FreeBrush(node->volume); - node->volume = NULL; - } //end if - } //end if - return node; - } //end if - - // this is a splitplane node - node->side = bestside; - node->planenum = bestside->planenum & ~1; // always use front facing - - //split the brush list in two for both children - SplitBrushList (brushes, node, &children[0], &children[1]); - //free the old brush list - FreeBrushList (brushes); - - // allocate children before recursing - for (i = 0; i < 2; i++) - { - newnode = AllocNode (); - newnode->parent = node; - node->children[i] = newnode; - } //end for - - //split the volume brush of the node for the children - SplitBrush (node->volume, node->planenum, &node->children[0]->volume, - &node->children[1]->volume); - - if (create_aas) - { - //free the volume brush - if (node->volume) - { - FreeBrush(node->volume); - node->volume = NULL; - } //end if - } //end if - // recursively process children - for (i = 0; i < 2; i++) - { - node->children[i] = BuildTree_r(node->children[i], children[i]); - } //end for - - return node; -} //end of the function BuildTree_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -node_t *firstnode; //first node in the list -node_t *lastnode; //last node in the list -int nodelistsize; //number of nodes in the list -int use_nodequeue = 0; //use nodequeue, otherwise a node stack is used -int numwaiting = 0; - -void (*AddNodeToList)(node_t *node); - -//add the node to the front of the node list -//(effectively using a node stack) -void AddNodeToStack(node_t *node) -{ - ThreadLock(); - - node->next = firstnode; - firstnode = node; - if (!lastnode) lastnode = node; - nodelistsize++; - - ThreadUnlock(); - // - ThreadSemaphoreIncrease(1); -} //end of the function AddNodeToStack -//add the node to the end of the node list -//(effectively using a node queue) -void AddNodeToQueue(node_t *node) -{ - ThreadLock(); - - node->next = NULL; - if (lastnode) lastnode->next = node; - else firstnode = node; - lastnode = node; - nodelistsize++; - - ThreadUnlock(); - // - ThreadSemaphoreIncrease(1); -} //end of the function AddNodeToQueue -//get the first node from the front of the node list -node_t *NextNodeFromList(void) -{ - node_t *node; - - ThreadLock(); - numwaiting++; - if (!firstnode) - { - if (numwaiting >= GetNumThreads()) ThreadSemaphoreIncrease(GetNumThreads()); - } //end if - ThreadUnlock(); - - ThreadSemaphoreWait(); - - ThreadLock(); - - numwaiting--; - - node = firstnode; - if (firstnode) - { - firstnode = firstnode->next; - nodelistsize--; - } //end if - if (!firstnode) lastnode = NULL; - - ThreadUnlock(); - - return node; -} //end of the function NextNodeFromList -//returns the size of the node list -int NodeListSize(void) -{ - int size; - - ThreadLock(); - size = nodelistsize; - ThreadUnlock(); - - return size; -} //end of the function NodeListSize -// -void IncreaseNodeCounter(void) -{ - ThreadLock(); - //if (verbose) printf("\r%6d", numrecurse++); - qprintf("\r%6d", numrecurse++); - //qprintf("\r%6d %d, %5d ", numrecurse++, GetNumThreads(), nodelistsize); - ThreadUnlock(); -} //end of the function IncreaseNodeCounter -//thread function, gets nodes from the nodelist and processes them -void BuildTreeThread(int threadid) -{ - node_t *newnode, *node; - side_t *bestside; - int i, totalmem; - bspbrush_t *brushes; - - for (node = NextNodeFromList(); node; ) - { - //if the nodelist isn't empty try to add another thread - //if (NodeListSize() > 10) AddThread(BuildTreeThread); - //display the number of nodes processed so far - if (numthreads == 1) - IncreaseNodeCounter(); - - brushes = node->brushlist; - - if (numthreads == 1) - { - totalmem = WindingMemory() + c_nodememory + c_brushmemory; - if (totalmem > c_peak_totalbspmemory) - { - c_peak_totalbspmemory = totalmem; - } //end if - c_nodes++; - } //endif - - if (drawflag) - { - DrawBrushList(brushes, node); - } //end if - - if (cancelconversion) - { - bestside = NULL; - } //end if - else - { - // find the best plane to use as a splitter - bestside = SelectSplitSide(brushes, node); - } //end else - //if there's no split side left - if (!bestside) - { - //create a leaf out of the node - LeafNode(node, brushes); - if (node->contents & CONTENTS_SOLID) c_solidleafnodes++; - if (create_aas) - { - //free up memory!!! - FreeBrushList(node->brushlist); - node->brushlist = NULL; - } //end if - //free the node volume brush (it is not used anymore) - if (node->volume) - { - FreeBrush(node->volume); - node->volume = NULL; - } //end if - node = NextNodeFromList(); - continue; - } //end if - - // this is a splitplane node - node->side = bestside; - node->planenum = bestside->planenum & ~1; //always use front facing - - //allocate children - for (i = 0; i < 2; i++) - { - newnode = AllocNode(); - newnode->parent = node; - node->children[i] = newnode; - } //end for - - //split the brush list in two for both children - SplitBrushList(brushes, node, &node->children[0]->brushlist, &node->children[1]->brushlist); - - CheckBrushLists(node->children[0]->brushlist, node->children[1]->brushlist); - //free the old brush list - FreeBrushList(brushes); - node->brushlist = NULL; - - //split the volume brush of the node for the children - SplitBrush(node->volume, node->planenum, &node->children[0]->volume, - &node->children[1]->volume); - - if (!node->children[0]->volume || !node->children[1]->volume) - { - Error("child without volume brush"); - } //end if - - //free the volume brush - if (node->volume) - { - FreeBrush(node->volume); - node->volume = NULL; - } //end if - //add both children to the node list - //AddNodeToList(node->children[0]); - AddNodeToList(node->children[1]); - node = node->children[0]; - } //end while - RemoveThread(threadid); -} //end of the function BuildTreeThread -//=========================================================================== -// build the bsp tree using a node list -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BuildTree(tree_t *tree) -{ - int i; - - firstnode = NULL; - lastnode = NULL; - //use a node queue or node stack - if (use_nodequeue) AddNodeToList = AddNodeToQueue; - else AddNodeToList = AddNodeToStack; - //setup thread locking - ThreadSetupLock(); - ThreadSetupSemaphore(); - numwaiting = 0; - // - Log_Print("%6d threads max\n", numthreads); - if (use_nodequeue) Log_Print("breadth first bsp building\n"); - else Log_Print("depth first bsp building\n"); - qprintf("%6d splits", 0); - //add the first node to the list - AddNodeToList(tree->headnode); - //start the threads - for (i = 0; i < numthreads; i++) - AddThread(BuildTreeThread); - //wait for all added threads to be finished - WaitForAllThreadsFinished(); - //shutdown the thread locking - ThreadShutdownLock(); - ThreadShutdownSemaphore(); -} //end of the function BuildTree -//=========================================================================== -// The incoming brush list will be freed before exiting -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tree_t *BrushBSP(bspbrush_t *brushlist, vec3_t mins, vec3_t maxs) -{ - int i, c_faces, c_nonvisfaces, c_brushes; - bspbrush_t *b; - node_t *node; - tree_t *tree; - vec_t volume; -// vec3_t point; - - Log_Print("-------- Brush BSP ---------\n"); - - tree = Tree_Alloc(); - - c_faces = 0; - c_nonvisfaces = 0; - c_brushes = 0; - c_totalsides = 0; - for (b = brushlist; b; b = b->next) - { - c_brushes++; - - volume = BrushVolume(b); - if (volume < microvolume) - { - Log_Print("WARNING: entity %i, brush %i: microbrush\n", - b->original->entitynum, b->original->brushnum); - } //end if - - for (i=0 ; inumsides ; i++) - { - if (b->sides[i].flags & SFL_BEVEL) - continue; - if (!b->sides[i].winding) - continue; - if (b->sides[i].texinfo == TEXINFO_NODE) - continue; - if (b->sides[i].flags & SFL_VISIBLE) - { - c_faces++; - } //end if - else - { - c_nonvisfaces++; - //if (create_aas) b->sides[i].texinfo = TEXINFO_NODE; - } //end if - } //end for - c_totalsides += b->numsides; - - AddPointToBounds (b->mins, tree->mins, tree->maxs); - AddPointToBounds (b->maxs, tree->mins, tree->maxs); - } //end for - - Log_Print("%6i brushes\n", c_brushes); - Log_Print("%6i visible faces\n", c_faces); - Log_Print("%6i nonvisible faces\n", c_nonvisfaces); - Log_Print("%6i total sides\n", c_totalsides); - - c_active_brushes = c_brushes; - c_nodememory = 0; - c_brushmemory = 0; - c_peak_brushmemory = 0; - - c_nodes = 0; - c_nonvis = 0; - node = AllocNode (); - - //volume of first node (head node) - node->volume = BrushFromBounds (mins, maxs); - // - tree->headnode = node; - //just get some statistics and the mins/maxs of the node - numrecurse = 0; -// qprintf("%6d splits", numrecurse); - - tree->headnode->brushlist = brushlist; - BuildTree(tree); - - //build the bsp tree with the start node from the brushlist -// node = BuildTree_r(node, brushlist); - - //if the conversion is cancelled - if (cancelconversion) return tree; - - qprintf("\n"); - Log_Write("%6d splits\r\n", numrecurse); -// Log_Print("%6i visible nodes\n", c_nodes/2 - c_nonvis); -// Log_Print("%6i nonvis nodes\n", c_nonvis); -// Log_Print("%6i leaves\n", (c_nodes+1)/2); -// Log_Print("%6i solid leaf nodes\n", c_solidleafnodes); -// Log_Print("%6i active brushes\n", c_active_brushes); - if (numthreads == 1) - { -// Log_Print("%6i KB of node memory\n", c_nodememory >> 10); -// Log_Print("%6i KB of brush memory\n", c_brushmemory >> 10); -// Log_Print("%6i KB of peak brush memory\n", c_peak_brushmemory >> 10); -// Log_Print("%6i KB of winding memory\n", WindingMemory() >> 10); -// Log_Print("%6i KB of peak winding memory\n", WindingPeakMemory() >> 10); - Log_Print("%6i KB of peak total bsp memory\n", c_peak_totalbspmemory >> 10); - } //end if - - /* - point[0] = 1485; - point[1] = 956.125; - point[2] = 352.125; - node = PointInLeaf(tree->headnode, point); - if (node->planenum != PLANENUM_LEAF) - { - Log_Print("node not a leaf\n"); - } //end if - Log_Print("at %f %f %f:\n", point[0], point[1], point[2]); - PrintContents(node->contents); - Log_Print("node->expansionbboxes = %d\n", node->expansionbboxes); - //*/ - return tree; -} //end of the function BrushBSP - diff --git a/tools/quake3/bspc/bspc.c b/tools/quake3/bspc/bspc.c deleted file mode 100644 index 0063eb4e..00000000 --- a/tools/quake3/bspc/bspc.c +++ /dev/null @@ -1,1036 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#if defined(WIN32) || defined(_WIN32) -#include -#include -#include -#include -#else -#include -#include -#include -#include -#endif -#include "qbsp.h" -#include "l_mem.h" -#include "botlib/aasfile.h" -#include "botlib/be_aas_cluster.h" -#include "botlib/be_aas_optimize.h" -#include "aas_create.h" -#include "aas_store.h" -#include "aas_file.h" -#include "aas_cfg.h" -#include "be_aas_bspc.h" - -extern int use_nodequeue; //brushbsp.c -extern int calcgrapplereach; //be_aas_reach.c - -float subdivide_size = 240; -char source[1024]; -char name[1024]; -vec_t microvolume = 1.0; -char outbase[32]; -int entity_num; -aas_settings_t aassettings; - -qboolean noprune; //don't prune nodes (bspc.c) -qboolean glview; //create a gl view -qboolean nodetail; //don't use detail brushes (map.c) -qboolean fulldetail; //use but don't mark detail brushes (map.c) -qboolean onlyents; //only process the entities (bspc.c) -qboolean nomerge; //don't merge bsp node faces (faces.c) -qboolean nowater; //don't use the water brushes (map.c) -qboolean nocsg; //don't carve intersecting brushes (bspc.c) -qboolean noweld; //use unique face vertexes (faces.c) -qboolean noshare; //don't share bsp edges (faces.c) -qboolean nosubdiv; //don't subdivide bsp node faces (faces.c) -qboolean notjunc; //don't create tjunctions (edge melting) (faces.c) -qboolean optimize; //enable optimisation -qboolean leaktest; //perform a leak test -qboolean verboseentities; -qboolean freetree; //free the bsp tree when not needed anymore -qboolean create_aas; //create an .AAS file -qboolean nobrushmerge; //don't merge brushes -qboolean lessbrushes; //create less brushes instead of correct texture placement -qboolean cancelconversion; //true if the conversion is being cancelled -qboolean noliquids; //no liquids when writing map file -qboolean forcesidesvisible; //force all brush sides to be visible when loaded from bsp -qboolean capsule_collision = 0; - -/* -============================================================================ - - BYTE ORDER FUNCTIONS - -============================================================================ -*/ - -short ShortSwap (short l) -{ - byte b1,b2; - - b1 = l&255; - b2 = (l>>8)&255; - - return (b1<<8) + b2; -} - -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; -} - -typedef union { - float f; - unsigned int i; -} _FloatByteUnion; - -float FloatSwap (const float *f) { - _FloatByteUnion out; - - out.f = *f; - out.i = LongSwap(out.i); - - return out.f; -} - -/* -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ProcessWorldModel (void) -{ - entity_t *e; - tree_t *tree; - qboolean leaked; - int brush_start, brush_end; - - e = &entities[entity_num]; - - brush_start = e->firstbrush; - brush_end = brush_start + e->numbrushes; - leaked = false; - - //process the whole world in one time - tree = ProcessWorldBrushes(brush_start, brush_end); - //create the bsp tree portals - MakeTreePortals(tree); - //mark all leafs that can be reached by entities - if (FloodEntities(tree)) - { - FillOutside(tree->headnode); - } //end if - else - { - Log_Print("**** leaked ****\n"); - leaked = true; - LeakFile(tree); - if (leaktest) - { - Log_Print("--- MAP LEAKED ---\n"); - exit(0); - } //end if - } //end else - - MarkVisibleSides (tree, brush_start, brush_end); - - FloodAreas (tree); - -#ifndef ME - if (glview) WriteGLView(tree, source); -#endif - MakeFaces(tree->headnode); - FixTjuncs(tree->headnode); - - //NOTE: Never prune the nodes because the portals - // are screwed when prunning is done and as - // a result portal writing will crash - //if (!noprune) PruneNodes(tree->headnode); - - WriteBSP(tree->headnode); - - if (!leaked) WritePortalFile(tree); - - Tree_Free(tree); -} //end of the function ProcessWorldModel -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ProcessSubModel (void) -{ - entity_t *e; - int start, end; - tree_t *tree; - bspbrush_t *list; - vec3_t mins, maxs; - - e = &entities[entity_num]; - - start = e->firstbrush; - end = start + e->numbrushes; - - mins[0] = mins[1] = mins[2] = -4096; - maxs[0] = maxs[1] = maxs[2] = 4096; - list = MakeBspBrushList(start, end, mins, maxs); - if (!nocsg) list = ChopBrushes (list); - tree = BrushBSP (list, mins, maxs); - MakeTreePortals (tree); - MarkVisibleSides (tree, start, end); - MakeFaces (tree->headnode); - FixTjuncs (tree->headnode); - WriteBSP (tree->headnode); - Tree_Free(tree); -} //end of the function ProcessSubModel -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ProcessModels (void) -{ - BeginBSPFile(); - - for (entity_num = 0; entity_num < num_entities; entity_num++) - { - if (!entities[entity_num].numbrushes) - continue; - - Log_Print("############### model %i ###############\n", nummodels); - BeginModel(); - if (entity_num == 0) ProcessWorldModel(); - else ProcessSubModel(); - EndModel(); - - if (!verboseentities) - verbose = false; // don't bother printing submodels - } //end for - EndBSPFile(); -} //end of the function ProcessModels -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Win_Map2Bsp(char *bspfilename) -{ - double start, end; - char path[1024]; - - start = I_FloatTime(); - - ThreadSetDefault(); - //yeah sure Carmack - //numthreads = 1; // multiple threads aren't helping... - - strcpy(source, ExpandArg(bspfilename)); - StripExtension(source); - - //delete portal and line files - sprintf(path, "%s.prt", source); - remove(path); - sprintf(path, "%s.lin", source); - remove(path); - - strcpy(name, ExpandArg(bspfilename)); - DefaultExtension(name, ".map"); // might be .reg - - Q2_AllocMaxBSP(); - // - SetModelNumbers(); - SetLightStyles(); - ProcessModels(); - //write the BSP - Q2_WriteBSPFile(bspfilename); - - Q2_FreeMaxBSP(); - - end = I_FloatTime(); - Log_Print("%5.0f seconds elapsed\n", end-start); -} //end of the function Win_Map2Bsp -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Map2Bsp(char *mapfilename, char *outputfilename) -{ - double start, end; - char path[1024]; - - start = I_FloatTime (); - - ThreadSetDefault (); - //yeah sure Carmack - //numthreads = 1; //multiple threads aren't helping... - //SetQdirFromPath(bspfilename); - - strcpy(source, ExpandArg(mapfilename)); - StripExtension(source); - - // delete portal and line files - sprintf(path, "%s.prt", source); - remove(path); - sprintf(path, "%s.lin", source); - remove(path); - - strcpy(name, ExpandArg(mapfilename)); - DefaultExtension(name, ".map"); // might be .reg - - // - // if onlyents, just grab the entites and resave - // - if (onlyents) - { - char out[1024]; - - Q2_AllocMaxBSP(); - sprintf (out, "%s.bsp", source); - Q2_LoadBSPFile(out, 0, 0); - num_entities = 0; - - Q2_LoadMapFile(name); - SetModelNumbers(); - SetLightStyles(); - - Q2_UnparseEntities(); - - Q2_WriteBSPFile(out); - // - Q2_FreeMaxBSP(); - } //end if - else - { - // - // start from scratch - // - Q2_AllocMaxBSP(); - //load the map - Q2_LoadMapFile(name); - //create the .bsp file - SetModelNumbers(); - SetLightStyles(); - ProcessModels(); - //write the BSP - Q2_WriteBSPFile(outputfilename); - // - Q2_FreeMaxBSP(); - } //end else - - end = I_FloatTime(); - Log_Print("%5.0f seconds elapsed\n", end-start); -} //end of the function Map2Bsp -*/ -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AASOuputFile(quakefile_t *qf, char *outputpath, char *filename) -{ - char ext[MAX_PATH]; - - // - if (strlen(outputpath)) - { - strcpy(filename, outputpath); - //append the bsp file base - AppendPathSeperator(filename, MAX_PATH); - ExtractFileBase(qf->origname, &filename[strlen(filename)]); - //append .aas - strcat(filename, ".aas"); - return; - } //end if - // - ExtractFileExtension(qf->filename, ext); - if (!stricmp(ext, "pk3") || !stricmp(ext, "pak") || !stricmp(ext, "sin")) - { - strcpy(filename, qf->filename); - while(strlen(filename) && - filename[strlen(filename)-1] != '\\' && - filename[strlen(filename)-1] != '/') - { - filename[strlen(filename)-1] = '\0'; - } //end while - strcat(filename, "maps"); - if (access(filename, 0x04)) CreatePath(filename); - //append the bsp file base - AppendPathSeperator(filename, MAX_PATH); - ExtractFileBase(qf->origname, &filename[strlen(filename)]); - //append .aas - strcat(filename, ".aas"); - } //end if - else - { - strcpy(filename, qf->filename); - while(strlen(filename) && - filename[strlen(filename)-1] != '.') - { - filename[strlen(filename)-1] = '\0'; - } //end while - strcat(filename, "aas"); - } //end else -} //end of the function AASOutputFile -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CreateAASFilesForAllBSPFiles(char *quakepath) -{ -#if defined(WIN32)|defined(_WIN32) - WIN32_FIND_DATA filedata; - HWND handle; - struct _stat statbuf; -#else - glob_t globbuf; - struct stat statbuf; - int j; -#endif - char filter[_MAX_PATH], bspfilter[_MAX_PATH], aasfilter[_MAX_PATH]; - char aasfile[_MAX_PATH], buf[_MAX_PATH], foldername[_MAX_PATH]; - quakefile_t *qf, *qf2, *files, *bspfiles, *aasfiles; - - int done; - - strcpy(filter, quakepath); - AppendPathSeperator(filter, sizeof(filter)); - strcat(filter, "*"); - -#if defined(WIN32)|defined(_WIN32) - handle = FindFirstFile(filter, &filedata); - done = (handle == INVALID_HANDLE_VALUE); - while(!done) - { - _splitpath(filter, foldername, NULL, NULL, NULL); - _splitpath(filter, NULL, &foldername[strlen(foldername)], NULL, NULL); - AppendPathSeperator(foldername, _MAX_PATH); - strcat(foldername, filedata.cFileName); - _stat(foldername, &statbuf); -#else - glob(filter, 0, NULL, &globbuf); - for (j = 0; j < globbuf.gl_pathc; j++) - { - strcpy(foldername, globbuf.gl_pathv[j]); - stat(foldername, &statbuf); -#endif - //if it is a folder - if (statbuf.st_mode & S_IFDIR) - { - // - AppendPathSeperator(foldername, sizeof(foldername)); - //get all the bsp files - strcpy(bspfilter, foldername); - strcat(bspfilter, "maps/*.bsp"); - files = FindQuakeFiles(bspfilter); - strcpy(bspfilter, foldername); - strcat(bspfilter, "*.pk3/maps/*.bsp"); - bspfiles = FindQuakeFiles(bspfilter); - for (qf = bspfiles; qf; qf = qf->next) if (!qf->next) break; - if (qf) qf->next = files; - else bspfiles = files; - //get all the aas files - strcpy(aasfilter, foldername); - strcat(aasfilter, "maps/*.aas"); - files = FindQuakeFiles(aasfilter); - strcpy(aasfilter, foldername); - strcat(aasfilter, "*.pk3/maps/*.aas"); - aasfiles = FindQuakeFiles(aasfilter); - for (qf = aasfiles; qf; qf = qf->next) if (!qf->next) break; - if (qf) qf->next = files; - else aasfiles = files; - // - for (qf = bspfiles; qf; qf = qf->next) - { - sprintf(aasfile, "%s/%s", qf->pakfile, qf->origname); - Log_Print("found %s\n", aasfile); - strcpy(&aasfile[strlen(aasfile)-strlen(".bsp")], ".aas"); - for (qf2 = aasfiles; qf2; qf2 = qf2->next) - { - sprintf(buf, "%s/%s", qf2->pakfile, qf2->origname); - if (!stricmp(aasfile, buf)) - { - Log_Print("found %s\n", buf); - break; - } //end if - } //end for - } //end for - } //end if -#if defined(WIN32)|defined(_WIN32) - //find the next file - done = !FindNextFile(handle, &filedata); - } //end while -#else - } //end for - globfree(&globbuf); -#endif -} //end of the function CreateAASFilesForAllBSPFiles -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -quakefile_t *GetArgumentFiles(int argc, char *argv[], int *i, char *ext) -{ - quakefile_t *qfiles, *lastqf, *qf; - int j; - char buf[1024]; - - qfiles = NULL; - lastqf = NULL; - for (; (*i)+1 < argc && argv[(*i)+1][0] != '-'; (*i)++) - { - strcpy(buf, argv[(*i)+1]); - for (j = strlen(buf)-1; j >= strlen(buf)-4; j--) - if (buf[j] == '.') break; - if (j >= strlen(buf)-4) - strcpy(&buf[j+1], ext); - qf = FindQuakeFiles(buf); - if (!qf) continue; - if (lastqf) lastqf->next = qf; - else qfiles = qf; - lastqf = qf; - while(lastqf->next) lastqf = lastqf->next; - } //end for - return qfiles; -} //end of the function GetArgumentFiles -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== - -#define COMP_BSP2MAP 1 -#define COMP_BSP2AAS 2 -#define COMP_REACH 3 -#define COMP_CLUSTER 4 -#define COMP_AASOPTIMIZE 5 -#define COMP_AASINFO 6 - -int main (int argc, char **argv) -{ - int i, comp = 0; - char outputpath[MAX_PATH] = ""; - char filename[MAX_PATH] = "unknown"; - quakefile_t *qfiles, *qf; - double start_time; - - myargc = argc; - myargv = argv; - - start_time = I_FloatTime(); - - Log_Open("bspc.log"); //open a log file - Log_Print("BSPC version "BSPC_VERSION", %s %s\n", __DATE__, __TIME__); - - DefaultCfg(); - for (i = 1; i < argc; i++) - { - if (!stricmp(argv[i],"-threads")) - { - if (i + 1 >= argc) {i = 0; break;} - numthreads = atoi(argv[++i]); - Log_Print("threads = %d\n", numthreads); - } //end if - else if (!stricmp(argv[i], "-noverbose")) - { - Log_Print("verbose = false\n"); - verbose = false; - } //end else if - else if (!stricmp(argv[i], "-nocsg")) - { - Log_Print("nocsg = true\n"); - nocsg = true; - } //end else if - else if (!stricmp(argv[i], "-optimize")) - { - Log_Print("optimize = true\n"); - optimize = true; - } //end else if - /* - else if (!stricmp(argv[i],"-glview")) - { - glview = true; - } //end else if - else if (!stricmp(argv[i], "-draw")) - { - Log_Print("drawflag = true\n"); - drawflag = true; - } //end else if - else if (!stricmp(argv[i], "-noweld")) - { - Log_Print("noweld = true\n"); - noweld = true; - } //end else if - else if (!stricmp(argv[i], "-noshare")) - { - Log_Print("noshare = true\n"); - noshare = true; - } //end else if - else if (!stricmp(argv[i], "-notjunc")) - { - Log_Print("notjunc = true\n"); - notjunc = true; - } //end else if - else if (!stricmp(argv[i], "-nowater")) - { - Log_Print("nowater = true\n"); - nowater = true; - } //end else if - else if (!stricmp(argv[i], "-noprune")) - { - Log_Print("noprune = true\n"); - noprune = true; - } //end else if - else if (!stricmp(argv[i], "-nomerge")) - { - Log_Print("nomerge = true\n"); - nomerge = true; - } //end else if - else if (!stricmp(argv[i], "-nosubdiv")) - { - Log_Print("nosubdiv = true\n"); - nosubdiv = true; - } //end else if - else if (!stricmp(argv[i], "-nodetail")) - { - Log_Print("nodetail = true\n"); - nodetail = true; - } //end else if - else if (!stricmp(argv[i], "-fulldetail")) - { - Log_Print("fulldetail = true\n"); - fulldetail = true; - } //end else if - else if (!stricmp(argv[i], "-onlyents")) - { - Log_Print("onlyents = true\n"); - onlyents = true; - } //end else if - else if (!stricmp(argv[i], "-micro")) - { - if (i + 1 >= argc) {i = 0; break;} - microvolume = atof(argv[++i]); - Log_Print("microvolume = %f\n", microvolume); - } //end else if - else if (!stricmp(argv[i], "-leaktest")) - { - Log_Print("leaktest = true\n"); - leaktest = true; - } //end else if - else if (!stricmp(argv[i], "-verboseentities")) - { - Log_Print("verboseentities = true\n"); - verboseentities = true; - } //end else if - else if (!stricmp(argv[i], "-chop")) - { - if (i + 1 >= argc) {i = 0; break;} - subdivide_size = atof(argv[++i]); - Log_Print("subdivide_size = %f\n", subdivide_size); - } //end else if - else if (!stricmp (argv[i], "-tmpout")) - { - strcpy (outbase, "/tmp"); - Log_Print("temp output\n"); - } //end else if - */ -#ifdef ME - else if (!stricmp(argv[i], "-freetree")) - { - freetree = true; - Log_Print("freetree = true\n"); - } //end else if - else if (!stricmp(argv[i], "-grapplereach")) - { - calcgrapplereach = true; - Log_Print("grapplereach = true\n"); - } //end else if - else if (!stricmp(argv[i], "-nobrushmerge")) - { - nobrushmerge = true; - Log_Print("nobrushmerge = true\n"); - } //end else if - else if (!stricmp(argv[i], "-noliquids")) - { - noliquids = true; - Log_Print("noliquids = true\n"); - } //end else if - else if (!stricmp(argv[i], "-forcesidesvisible")) - { - forcesidesvisible = true; - Log_Print("forcesidesvisible = true\n"); - } //end else if - else if (!stricmp(argv[i], "-output")) - { - if (i + 1 >= argc) {i = 0; break;} - if (access(argv[i+1], 0x04)) Warning("the folder %s does not exist", argv[i+1]); - strcpy(outputpath, argv[++i]); - } //end else if - else if (!stricmp(argv[i], "-breadthfirst")) - { - use_nodequeue = true; - Log_Print("breadthfirst = true\n"); - } //end else if - else if (!stricmp(argv[i], "-capsule")) - { - capsule_collision = true; - Log_Print("capsule_collision = true\n"); - } //end else if - else if (!stricmp(argv[i], "-cfg")) - { - if (i + 1 >= argc) {i = 0; break;} - if (!LoadCfgFile(argv[++i])) - exit(0); - } //end else if - else if (!stricmp(argv[i], "-bsp2map")) - { - if (i + 1 >= argc) {i = 0; break;} - comp = COMP_BSP2MAP; - qfiles = GetArgumentFiles(argc, argv, &i, "bsp"); - } //end else if - else if (!stricmp(argv[i], "-bsp2aas")) - { - if (i + 1 >= argc) {i = 0; break;} - comp = COMP_BSP2AAS; - qfiles = GetArgumentFiles(argc, argv, &i, "bsp"); - } //end else if - else if (!stricmp(argv[i], "-aasall")) - { - if (i + 1 >= argc) {i = 0; break;} - CreateAASFilesForAllBSPFiles(argv[++i]); - } //end else if - else if (!stricmp(argv[i], "-reach")) - { - if (i + 1 >= argc) {i = 0; break;} - comp = COMP_REACH; - qfiles = GetArgumentFiles(argc, argv, &i, "bsp"); - } //end else if - else if (!stricmp(argv[i], "-cluster")) - { - if (i + 1 >= argc) {i = 0; break;} - comp = COMP_CLUSTER; - qfiles = GetArgumentFiles(argc, argv, &i, "bsp"); - } //end else if - else if (!stricmp(argv[i], "-aasinfo")) - { - if (i + 1 >= argc) {i = 0; break;} - comp = COMP_AASINFO; - qfiles = GetArgumentFiles(argc, argv, &i, "aas"); - } //end else if - else if (!stricmp(argv[i], "-aasopt")) - { - if (i + 1 >= argc) {i = 0; break;} - comp = COMP_AASOPTIMIZE; - qfiles = GetArgumentFiles(argc, argv, &i, "aas"); - } //end else if -#endif //ME - else - { - Log_Print("unknown parameter %s\n", argv[i]); - break; - } //end else - } //end for - - //if there are parameters and there's no mismatch in one of the parameters - if (argc > 1 && i == argc) - { - switch(comp) - { - case COMP_BSP2MAP: - { - if (!qfiles) Log_Print("no files found\n"); - for (qf = qfiles; qf; qf = qf->next) - { - //copy the output path - strcpy(filename, outputpath); - //append the bsp file base - AppendPathSeperator(filename, MAX_PATH); - ExtractFileBase(qf->origname, &filename[strlen(filename)]); - //append .map - strcat(filename, ".map"); - // - Log_Print("bsp2map: %s to %s\n", qf->origname, filename); - if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname); - // - LoadMapFromBSP(qf); - //write the map file - WriteMapFile(filename); - } //end for - break; - } //end case - case COMP_BSP2AAS: - { - if (!qfiles) Log_Print("no files found\n"); - for (qf = qfiles; qf; qf = qf->next) - { - AASOuputFile(qf, outputpath, filename); - // - Log_Print("bsp2aas: %s to %s\n", qf->origname, filename); - if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname); - //set before map loading - create_aas = 1; - LoadMapFromBSP(qf); - //create the AAS file - AAS_Create(filename); - //if it's a Quake3 map calculate the reachabilities and clusters - if (loadedmaptype == MAPTYPE_QUAKE3) AAS_CalcReachAndClusters(qf); - // - if (optimize) AAS_Optimize(); - // - //write out the stored AAS file - if (!AAS_WriteAASFile(filename)) - { - Error("error writing %s\n", filename); - } //end if - //deallocate memory - AAS_FreeMaxAAS(); - } //end for - break; - } //end case - case COMP_REACH: - { - if (!qfiles) Log_Print("no files found\n"); - for (qf = qfiles; qf; qf = qf->next) - { - AASOuputFile(qf, outputpath, filename); - // - Log_Print("reach: %s to %s\n", qf->origname, filename); - if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname); - //if the AAS file exists in the output directory - if (!access(filename, 0x04)) - { - if (!AAS_LoadAASFile(filename, 0, 0)) - { - Error("error loading aas file %s\n", filename); - } //end if - //assume it's a Quake3 BSP file - loadedmaptype = MAPTYPE_QUAKE3; - } //end if - else - { - Warning("AAS file %s not found in output folder\n", filename); - Log_Print("creating %s...\n", filename); - //set before map loading - create_aas = 1; - LoadMapFromBSP(qf); - //create the AAS file - AAS_Create(filename); - } //end else - //if it's a Quake3 map calculate the reachabilities and clusters - if (loadedmaptype == MAPTYPE_QUAKE3) - { - AAS_CalcReachAndClusters(qf); - } //end if - // - if (optimize) AAS_Optimize(); - //write out the stored AAS file - if (!AAS_WriteAASFile(filename)) - { - Error("error writing %s\n", filename); - } //end if - //deallocate memory - AAS_FreeMaxAAS(); - } //end for - break; - } //end case - case COMP_CLUSTER: - { - if (!qfiles) Log_Print("no files found\n"); - for (qf = qfiles; qf; qf = qf->next) - { - AASOuputFile(qf, outputpath, filename); - // - Log_Print("cluster: %s to %s\n", qf->origname, filename); - if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname); - //if the AAS file exists in the output directory - if (!access(filename, 0x04)) - { - if (!AAS_LoadAASFile(filename, 0, 0)) - { - Error("error loading aas file %s\n", filename); - } //end if - //assume it's a Quake3 BSP file - loadedmaptype = MAPTYPE_QUAKE3; - //if it's a Quake3 map calculate the clusters - if (loadedmaptype == MAPTYPE_QUAKE3) - { - aasworld.numclusters = 0; - AAS_InitBotImport(); - AAS_InitClustering(); - } //end if - } //end if - else - { - Warning("AAS file %s not found in output folder\n", filename); - Log_Print("creating %s...\n", filename); - //set before map loading - create_aas = 1; - LoadMapFromBSP(qf); - //create the AAS file - AAS_Create(filename); - //if it's a Quake3 map calculate the reachabilities and clusters - if (loadedmaptype == MAPTYPE_QUAKE3) AAS_CalcReachAndClusters(qf); - } //end else - // - if (optimize) AAS_Optimize(); - //write out the stored AAS file - if (!AAS_WriteAASFile(filename)) - { - Error("error writing %s\n", filename); - } //end if - //deallocate memory - AAS_FreeMaxAAS(); - } //end for - break; - } //end case - case COMP_AASOPTIMIZE: - { - if (!qfiles) Log_Print("no files found\n"); - for (qf = qfiles; qf; qf = qf->next) - { - AASOuputFile(qf, outputpath, filename); - // - Log_Print("optimizing: %s to %s\n", qf->origname, filename); - if (qf->type != QFILETYPE_AAS) Warning("%s is probably not a AAS file\n", qf->origname); - // - AAS_InitBotImport(); - // - if (!AAS_LoadAASFile(qf->filename, qf->offset, qf->length)) - { - Error("error loading aas file %s\n", qf->filename); - } //end if - AAS_Optimize(); - //write out the stored AAS file - if (!AAS_WriteAASFile(filename)) - { - Error("error writing %s\n", filename); - } //end if - //deallocate memory - AAS_FreeMaxAAS(); - } //end for - break; - } //end case - case COMP_AASINFO: - { - if (!qfiles) Log_Print("no files found\n"); - for (qf = qfiles; qf; qf = qf->next) - { - AASOuputFile(qf, outputpath, filename); - // - Log_Print("aas info for: %s\n", filename); - if (qf->type != QFILETYPE_AAS) Warning("%s is probably not a AAS file\n", qf->origname); - // - AAS_InitBotImport(); - // - if (!AAS_LoadAASFile(qf->filename, qf->offset, qf->length)) - { - Error("error loading aas file %s\n", qf->filename); - } //end if - AAS_ShowTotals(); - } //end for - } //end case - default: - { - Log_Print("don't know what to do\n"); - break; - } //end default - } //end switch - } //end if - else - { - Log_Print("Usage: bspc [- [- ...]]\n" -#if defined(WIN32) || defined(_WIN32) - "Example 1: bspc -bsp2aas d:\\quake3\\baseq3\\maps\\mymap?.bsp\n" - "Example 2: bspc -bsp2aas d:\\quake3\\baseq3\\pak0.pk3\\maps/q3dm*.bsp\n" -#else - "Example 1: bspc -bsp2aas /quake3/baseq3/maps/mymap?.bsp\n" - "Example 2: bspc -bsp2aas /quake3/baseq3/pak0.pk3/maps/q3dm*.bsp\n" -#endif - "\n" - "Switches:\n" - //" bsp2map <[pakfilter/]filter.bsp> = convert BSP to MAP\n" - //" aasall = create AAS files for all BSPs\n" - " bsp2aas <[pakfilter/]filter.bsp> = convert BSP to AAS\n" - " reach = compute reachability & clusters\n" - " cluster = compute clusters\n" - " aasopt = optimize aas file\n" - " aasinfo = show AAS file info\n" - " output = set output path\n" - " threads = set number of threads to X\n" - " cfg = use this cfg file\n" - " optimize = enable optimization\n" - " noverbose = disable verbose output\n" - " breadthfirst = breadth first bsp building\n" - " nobrushmerge = don't merge brushes\n" - " noliquids = don't write liquids to map\n" - " freetree = free the bsp tree\n" - " nocsg = disables brush chopping\n" - " forcesidesvisible = force all sides to be visible\n" - " grapplereach = calculate grapple reachabilities\n" - -/* " glview = output a GL view\n" - " draw = enables drawing\n" - " noweld = disables weld\n" - " noshare = disables sharing\n" - " notjunc = disables juncs\n" - " nowater = disables water brushes\n" - " noprune = disables node prunes\n" - " nomerge = disables face merging\n" - " nosubdiv = disables subdeviding\n" - " nodetail = disables detail brushes\n" - " fulldetail = enables full detail\n" - " onlyents = only compile entities with bsp\n" - " micro \n" - " = sets the micro volume to the given float\n" - " leaktest = perform a leak test\n" - " verboseentities\n" - " = enable entity verbose mode\n" - " chop \n" - " = sets the subdivide size to the given float\n"*/ - "\n"); - } //end else - Log_Print("BSPC run time is %5.0f seconds\n", I_FloatTime() - start_time); - Log_Close(); //close the log file - return 0; -} //end of the function main - diff --git a/tools/quake3/bspc/bspc.sln b/tools/quake3/bspc/bspc.sln deleted file mode 100644 index 6a8493a7..00000000 --- a/tools/quake3/bspc/bspc.sln +++ /dev/null @@ -1,19 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bspc", "bspc.vcproj", "{4E4EBC16-F345-4667-84E1-86633BAFDAE6}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Debug|Win32.ActiveCfg = Debug|Win32 - {4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Debug|Win32.Build.0 = Debug|Win32 - {4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Release|Win32.ActiveCfg = Release|Win32 - {4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/tools/quake3/bspc/bspc.vcproj b/tools/quake3/bspc/bspc.vcproj deleted file mode 100644 index 9e05022a..00000000 --- a/tools/quake3/bspc/bspc.vcproj +++ /dev/null @@ -1,1771 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/quake3/bspc/cfgq3.c b/tools/quake3/bspc/cfgq3.c deleted file mode 100644 index c0598ae8..00000000 --- a/tools/quake3/bspc/cfgq3.c +++ /dev/null @@ -1,84 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -//=========================================================================== -// BSPC configuration file -// Quake3 -//=========================================================================== - -#define PRESENCE_NONE 1 -#define PRESENCE_NORMAL 2 -#define PRESENCE_CROUCH 4 - -bbox //30x30x56 -{ - presencetype PRESENCE_NORMAL - flags 0x0000 - mins {-15, -15, -24} - maxs {15, 15, 32} -} //end bbox - -bbox //30x30x40 -{ - presencetype PRESENCE_CROUCH - flags 0x0001 - mins {-15, -15, -24} - maxs {15, 15, 16} -} //end bbox - -settings -{ - phys_gravitydirection {0, 0, -1} - phys_friction 6 - phys_stopspeed 100 - phys_gravity 800 - phys_waterfriction 1 - phys_watergravity 400 - phys_maxvelocity 320 - phys_maxwalkvelocity 320 - phys_maxcrouchvelocity 100 - phys_maxswimvelocity 150 - phys_maxacceleration 2200 - phys_airaccelerate 0 - phys_maxstep 18 - phys_maxsteepness 0.7 - phys_maxwaterjump 19 - phys_maxbarrier 33 - phys_jumpvel 270 - phys_falldelta5 40 - phys_falldelta10 60 - rs_waterjump 400 - rs_teleport 50 - rs_barrierjump 100 - rs_startcrouch 300 - rs_startgrapple 500 - rs_startwalkoffledge 70 - rs_startjump 300 - rs_rocketjump 500 - rs_bfgjump 500 - rs_jumppad 250 - rs_aircontrolledjumppad 300 - rs_funcbob 300 - rs_startelevator 50 - rs_falldamage5 300 - rs_falldamage10 500 - rs_maxjumpfallheight 450 -} //end settings diff --git a/tools/quake3/bspc/csg.c b/tools/quake3/bspc/csg.c deleted file mode 100644 index fc7c3276..00000000 --- a/tools/quake3/bspc/csg.c +++ /dev/null @@ -1,1005 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" - -/* - -tag all brushes with original contents -brushes may contain multiple contents -there will be no brush overlap after csg phase - -*/ - -int minplanenums[3]; -int maxplanenums[3]; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CheckBSPBrush(bspbrush_t *brush) -{ - int i, j; - plane_t *plane1, *plane2; - - //check if the brush is convex... flipped planes make a brush non-convex - for (i = 0; i < brush->numsides; i++) - { - for (j = 0; j < brush->numsides; j++) - { - if (i == j) continue; - plane1 = &mapplanes[brush->sides[i].planenum]; - plane2 = &mapplanes[brush->sides[j].planenum]; - // - if (WindingsNonConvex(brush->sides[i].winding, - brush->sides[j].winding, - plane1->normal, plane2->normal, - plane1->dist, plane2->dist)) - { - Log_Print("non convex brush"); - break; - } //end if - } //end for - } //end for - BoundBrush(brush); - //check for out of bound brushes - for (i = 0; i < 3; i++) - { - if (brush->mins[i] < -MAX_MAP_BOUNDS || brush->maxs[i] > MAX_MAP_BOUNDS) - { - Log_Print("brush: bounds out of range\n"); - Log_Print("ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, brush->mins[i], i, brush->maxs[i]); - break; - } //end if - if (brush->mins[i] > MAX_MAP_BOUNDS || brush->maxs[i] < -MAX_MAP_BOUNDS) - { - Log_Print("brush: no visible sides on brush\n"); - Log_Print("ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, brush->mins[i], i, brush->maxs[i]); - break; - } //end if - } //end for -} //end of the function CheckBSPBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BSPBrushWindings(bspbrush_t *brush) -{ - int i, j; - winding_t *w; - plane_t *plane; - - for (i = 0; i < brush->numsides; i++) - { - plane = &mapplanes[brush->sides[i].planenum]; - w = BaseWindingForPlane(plane->normal, plane->dist); - for (j = 0; j < brush->numsides && w; j++) - { - if (i == j) continue; - plane = &mapplanes[brush->sides[j].planenum^1]; - ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); - } //end for - brush->sides[i].winding = w; - } //end for -} //end of the function BSPBrushWindings -//=========================================================================== -// NOTE: can't keep brush->original intact -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *TryMergeBrushes(bspbrush_t *brush1, bspbrush_t *brush2) -{ - int i, j, k, n, shared; - side_t *side1, *side2, *cs; - plane_t *plane1, *plane2; - bspbrush_t *newbrush; - - //check for bounding box overlapp - for (i = 0; i < 3; i++) - { - if (brush1->mins[i] > brush2->maxs[i] + 2 - || brush1->maxs[i] < brush2->mins[i] - 2) - { - return NULL; - } //end if - } //end for - // - shared = 0; - //check if the brush is convex... flipped planes make a brush non-convex - for (i = 0; i < brush1->numsides; i++) - { - side1 = &brush1->sides[i]; - //don't check the "shared" sides - for (k = 0; k < brush2->numsides; k++) - { - side2 = &brush2->sides[k]; - if (side1->planenum == (side2->planenum^1)) - { - shared++; - //there may only be ONE shared side - if (shared > 1) return NULL; - break; - } //end if - } //end for - if (k < brush2->numsides) continue; - // - for (j = 0; j < brush2->numsides; j++) - { - side2 = &brush2->sides[j]; - //don't check the "shared" sides - for (n = 0; n < brush1->numsides; n++) - { - side1 = &brush1->sides[n]; - if (side1->planenum == (side2->planenum^1)) break; - } //end for - if (n < brush1->numsides) continue; - // - side1 = &brush1->sides[i]; - //if the side is in the same plane - //* - if (side1->planenum == side2->planenum) - { - if (side1->texinfo != TEXINFO_NODE && - side2->texinfo != TEXINFO_NODE && - side1->texinfo != side2->texinfo) return NULL; - continue; - } //end if - // - plane1 = &mapplanes[side1->planenum]; - plane2 = &mapplanes[side2->planenum]; - // - if (WindingsNonConvex(side1->winding, side2->winding, - plane1->normal, plane2->normal, - plane1->dist, plane2->dist)) - { - return NULL; - } //end if - } //end for - } //end for - newbrush = AllocBrush(brush1->numsides + brush2->numsides); - newbrush->original = brush1->original; - newbrush->numsides = 0; - //newbrush->side = brush1->side; //brush contents - //fix texinfos for sides lying in the same plane - for (i = 0; i < brush1->numsides; i++) - { - side1 = &brush1->sides[i]; - // - for (n = 0; n < brush2->numsides; n++) - { - side2 = &brush2->sides[n]; - //if both sides are in the same plane get the texinfo right - if (side1->planenum == side2->planenum) - { - if (side1->texinfo == TEXINFO_NODE) side1->texinfo = side2->texinfo; - if (side2->texinfo == TEXINFO_NODE) side2->texinfo = side1->texinfo; - } //end if - } //end for - } //end for - // - for (i = 0; i < brush1->numsides; i++) - { - side1 = &brush1->sides[i]; - //don't add the "shared" sides - for (n = 0; n < brush2->numsides; n++) - { - side2 = &brush2->sides[n]; - if (side1->planenum == (side2->planenum ^ 1)) break; - } //end for - if (n < brush2->numsides) continue; - // - for (n = 0; n < newbrush->numsides; n++) - { - cs = &newbrush->sides[n]; - if (cs->planenum == side1->planenum) - { - Log_Print("brush duplicate plane\n"); - break; - } //end if - } //end if - if (n < newbrush->numsides) continue; - //add this side - cs = &newbrush->sides[newbrush->numsides]; - newbrush->numsides++; - *cs = *side1; - } //end for - for (j = 0; j < brush2->numsides; j++) - { - side2 = &brush2->sides[j]; - for (n = 0; n < brush1->numsides; n++) - { - side1 = &brush1->sides[n]; - //if the side is in the same plane - if (side2->planenum == side1->planenum) break; - //don't add the "shared" sides - if (side2->planenum == (side1->planenum ^ 1)) break; - } //end for - if (n < brush1->numsides) continue; - // - for (n = 0; n < newbrush->numsides; n++) - { - cs = &newbrush->sides[n]; - if (cs->planenum == side2->planenum) - { - Log_Print("brush duplicate plane\n"); - break; - } //end if - } //end if - if (n < newbrush->numsides) continue; - //add this side - cs = &newbrush->sides[newbrush->numsides]; - newbrush->numsides++; - *cs = *side2; - } //end for - BSPBrushWindings(newbrush); - BoundBrush(newbrush); - CheckBSPBrush(newbrush); - return newbrush; -} //end of the function TryMergeBrushes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *MergeBrushes(bspbrush_t *brushlist) -{ - int nummerges, merged; - bspbrush_t *b1, *b2, *tail, *newbrush, *newbrushlist; - bspbrush_t *lastb2; - - if (!brushlist) return NULL; - - qprintf("%5d brushes merged", nummerges = 0); - do - { - for (tail = brushlist; tail; tail = tail->next) - { - if (!tail->next) break; - } //end for - merged = 0; - newbrushlist = NULL; - for (b1 = brushlist; b1; b1 = brushlist) - { - lastb2 = b1; - for (b2 = b1->next; b2; b2 = b2->next) - { - //if the brushes don't have the same contents - if (b1->original->contents != b2->original->contents || - b1->original->expansionbbox != b2->original->expansionbbox) newbrush = NULL; - else newbrush = TryMergeBrushes(b1, b2); - if (newbrush) - { - tail->next = newbrush; - lastb2->next = b2->next; - brushlist = brushlist->next; - FreeBrush(b1); - FreeBrush(b2); - for (tail = brushlist; tail; tail = tail->next) - { - if (!tail->next) break; - } //end for - merged++; - qprintf("\r%5d", nummerges++); - break; - } //end if - lastb2 = b2; - } //end for - //if b1 can't be merged with any of the other brushes - if (!b2) - { - brushlist = brushlist->next; - //keep b1 - b1->next = newbrushlist; - newbrushlist = b1; - } //end else - } //end for - brushlist = newbrushlist; - } while(merged); - qprintf("\n"); - return newbrushlist; -} //end of the function MergeBrushes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void SplitBrush2 (bspbrush_t *brush, int planenum, - bspbrush_t **front, bspbrush_t **back) -{ - SplitBrush (brush, planenum, front, back); -#if 0 - if (*front && (*front)->sides[(*front)->numsides-1].texinfo == -1) - (*front)->sides[(*front)->numsides-1].texinfo = (*front)->sides[0].texinfo; // not -1 - if (*back && (*back)->sides[(*back)->numsides-1].texinfo == -1) - (*back)->sides[(*back)->numsides-1].texinfo = (*back)->sides[0].texinfo; // not -1 -#endif -} //end of the function SplitBrush2 -//=========================================================================== -// Returns a list of brushes that remain after B is subtracted from A. -// May by empty if A is contained inside B. -// The originals are undisturbed. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *SubtractBrush (bspbrush_t *a, bspbrush_t *b) -{ // a - b = out (list) - int i; - bspbrush_t *front, *back; - bspbrush_t *out, *in; - - in = a; - out = NULL; - for (i = 0; i < b->numsides && in; i++) - { - SplitBrush2(in, b->sides[i].planenum, &front, &back); - if (in != a) FreeBrush(in); - if (front) - { // add to list - front->next = out; - out = front; - } //end if - in = back; - } //end for - if (in) - { - FreeBrush (in); - } //end if - else - { // didn't really intersect - FreeBrushList (out); - return a; - } //end else - return out; -} //end of the function SubtractBrush -//=========================================================================== -// Returns a single brush made up by the intersection of the -// two provided brushes, or NULL if they are disjoint. -// -// The originals are undisturbed. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b) -{ - int i; - bspbrush_t *front, *back; - bspbrush_t *in; - - in = a; - for (i=0 ; inumsides && in ; i++) - { - SplitBrush2(in, b->sides[i].planenum, &front, &back); - if (in != a) FreeBrush(in); - if (front) FreeBrush(front); - in = back; - } //end for - - if (in == a) return NULL; - - in->next = NULL; - return in; -} //end of the function IntersectBrush -//=========================================================================== -// Returns true if the two brushes definately do not intersect. -// There will be false negatives for some non-axial combinations. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b) -{ - int i, j; - - // check bounding boxes - for (i=0 ; i<3 ; i++) - if (a->mins[i] >= b->maxs[i] - || a->maxs[i] <= b->mins[i]) - return true; // bounding boxes don't overlap - - // check for opposing planes - for (i=0 ; inumsides ; i++) - { - for (j=0 ; jnumsides ; j++) - { - if (a->sides[i].planenum == - (b->sides[j].planenum^1) ) - return true; // opposite planes, so not touching - } - } - - return false; // might intersect -} //end of the function BrushesDisjoint -//=========================================================================== -// Returns a content word for the intersection of two brushes. -// Some combinations will generate a combination (water + clip), -// but most will be the stronger of the two contents. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int IntersectionContents (int c1, int c2) -{ - int out; - - out = c1 | c2; - - if (out & CONTENTS_SOLID) out = CONTENTS_SOLID; - - return out; -} //end of the function IntersectionContents -//=========================================================================== -// Any planes shared with the box edge will be set to no texinfo -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *ClipBrushToBox(bspbrush_t *brush, vec3_t clipmins, vec3_t clipmaxs) -{ - int i, j; - bspbrush_t *front, *back; - int p; - - for (j=0 ; j<2 ; j++) - { - if (brush->maxs[j] > clipmaxs[j]) - { - SplitBrush (brush, maxplanenums[j], &front, &back); - if (front) - FreeBrush (front); - brush = back; - if (!brush) - return NULL; - } - if (brush->mins[j] < clipmins[j]) - { - SplitBrush (brush, minplanenums[j], &front, &back); - if (back) - FreeBrush (back); - brush = front; - if (!brush) - return NULL; - } - } - - // remove any colinear faces - - for (i=0 ; inumsides ; i++) - { - p = brush->sides[i].planenum & ~1; - if (p == maxplanenums[0] || p == maxplanenums[1] - || p == minplanenums[0] || p == minplanenums[1]) - { - brush->sides[i].texinfo = TEXINFO_NODE; - brush->sides[i].flags &= ~SFL_VISIBLE; - } - } - return brush; -} //end of the function ClipBrushToBox -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *MakeBspBrushList(int startbrush, int endbrush, - vec3_t clipmins, vec3_t clipmaxs) -{ - mapbrush_t *mb; - bspbrush_t *brushlist, *newbrush; - int i, j; - int c_faces; - int c_brushes; - int numsides; - int vis; - vec3_t normal; - float dist; - - for (i=0 ; i<2 ; i++) - { - VectorClear (normal); - normal[i] = 1; - dist = clipmaxs[i]; - maxplanenums[i] = FindFloatPlane(normal, dist); - dist = clipmins[i]; - minplanenums[i] = FindFloatPlane(normal, dist); - } - - brushlist = NULL; - c_faces = 0; - c_brushes = 0; - - for (i=startbrush ; inumsides; - if (!numsides) - continue; - - // make sure the brush has at least one face showing - vis = 0; - for (j=0 ; joriginal_sides[j].flags & SFL_VISIBLE) && mb->original_sides[j].winding) - vis++; -#if 0 - if (!vis) - continue; // no faces at all -#endif - // if the brush is outside the clip area, skip it - for (j=0 ; j<3 ; j++) - if (mb->mins[j] >= clipmaxs[j] - || mb->maxs[j] <= clipmins[j]) - break; - if (j != 3) - continue; - - // - // make a copy of the brush - // - newbrush = AllocBrush (mb->numsides); - newbrush->original = mb; - newbrush->numsides = mb->numsides; - memcpy (newbrush->sides, mb->original_sides, numsides*sizeof(side_t)); - for (j=0 ; jsides[j].winding) - newbrush->sides[j].winding = CopyWinding (newbrush->sides[j].winding); - if (newbrush->sides[j].surf & SURF_HINT) - newbrush->sides[j].flags |= SFL_VISIBLE; // hints are always visible - } - VectorCopy (mb->mins, newbrush->mins); - VectorCopy (mb->maxs, newbrush->maxs); - - // - // carve off anything outside the clip box - // - newbrush = ClipBrushToBox (newbrush, clipmins, clipmaxs); - if (!newbrush) - continue; - - c_faces += vis; - c_brushes++; - - newbrush->next = brushlist; - brushlist = newbrush; - } - - return brushlist; -} //end of the function MakeBspBrushList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *AddBrushListToTail (bspbrush_t *list, bspbrush_t *tail) -{ - bspbrush_t *walk, *next; - - for (walk=list ; walk ; walk=next) - { // add to end of list - next = walk->next; - walk->next = NULL; - tail->next = walk; - tail = walk; - } //end for - return tail; -} //end of the function AddBrushListToTail -//=========================================================================== -// Builds a new list that doesn't hold the given brush -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *CullList(bspbrush_t *list, bspbrush_t *skip1) -{ - bspbrush_t *newlist; - bspbrush_t *next; - - newlist = NULL; - - for ( ; list ; list = next) - { - next = list->next; - if (list == skip1) - { - FreeBrush (list); - continue; - } - list->next = newlist; - newlist = list; - } - return newlist; -} //end of the function CullList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -/* -void WriteBrushMap(char *name, bspbrush_t *list) -{ - FILE *f; - side_t *s; - int i; - winding_t *w; - - Log_Print("writing %s\n", name); - f = fopen (name, "wb"); - if (!f) - Error ("Can't write %s\b", name); - - fprintf (f, "{\n\"classname\" \"worldspawn\"\n"); - - for ( ; list ; list=list->next ) - { - fprintf (f, "{\n"); - for (i=0,s=list->sides ; inumsides ; i++,s++) - { - w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist); - - fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]); - fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]); - fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]); - - fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture); - FreeWinding (w); - } - fprintf (f, "}\n"); - } - fprintf (f, "}\n"); - - fclose (f); -} //end of the function WriteBrushMap -*/ -//=========================================================================== -// Returns true if b1 is allowed to bite b2 -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2) -{ -#ifdef ME - if (create_aas) - { - if (b1->original->expansionbbox != b2->original->expansionbbox) - { - return false; - } //end if - //never have something else bite a ladder brush - //never have a ladder brush bite something else - if ( (b1->original->contents & CONTENTS_LADDER) - && !(b2->original->contents & CONTENTS_LADDER)) - { - return false; - } //end if - } //end if -#endif //ME - // detail brushes never bite structural brushes - if ( (b1->original->contents & CONTENTS_DETAIL) - && !(b2->original->contents & CONTENTS_DETAIL) ) - { - return false; - } //end if - if (b1->original->contents & CONTENTS_SOLID) - { - return true; - } //end if - return false; -} //end of the function BrushGE -//=========================================================================== -// Carves any intersecting solid brushes into the minimum number -// of non-intersecting brushes. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *ChopBrushes (bspbrush_t *head) -{ - bspbrush_t *b1, *b2, *next; - bspbrush_t *tail; - bspbrush_t *keep; - bspbrush_t *sub, *sub2; - int c1, c2; - int num_csg_iterations; - - Log_Print("-------- Brush CSG ---------\n"); - Log_Print("%6d original brushes\n", CountBrushList (head)); - - num_csg_iterations = 0; - qprintf("%6d output brushes", num_csg_iterations); - -#if 0 - if (startbrush == 0) - WriteBrushList ("before.gl", head, false); -#endif - keep = NULL; - -newlist: - // find tail - if (!head) return NULL; - - for (tail = head; tail->next; tail = tail->next) - ; - - for (b1=head ; b1 ; b1=next) - { - next = b1->next; - - //if the conversion is cancelled - if (cancelconversion) - { - b1->next = keep; - keep = b1; - continue; - } //end if - - for (b2 = b1->next; b2; b2 = b2->next) - { - if (BrushesDisjoint (b1, b2)) - continue; - - sub = NULL; - sub2 = NULL; - c1 = 999999; - c2 = 999999; - - if (BrushGE (b2, b1)) - { - sub = SubtractBrush (b1, b2); - if (sub == b1) - { - continue; // didn't really intersect - } //end if - if (!sub) - { // b1 is swallowed by b2 - head = CullList (b1, b1); - goto newlist; - } - c1 = CountBrushList (sub); - } - - if ( BrushGE (b1, b2) ) - { - sub2 = SubtractBrush (b2, b1); - if (sub2 == b2) - continue; // didn't really intersect - if (!sub2) - { // b2 is swallowed by b1 - FreeBrushList (sub); - head = CullList (b1, b2); - goto newlist; - } - c2 = CountBrushList (sub2); - } - - if (!sub && !sub2) - continue; // neither one can bite - - // only accept if it didn't fragment - // (commenting this out allows full fragmentation) - if (c1 > 1 && c2 > 1) - { - if (sub2) - FreeBrushList (sub2); - if (sub) - FreeBrushList (sub); - continue; - } - - if (c1 < c2) - { - if (sub2) FreeBrushList (sub2); - tail = AddBrushListToTail (sub, tail); - head = CullList (b1, b1); - goto newlist; - } //end if - else - { - if (sub) FreeBrushList (sub); - tail = AddBrushListToTail (sub2, tail); - head = CullList (b1, b2); - goto newlist; - } //end else - } //end for - - if (!b2) - { // b1 is no longer intersecting anything, so keep it - b1->next = keep; - keep = b1; - } //end if - num_csg_iterations++; - qprintf("\r%6d", num_csg_iterations); - } //end for - - if (cancelconversion) return keep; - // - qprintf("\n"); - Log_Write("%6d output brushes\r\n", num_csg_iterations); - -#if 0 - { - WriteBrushList ("after.gl", keep, false); - WriteBrushMap ("after.map", keep); - } -#endif - - return keep; -} //end of the function ChopBrushes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *InitialBrushList (bspbrush_t *list) -{ - bspbrush_t *b; - bspbrush_t *out, *newb; - int i; - - // only return brushes that have visible faces - out = NULL; - for (b=list ; b ; b=b->next) - { -#if 0 - for (i=0 ; inumsides ; i++) - if (b->sides[i].flags & SFL_VISIBLE) - break; - if (i == b->numsides) - continue; -#endif - newb = CopyBrush (b); - newb->next = out; - out = newb; - - // clear visible, so it must be set by MarkVisibleFaces_r - // to be used in the optimized list - for (i=0 ; inumsides ; i++) - { - newb->sides[i].original = &b->sides[i]; -// newb->sides[i].visible = true; - b->sides[i].flags &= ~SFL_VISIBLE; - } - } - - return out; -} //end of the function InitialBrushList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *OptimizedBrushList (bspbrush_t *list) -{ - bspbrush_t *b; - bspbrush_t *out, *newb; - int i; - - // only return brushes that have visible faces - out = NULL; - for (b=list ; b ; b=b->next) - { - for (i=0 ; inumsides ; i++) - if (b->sides[i].flags & SFL_VISIBLE) - break; - if (i == b->numsides) - continue; - newb = CopyBrush (b); - newb->next = out; - out = newb; - } //end for - -// WriteBrushList ("vis.gl", out, true); - return out; -} //end of the function OptimizeBrushList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tree_t *ProcessWorldBrushes(int brush_start, int brush_end) -{ - bspbrush_t *brushes; - tree_t *tree; - node_t *node; - vec3_t mins, maxs; - - //take the whole world - mins[0] = map_mins[0] - 8; - mins[1] = map_mins[1] - 8; - mins[2] = map_mins[2] - 8; - - maxs[0] = map_maxs[0] + 8; - maxs[1] = map_maxs[1] + 8; - maxs[2] = map_maxs[2] + 8; - - //reset the brush bsp - ResetBrushBSP(); - - // the makelist and chopbrushes could be cached between the passes... - - //create a list with brushes that are within the given mins/maxs - //some brushes will be cut and only the part that falls within the - //mins/maxs will be in the bush list - brushes = MakeBspBrushList(brush_start, brush_end, mins, maxs); - // - - if (!brushes) - { - node = AllocNode (); - node->planenum = PLANENUM_LEAF; - node->contents = CONTENTS_SOLID; - - tree = Tree_Alloc(); - tree->headnode = node; - VectorCopy(mins, tree->mins); - VectorCopy(maxs, tree->maxs); - } //end if - else - { - //Carves any intersecting solid brushes into the minimum number - //of non-intersecting brushes. - if (!nocsg) - { - brushes = ChopBrushes(brushes); - /* - if (create_aas) - { - brushes = MergeBrushes(brushes); - } //end if*/ - } //end if - //if the conversion is cancelled - if (cancelconversion) - { - FreeBrushList(brushes); - return NULL; - } //end if - //create the actual bsp tree - tree = BrushBSP(brushes, mins, maxs); - } //end else - //return the tree - return tree; -} //end of the function ProcessWorldBrushes diff --git a/tools/quake3/bspc/deps/botlib/aasfile.h b/tools/quake3/bspc/deps/botlib/aasfile.h deleted file mode 100644 index 8f2fbf62..00000000 --- a/tools/quake3/bspc/deps/botlib/aasfile.h +++ /dev/null @@ -1,267 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - - -//NOTE: int = default signed -// default long - -#define AASID (('S'<<24)+('A'<<16)+('A'<<8)+'E') -#define AASVERSION_OLD 4 -#define AASVERSION 5 - -//presence types -#define PRESENCE_NONE 1 -#define PRESENCE_NORMAL 2 -#define PRESENCE_CROUCH 4 - -//travel types -#define MAX_TRAVELTYPES 32 -#define TRAVEL_INVALID 1 //temporary not possible -#define TRAVEL_WALK 2 //walking -#define TRAVEL_CROUCH 3 //crouching -#define TRAVEL_BARRIERJUMP 4 //jumping onto a barrier -#define TRAVEL_JUMP 5 //jumping -#define TRAVEL_LADDER 6 //climbing a ladder -#define TRAVEL_WALKOFFLEDGE 7 //walking of a ledge -#define TRAVEL_SWIM 8 //swimming -#define TRAVEL_WATERJUMP 9 //jump out of the water -#define TRAVEL_TELEPORT 10 //teleportation -#define TRAVEL_ELEVATOR 11 //travel by elevator -#define TRAVEL_ROCKETJUMP 12 //rocket jumping required for travel -#define TRAVEL_BFGJUMP 13 //bfg jumping required for travel -#define TRAVEL_GRAPPLEHOOK 14 //grappling hook required for travel -#define TRAVEL_DOUBLEJUMP 15 //double jump -#define TRAVEL_RAMPJUMP 16 //ramp jump -#define TRAVEL_STRAFEJUMP 17 //strafe jump -#define TRAVEL_JUMPPAD 18 //jump pad -#define TRAVEL_FUNCBOB 19 //func bob - -//additional travel flags -#define TRAVELTYPE_MASK 0xFFFFFF -#define TRAVELFLAG_NOTTEAM1 (1 << 24) -#define TRAVELFLAG_NOTTEAM2 (2 << 24) - -//face flags -#define FACE_SOLID 1 //just solid at the other side -#define FACE_LADDER 2 //ladder -#define FACE_GROUND 4 //standing on ground when in this face -#define FACE_GAP 8 //gap in the ground -#define FACE_LIQUID 16 //face seperating two areas with liquid -#define FACE_LIQUIDSURFACE 32 //face seperating liquid and air -#define FACE_BRIDGE 64 //can walk over this face if bridge is closed - -//area contents -#define AREACONTENTS_WATER 1 -#define AREACONTENTS_LAVA 2 -#define AREACONTENTS_SLIME 4 -#define AREACONTENTS_CLUSTERPORTAL 8 -#define AREACONTENTS_TELEPORTAL 16 -#define AREACONTENTS_ROUTEPORTAL 32 -#define AREACONTENTS_TELEPORTER 64 -#define AREACONTENTS_JUMPPAD 128 -#define AREACONTENTS_DONOTENTER 256 -#define AREACONTENTS_VIEWPORTAL 512 -#define AREACONTENTS_MOVER 1024 -#define AREACONTENTS_NOTTEAM1 2048 -#define AREACONTENTS_NOTTEAM2 4096 -//number of model of the mover inside this area -#define AREACONTENTS_MODELNUMSHIFT 24 -#define AREACONTENTS_MAXMODELNUM 0xFF -#define AREACONTENTS_MODELNUM (AREACONTENTS_MAXMODELNUM << AREACONTENTS_MODELNUMSHIFT) - -//area flags -#define AREA_GROUNDED 1 //bot can stand on the ground -#define AREA_LADDER 2 //area contains one or more ladder faces -#define AREA_LIQUID 4 //area contains a liquid -#define AREA_DISABLED 8 //area is disabled for routing when set -#define AREA_BRIDGE 16 //area ontop of a bridge - -//aas file header lumps -#define AAS_LUMPS 14 -#define AASLUMP_BBOXES 0 -#define AASLUMP_VERTEXES 1 -#define AASLUMP_PLANES 2 -#define AASLUMP_EDGES 3 -#define AASLUMP_EDGEINDEX 4 -#define AASLUMP_FACES 5 -#define AASLUMP_FACEINDEX 6 -#define AASLUMP_AREAS 7 -#define AASLUMP_AREASETTINGS 8 -#define AASLUMP_REACHABILITY 9 -#define AASLUMP_NODES 10 -#define AASLUMP_PORTALS 11 -#define AASLUMP_PORTALINDEX 12 -#define AASLUMP_CLUSTERS 13 - -//========== bounding box ========= - -//bounding box -typedef struct aas_bbox_s -{ - int presencetype; - int flags; - vec3_t mins, maxs; -} aas_bbox_t; - -//============ settings =========== - -//reachability to another area -typedef struct aas_reachability_s -{ - int areanum; //number of the reachable area - int facenum; //number of the face towards the other area - int edgenum; //number of the edge towards the other area - vec3_t start; //start point of inter area movement - vec3_t end; //end point of inter area movement - int traveltype; //type of travel required to get to the area - unsigned short int traveltime;//travel time of the inter area movement -} aas_reachability_t; - -//area settings -typedef struct aas_areasettings_s -{ - //could also add all kind of statistic fields - int contents; //contents of the area - int areaflags; //several area flags - int presencetype; //how a bot can be present in this area - int cluster; //cluster the area belongs to, if negative it's a portal - int clusterareanum; //number of the area in the cluster - int numreachableareas; //number of reachable areas from this one - int firstreachablearea; //first reachable area in the reachable area index -} aas_areasettings_t; - -//cluster portal -typedef struct aas_portal_s -{ - int areanum; //area that is the actual portal - int frontcluster; //cluster at front of portal - int backcluster; //cluster at back of portal - int clusterareanum[2]; //number of the area in the front and back cluster -} aas_portal_t; - -//cluster portal index -typedef int aas_portalindex_t; - -//cluster -typedef struct aas_cluster_s -{ - int numareas; //number of areas in the cluster - int numreachabilityareas; //number of areas with reachabilities - int numportals; //number of cluster portals - int firstportal; //first cluster portal in the index -} aas_cluster_t; - -//============ 3d definition ============ - -typedef vec3_t aas_vertex_t; - -//just a plane in the third dimension -typedef struct aas_plane_s -{ - vec3_t normal; //normal vector of the plane - float dist; //distance of the plane (normal vector * distance = point in plane) - int type; -} aas_plane_t; - -//edge -typedef struct aas_edge_s -{ - int v[2]; //numbers of the vertexes of this edge -} aas_edge_t; - -//edge index, negative if vertexes are reversed -typedef int aas_edgeindex_t; - -//a face bounds an area, often it will also seperate two areas -typedef struct aas_face_s -{ - int planenum; //number of the plane this face is in - int faceflags; //face flags (no use to create face settings for just this field) - int numedges; //number of edges in the boundary of the face - int firstedge; //first edge in the edge index - int frontarea; //area at the front of this face - int backarea; //area at the back of this face -} aas_face_t; - -//face index, stores a negative index if backside of face -typedef int aas_faceindex_t; - -//area with a boundary of faces -typedef struct aas_area_s -{ - int areanum; //number of this area - //3d definition - int numfaces; //number of faces used for the boundary of the area - int firstface; //first face in the face index used for the boundary of the area - vec3_t mins; //mins of the area - vec3_t maxs; //maxs of the area - vec3_t center; //'center' of the area -} aas_area_t; - -//nodes of the bsp tree -typedef struct aas_node_s -{ - int planenum; - int children[2]; //child nodes of this node, or areas as leaves when negative - //when a child is zero it's a solid leaf -} aas_node_t; - -//=========== aas file =============== - -//header lump -typedef struct -{ - int fileofs; - int filelen; -} aas_lump_t; - -//aas file header -typedef struct aas_header_s -{ - int ident; - int version; - int bspchecksum; - //data entries - aas_lump_t lumps[AAS_LUMPS]; -} aas_header_t; - - -//====== additional information ====== -/* - -- when a node child is a solid leaf the node child number is zero -- two adjacent areas (sharing a plane at opposite sides) share a face - this face is a portal between the areas -- when an area uses a face from the faceindex with a positive index - then the face plane normal points into the area -- the face edges are stored counter clockwise using the edgeindex -- two adjacent convex areas (sharing a face) only share One face - this is a simple result of the areas being convex -- the areas can't have a mixture of ground and gap faces - other mixtures of faces in one area are allowed -- areas with the AREACONTENTS_CLUSTERPORTAL in the settings have - the cluster number set to the negative portal number -- edge zero is a dummy -- face zero is a dummy -- area zero is a dummy -- node zero is a dummy -*/ diff --git a/tools/quake3/bspc/deps/botlib/be_aas.h b/tools/quake3/bspc/deps/botlib/be_aas.h deleted file mode 100644 index dbf24bc1..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas.h +++ /dev/null @@ -1,221 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// - -/***************************************************************************** - * name: be_aas.h - * - * desc: Area Awareness System, stuff exported to the AI - * - * $Archive: /source/code/botlib/be_aas.h $ - * - *****************************************************************************/ - -#ifndef MAX_STRINGFIELD -#define MAX_STRINGFIELD 80 -#endif - -//travel flags -#define TFL_INVALID 0x00000001 //traveling temporary not possible -#define TFL_WALK 0x00000002 //walking -#define TFL_CROUCH 0x00000004 //crouching -#define TFL_BARRIERJUMP 0x00000008 //jumping onto a barrier -#define TFL_JUMP 0x00000010 //jumping -#define TFL_LADDER 0x00000020 //climbing a ladder -#define TFL_WALKOFFLEDGE 0x00000080 //walking of a ledge -#define TFL_SWIM 0x00000100 //swimming -#define TFL_WATERJUMP 0x00000200 //jumping out of the water -#define TFL_TELEPORT 0x00000400 //teleporting -#define TFL_ELEVATOR 0x00000800 //elevator -#define TFL_ROCKETJUMP 0x00001000 //rocket jumping -#define TFL_BFGJUMP 0x00002000 //bfg jumping -#define TFL_GRAPPLEHOOK 0x00004000 //grappling hook -#define TFL_DOUBLEJUMP 0x00008000 //double jump -#define TFL_RAMPJUMP 0x00010000 //ramp jump -#define TFL_STRAFEJUMP 0x00020000 //strafe jump -#define TFL_JUMPPAD 0x00040000 //jump pad -#define TFL_AIR 0x00080000 //travel through air -#define TFL_WATER 0x00100000 //travel through water -#define TFL_SLIME 0x00200000 //travel through slime -#define TFL_LAVA 0x00400000 //travel through lava -#define TFL_DONOTENTER 0x00800000 //travel through donotenter area -#define TFL_FUNCBOB 0x01000000 //func bobbing -#define TFL_FLIGHT 0x02000000 //flight -#define TFL_BRIDGE 0x04000000 //move over a bridge -// -#define TFL_NOTTEAM1 0x08000000 //not team 1 -#define TFL_NOTTEAM2 0x10000000 //not team 2 - -//default travel flags -#define TFL_DEFAULT TFL_WALK|TFL_CROUCH|TFL_BARRIERJUMP|\ - TFL_JUMP|TFL_LADDER|\ - TFL_WALKOFFLEDGE|TFL_SWIM|TFL_WATERJUMP|\ - TFL_TELEPORT|TFL_ELEVATOR|\ - TFL_AIR|TFL_WATER|TFL_JUMPPAD|TFL_FUNCBOB - -typedef enum -{ - SOLID_NOT, // no interaction with other objects - SOLID_TRIGGER, // only touch when inside, after moving - SOLID_BBOX, // touch on edge - SOLID_BSP // bsp clip, touch on edge -} solid_t; - -//a trace is returned when a box is swept through the AAS world -typedef struct aas_trace_s -{ - qboolean startsolid; // if true, the initial point was in a solid area - float fraction; // time completed, 1.0 = didn't hit anything - vec3_t endpos; // final position - int ent; // entity blocking the trace - int lastarea; // last area the trace was in (zero if none) - int area; // area blocking the trace (zero if none) - int planenum; // number of the plane that was hit -} aas_trace_t; - -/* Defined in botlib.h - -//bsp_trace_t hit surface -typedef struct bsp_surface_s -{ - char name[16]; - int flags; - int value; -} bsp_surface_t; - -//a trace is returned when a box is swept through the BSP world -typedef struct bsp_trace_s -{ - qboolean allsolid; // if true, plane is not valid - qboolean startsolid; // if true, the initial point was in a solid area - float fraction; // time completed, 1.0 = didn't hit anything - vec3_t endpos; // final position - cplane_t plane; // surface normal at impact - float exp_dist; // expanded plane distance - int sidenum; // number of the brush side hit - bsp_surface_t surface; // hit surface - int contents; // contents on other side of surface hit - int ent; // number of entity hit -} bsp_trace_t; -// -*/ - -//entity info -typedef struct aas_entityinfo_s -{ - int valid; // true if updated this frame - int type; // entity type - int flags; // entity flags - float ltime; // local time - float update_time; // time between last and current update - int number; // number of the entity - vec3_t origin; // origin of the entity - vec3_t angles; // angles of the model - vec3_t old_origin; // for lerping - vec3_t lastvisorigin; // last visible origin - vec3_t mins; // bounding box minimums - vec3_t maxs; // bounding box maximums - int groundent; // ground entity - int solid; // solid type - int modelindex; // model used - int modelindex2; // weapons, CTF flags, etc - int frame; // model frame number - int event; // impulse events -- muzzle flashes, footsteps, etc - int eventParm; // even parameter - int powerups; // bit flags - int weapon; // determines weapon and flash model, etc - int legsAnim; // mask off ANIM_TOGGLEBIT - int torsoAnim; // mask off ANIM_TOGGLEBIT -} aas_entityinfo_t; - -// area info -typedef struct aas_areainfo_s -{ - int contents; - int flags; - int presencetype; - int cluster; - vec3_t mins; - vec3_t maxs; - vec3_t center; -} aas_areainfo_t; - -// client movement prediction stop events, stop as soon as: -#define SE_NONE 0 -#define SE_HITGROUND 1 // the ground is hit -#define SE_LEAVEGROUND 2 // there's no ground -#define SE_ENTERWATER 4 // water is entered -#define SE_ENTERSLIME 8 // slime is entered -#define SE_ENTERLAVA 16 // lava is entered -#define SE_HITGROUNDDAMAGE 32 // the ground is hit with damage -#define SE_GAP 64 // there's a gap -#define SE_TOUCHJUMPPAD 128 // touching a jump pad area -#define SE_TOUCHTELEPORTER 256 // touching teleporter -#define SE_ENTERAREA 512 // the given stoparea is entered -#define SE_HITGROUNDAREA 1024 // a ground face in the area is hit -#define SE_HITBOUNDINGBOX 2048 // hit the specified bounding box -#define SE_TOUCHCLUSTERPORTAL 4096 // touching a cluster portal - -typedef struct aas_clientmove_s -{ - vec3_t endpos; //position at the end of movement prediction - int endarea; //area at end of movement prediction - vec3_t velocity; //velocity at the end of movement prediction - aas_trace_t trace; //last trace - int presencetype; //presence type at end of movement prediction - int stopevent; //event that made the prediction stop - int endcontents; //contents at the end of movement prediction - float time; //time predicted ahead - int frames; //number of frames predicted ahead -} aas_clientmove_t; - -// alternate route goals -#define ALTROUTEGOAL_ALL 1 -#define ALTROUTEGOAL_CLUSTERPORTALS 2 -#define ALTROUTEGOAL_VIEWPORTALS 4 - -typedef struct aas_altroutegoal_s -{ - vec3_t origin; - int areanum; - unsigned short starttraveltime; - unsigned short goaltraveltime; - unsigned short extratraveltime; -} aas_altroutegoal_t; - -// route prediction stop events -#define RSE_NONE 0 -#define RSE_NOROUTE 1 //no route to goal -#define RSE_USETRAVELTYPE 2 //stop as soon as on of the given travel types is used -#define RSE_ENTERCONTENTS 4 //stop when entering the given contents -#define RSE_ENTERAREA 8 //stop when entering the given area - -typedef struct aas_predictroute_s -{ - vec3_t endpos; //position at the end of movement prediction - int endarea; //area at end of movement prediction - int stopevent; //event that made the prediction stop - int endcontents; //contents at the end of movement prediction - int endtravelflags; //end travel flags - int numareas; //number of areas predicted ahead - int time; //time predicted ahead (in hundreth of a sec) -} aas_predictroute_t; diff --git a/tools/quake3/bspc/deps/botlib/be_aas_bsp.h b/tools/quake3/bspc/deps/botlib/be_aas_bsp.h deleted file mode 100644 index 932874a1..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_bsp.h +++ /dev/null @@ -1,89 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_bsp.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_bsp.h $ - * - *****************************************************************************/ - -#ifdef AASINTERN -//loads the given BSP file -int AAS_LoadBSPFile(void); -//dump the loaded BSP data -void AAS_DumpBSPData(void); -//unlink the given entity from the bsp tree leaves -void AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves); -//link the given entity to the bsp tree leaves of the given model -bsp_link_t *AAS_BSPLinkEntity(vec3_t absmins, - vec3_t absmaxs, - int entnum, - int modelnum); - -//calculates collision with given entity -qboolean AAS_EntityCollision(int entnum, - vec3_t start, - vec3_t boxmins, - vec3_t boxmaxs, - vec3_t end, - int contentmask, - bsp_trace_t *trace); -//for debugging -void AAS_PrintFreeBSPLinks(char *str); -// -#endif //AASINTERN - -#define MAX_EPAIRKEY 128 - -//trace through the world -bsp_trace_t AAS_Trace( vec3_t start, - vec3_t mins, - vec3_t maxs, - vec3_t end, - int passent, - int contentmask); -//returns the contents at the given point -int AAS_PointContents(vec3_t point); -//returns true when p2 is in the PVS of p1 -qboolean AAS_inPVS(vec3_t p1, vec3_t p2); -//returns true when p2 is in the PHS of p1 -qboolean AAS_inPHS(vec3_t p1, vec3_t p2); -//returns true if the given areas are connected -qboolean AAS_AreasConnected(int area1, int area2); -//creates a list with entities totally or partly within the given box -int AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount); -//gets the mins, maxs and origin of a BSP model -void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin); -//handle to the next bsp entity -int AAS_NextBSPEntity(int ent); -//return the value of the BSP epair key -int AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size); -//get a vector for the BSP epair key -int AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v); -//get a float for the BSP epair key -int AAS_FloatForBSPEpairKey(int ent, char *key, float *value); -//get an integer for the BSP epair key -int AAS_IntForBSPEpairKey(int ent, char *key, int *value); - diff --git a/tools/quake3/bspc/deps/botlib/be_aas_bspq3.c b/tools/quake3/bspc/deps/botlib/be_aas_bspq3.c deleted file mode 100644 index 5ec7fe74..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_bspq3.c +++ /dev/null @@ -1,487 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_bspq3.c - * - * desc: BSP, Environment Sampling - * - * $Archive: /MissionPack/code/botlib/be_aas_bspq3.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_aas_def.h" - -extern botlib_import_t botimport; - -//#define TRACE_DEBUG - -#define ON_EPSILON 0.005 -//#define DEG2RAD( a ) (( a * M_PI ) / 180.0F) - -#define MAX_BSPENTITIES 2048 - -typedef struct rgb_s -{ - int red; - int green; - int blue; -} rgb_t; - -//bsp entity epair -typedef struct bsp_epair_s -{ - char *key; - char *value; - struct bsp_epair_s *next; -} bsp_epair_t; - -//bsp data entity -typedef struct bsp_entity_s -{ - bsp_epair_t *epairs; -} bsp_entity_t; - -//id Sofware BSP data -typedef struct bsp_s -{ - //true when bsp file is loaded - int loaded; - //entity data - int entdatasize; - char *dentdata; - //bsp entities - int numentities; - bsp_entity_t entities[MAX_BSPENTITIES]; -} bsp_t; - -//global bsp -bsp_t bspworld; - - -#ifdef BSP_DEBUG -typedef struct cname_s -{ - int value; - char *name; -} cname_t; - -cname_t contentnames[] = -{ - {CONTENTS_SOLID,"CONTENTS_SOLID"}, - {CONTENTS_WINDOW,"CONTENTS_WINDOW"}, - {CONTENTS_AUX,"CONTENTS_AUX"}, - {CONTENTS_LAVA,"CONTENTS_LAVA"}, - {CONTENTS_SLIME,"CONTENTS_SLIME"}, - {CONTENTS_WATER,"CONTENTS_WATER"}, - {CONTENTS_MIST,"CONTENTS_MIST"}, - {LAST_VISIBLE_CONTENTS,"LAST_VISIBLE_CONTENTS"}, - - {CONTENTS_AREAPORTAL,"CONTENTS_AREAPORTAL"}, - {CONTENTS_PLAYERCLIP,"CONTENTS_PLAYERCLIP"}, - {CONTENTS_MONSTERCLIP,"CONTENTS_MONSTERCLIP"}, - {CONTENTS_CURRENT_0,"CONTENTS_CURRENT_0"}, - {CONTENTS_CURRENT_90,"CONTENTS_CURRENT_90"}, - {CONTENTS_CURRENT_180,"CONTENTS_CURRENT_180"}, - {CONTENTS_CURRENT_270,"CONTENTS_CURRENT_270"}, - {CONTENTS_CURRENT_UP,"CONTENTS_CURRENT_UP"}, - {CONTENTS_CURRENT_DOWN,"CONTENTS_CURRENT_DOWN"}, - {CONTENTS_ORIGIN,"CONTENTS_ORIGIN"}, - {CONTENTS_MONSTER,"CONTENTS_MONSTER"}, - {CONTENTS_DEADMONSTER,"CONTENTS_DEADMONSTER"}, - {CONTENTS_DETAIL,"CONTENTS_DETAIL"}, - {CONTENTS_TRANSLUCENT,"CONTENTS_TRANSLUCENT"}, - {CONTENTS_LADDER,"CONTENTS_LADDER"}, - {0, 0} -}; - -void PrintContents(int contents) -{ - int i; - - for (i = 0; contentnames[i].value; i++) - { - if (contents & contentnames[i].value) - { - botimport.Print(PRT_MESSAGE, "%s\n", contentnames[i].name); - } //end if - } //end for -} //end of the function PrintContents - -#endif // BSP_DEBUG -//=========================================================================== -// traces axial boxes of any size through the world -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bsp_trace_t AAS_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) -{ - bsp_trace_t bsptrace; - botimport.Trace(&bsptrace, start, mins, maxs, end, passent, contentmask); - return bsptrace; -} //end of the function AAS_Trace -//=========================================================================== -// returns the contents at the given point -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_PointContents(vec3_t point) -{ - return botimport.PointContents(point); -} //end of the function AAS_PointContents -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_EntityCollision(int entnum, - vec3_t start, vec3_t boxmins, vec3_t boxmaxs, vec3_t end, - int contentmask, bsp_trace_t *trace) -{ - bsp_trace_t enttrace; - - botimport.EntityTrace(&enttrace, start, boxmins, boxmaxs, end, entnum, contentmask); - if (enttrace.fraction < trace->fraction) - { - Com_Memcpy(trace, &enttrace, sizeof(bsp_trace_t)); - return qtrue; - } //end if - return qfalse; -} //end of the function AAS_EntityCollision -//=========================================================================== -// returns true if in Potentially Hearable Set -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_inPVS(vec3_t p1, vec3_t p2) -{ - return botimport.inPVS(p1, p2); -} //end of the function AAS_InPVS -//=========================================================================== -// returns true if in Potentially Visible Set -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_inPHS(vec3_t p1, vec3_t p2) -{ - return qtrue; -} //end of the function AAS_inPHS -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin) -{ - botimport.BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); -} //end of the function AAS_BSPModelMinsMaxs -//=========================================================================== -// unlinks the entity from all leaves -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves) -{ -} //end of the function AAS_UnlinkFromBSPLeaves -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bsp_link_t *AAS_BSPLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum, int modelnum) -{ - return NULL; -} //end of the function AAS_BSPLinkEntity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount) -{ - return 0; -} //end of the function AAS_BoxEntities -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_NextBSPEntity(int ent) -{ - ent++; - if (ent >= 1 && ent < bspworld.numentities) return ent; - return 0; -} //end of the function AAS_NextBSPEntity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_BSPEntityInRange(int ent) -{ - if (ent <= 0 || ent >= bspworld.numentities) - { - botimport.Print(PRT_MESSAGE, "bsp entity out of range\n"); - return qfalse; - } //end if - return qtrue; -} //end of the function AAS_BSPEntityInRange -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size) -{ - bsp_epair_t *epair; - - value[0] = '\0'; - if (!AAS_BSPEntityInRange(ent)) return qfalse; - for (epair = bspworld.entities[ent].epairs; epair; epair = epair->next) - { - if (!strcmp(epair->key, key)) - { - strncpy(value, epair->value, size-1); - value[size-1] = '\0'; - return qtrue; - } //end if - } //end for - return qfalse; -} //end of the function AAS_FindBSPEpair -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v) -{ - char buf[MAX_EPAIRKEY]; - double v1, v2, v3; - - VectorClear(v); - if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; - //scanf into doubles, then assign, so it is vec_t size independent - v1 = v2 = v3 = 0; - sscanf(buf, "%lf %lf %lf", &v1, &v2, &v3); - v[0] = v1; - v[1] = v2; - v[2] = v3; - return qtrue; -} //end of the function AAS_VectorForBSPEpairKey -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_FloatForBSPEpairKey(int ent, char *key, float *value) -{ - char buf[MAX_EPAIRKEY]; - - *value = 0; - if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; - *value = atof(buf); - return qtrue; -} //end of the function AAS_FloatForBSPEpairKey -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_IntForBSPEpairKey(int ent, char *key, int *value) -{ - char buf[MAX_EPAIRKEY]; - - *value = 0; - if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; - *value = atoi(buf); - return qtrue; -} //end of the function AAS_IntForBSPEpairKey -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeBSPEntities(void) -{ - int i; - bsp_entity_t *ent; - bsp_epair_t *epair, *nextepair; - - for (i = 1; i < bspworld.numentities; i++) - { - ent = &bspworld.entities[i]; - for (epair = ent->epairs; epair; epair = nextepair) - { - nextepair = epair->next; - // - if (epair->key) FreeMemory(epair->key); - if (epair->value) FreeMemory(epair->value); - FreeMemory(epair); - } //end for - } //end for - bspworld.numentities = 0; -} //end of the function AAS_FreeBSPEntities -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ParseBSPEntities(void) -{ - script_t *script; - token_t token; - bsp_entity_t *ent; - bsp_epair_t *epair; - - script = LoadScriptMemory(bspworld.dentdata, bspworld.entdatasize, "entdata"); - SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES|SCFL_NOSTRINGESCAPECHARS);//SCFL_PRIMITIVE); - - bspworld.numentities = 1; - - while(PS_ReadToken(script, &token)) - { - if (strcmp(token.string, "{")) - { - ScriptError(script, "invalid %s\n", token.string); - AAS_FreeBSPEntities(); - FreeScript(script); - return; - } //end if - if (bspworld.numentities >= MAX_BSPENTITIES) - { - botimport.Print(PRT_MESSAGE, "too many entities in BSP file\n"); - break; - } //end if - ent = &bspworld.entities[bspworld.numentities]; - bspworld.numentities++; - ent->epairs = NULL; - while(PS_ReadToken(script, &token)) - { - if (!strcmp(token.string, "}")) break; - epair = (bsp_epair_t *) GetClearedHunkMemory(sizeof(bsp_epair_t)); - epair->next = ent->epairs; - ent->epairs = epair; - if (token.type != TT_STRING) - { - ScriptError(script, "invalid %s\n", token.string); - AAS_FreeBSPEntities(); - FreeScript(script); - return; - } //end if - StripDoubleQuotes(token.string); - epair->key = (char *) GetHunkMemory(strlen(token.string) + 1); - strcpy(epair->key, token.string); - if (!PS_ExpectTokenType(script, TT_STRING, 0, &token)) - { - AAS_FreeBSPEntities(); - FreeScript(script); - return; - } //end if - StripDoubleQuotes(token.string); - epair->value = (char *) GetHunkMemory(strlen(token.string) + 1); - strcpy(epair->value, token.string); - } //end while - if (strcmp(token.string, "}")) - { - ScriptError(script, "missing }\n"); - AAS_FreeBSPEntities(); - FreeScript(script); - return; - } //end if - } //end while - FreeScript(script); -} //end of the function AAS_ParseBSPEntities -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_BSPTraceLight(vec3_t start, vec3_t end, vec3_t endpos, int *red, int *green, int *blue) -{ - return 0; -} //end of the function AAS_BSPTraceLight -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DumpBSPData(void) -{ - AAS_FreeBSPEntities(); - - if (bspworld.dentdata) FreeMemory(bspworld.dentdata); - bspworld.dentdata = NULL; - bspworld.entdatasize = 0; - // - bspworld.loaded = qfalse; - Com_Memset( &bspworld, 0, sizeof(bspworld) ); -} //end of the function AAS_DumpBSPData -//=========================================================================== -// load an bsp file -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_LoadBSPFile(void) -{ - AAS_DumpBSPData(); - bspworld.entdatasize = strlen(botimport.BSPEntityData()) + 1; - bspworld.dentdata = (char *) GetClearedHunkMemory(bspworld.entdatasize); - Com_Memcpy(bspworld.dentdata, botimport.BSPEntityData(), bspworld.entdatasize); - AAS_ParseBSPEntities(); - bspworld.loaded = qtrue; - return BLERR_NOERROR; -} //end of the function AAS_LoadBSPFile diff --git a/tools/quake3/bspc/deps/botlib/be_aas_cluster.c b/tools/quake3/bspc/deps/botlib/be_aas_cluster.c deleted file mode 100644 index d1ea9e1b..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_cluster.c +++ /dev/null @@ -1,1545 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_cluster.c - * - * desc: area clustering - * - * $Archive: /MissionPack/code/botlib/be_aas_cluster.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "l_log.h" -#include "l_memory.h" -#include "l_libvar.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_aas_def.h" - -extern botlib_import_t botimport; - -#define AAS_MAX_PORTALS 65536 -#define AAS_MAX_PORTALINDEXSIZE 65536 -#define AAS_MAX_CLUSTERS 65536 -// -#define MAX_PORTALAREAS 1024 - -// do not flood through area faces, only use reachabilities -int nofaceflood = qtrue; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_RemoveClusterAreas(void) -{ - int i; - - for (i = 1; i < aasworld.numareas; i++) - { - aasworld.areasettings[i].cluster = 0; - } //end for -} //end of the function AAS_RemoveClusterAreas -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ClearCluster(int clusternum) -{ - int i; - - for (i = 1; i < aasworld.numareas; i++) - { - if (aasworld.areasettings[i].cluster == clusternum) - { - aasworld.areasettings[i].cluster = 0; - } //end if - } //end for -} //end of the function AAS_ClearCluster -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_RemovePortalsClusterReference(int clusternum) -{ - int portalnum; - - for (portalnum = 1; portalnum < aasworld.numportals; portalnum++) - { - if (aasworld.portals[portalnum].frontcluster == clusternum) - { - aasworld.portals[portalnum].frontcluster = 0; - } //end if - if (aasworld.portals[portalnum].backcluster == clusternum) - { - aasworld.portals[portalnum].backcluster = 0; - } //end if - } //end for -} //end of the function AAS_RemovePortalsClusterReference -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_UpdatePortal(int areanum, int clusternum) -{ - int portalnum; - aas_portal_t *portal; - aas_cluster_t *cluster; - - //find the portal of the area - for (portalnum = 1; portalnum < aasworld.numportals; portalnum++) - { - if (aasworld.portals[portalnum].areanum == areanum) break; - } //end for - // - if (portalnum == aasworld.numportals) - { - AAS_Error("no portal of area %d", areanum); - return qtrue; - } //end if - // - portal = &aasworld.portals[portalnum]; - //if the portal is already fully updated - if (portal->frontcluster == clusternum) return qtrue; - if (portal->backcluster == clusternum) return qtrue; - //if the portal has no front cluster yet - if (!portal->frontcluster) - { - portal->frontcluster = clusternum; - } //end if - //if the portal has no back cluster yet - else if (!portal->backcluster) - { - portal->backcluster = clusternum; - } //end else if - else - { - //remove the cluster portal flag contents - aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; - Log_Write("portal area %d is seperating more than two clusters\r\n", areanum); - return qfalse; - } //end else - if (aasworld.portalindexsize >= AAS_MAX_PORTALINDEXSIZE) - { - AAS_Error("AAS_MAX_PORTALINDEXSIZE"); - return qtrue; - } //end if - //set the area cluster number to the negative portal number - aasworld.areasettings[areanum].cluster = -portalnum; - //add the portal to the cluster using the portal index - cluster = &aasworld.clusters[clusternum]; - aasworld.portalindex[cluster->firstportal + cluster->numportals] = portalnum; - aasworld.portalindexsize++; - cluster->numportals++; - return qtrue; -} //end of the function AAS_UpdatePortal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_FloodClusterAreas_r(int areanum, int clusternum) -{ - aas_area_t *area; - aas_face_t *face; - int facenum, i; - - // - if (areanum <= 0 || areanum >= aasworld.numareas) - { - AAS_Error("AAS_FloodClusterAreas_r: areanum out of range"); - return qfalse; - } //end if - //if the area is already part of a cluster - if (aasworld.areasettings[areanum].cluster > 0) - { - if (aasworld.areasettings[areanum].cluster == clusternum) return qtrue; - // - //there's a reachability going from one cluster to another only in one direction - // - AAS_Error("cluster %d touched cluster %d at area %d\r\n", - clusternum, aasworld.areasettings[areanum].cluster, areanum); - return qfalse; - } //end if - //don't add the cluster portal areas to the clusters - if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) - { - return AAS_UpdatePortal(areanum, clusternum); - } //end if - //set the area cluster number - aasworld.areasettings[areanum].cluster = clusternum; - aasworld.areasettings[areanum].clusterareanum = - aasworld.clusters[clusternum].numareas; - //the cluster has an extra area - aasworld.clusters[clusternum].numareas++; - - area = &aasworld.areas[areanum]; - //use area faces to flood into adjacent areas - if (!nofaceflood) - { - for (i = 0; i < area->numfaces; i++) - { - facenum = abs(aasworld.faceindex[area->firstface + i]); - face = &aasworld.faces[facenum]; - if (face->frontarea == areanum) - { - if (face->backarea) if (!AAS_FloodClusterAreas_r(face->backarea, clusternum)) return qfalse; - } //end if - else - { - if (face->frontarea) if (!AAS_FloodClusterAreas_r(face->frontarea, clusternum)) return qfalse; - } //end else - } //end for - } //end if - //use the reachabilities to flood into other areas - for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++) - { - if (!aasworld.reachability[ - aasworld.areasettings[areanum].firstreachablearea + i].areanum) - { - continue; - } //end if - if (!AAS_FloodClusterAreas_r(aasworld.reachability[ - aasworld.areasettings[areanum].firstreachablearea + i].areanum, clusternum)) return qfalse; - } //end for - return qtrue; -} //end of the function AAS_FloodClusterAreas_r -//=========================================================================== -// try to flood from all areas without cluster into areas with a cluster set -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_FloodClusterAreasUsingReachabilities(int clusternum) -{ - int i, j, areanum; - - for (i = 1; i < aasworld.numareas; i++) - { - //if this area already has a cluster set - if (aasworld.areasettings[i].cluster) - continue; - //if this area is a cluster portal - if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) - continue; - //loop over the reachable areas from this area - for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) - { - //the reachable area - areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; - //if this area is a cluster portal - if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) - continue; - //if this area has a cluster set - if (aasworld.areasettings[areanum].cluster) - { - if (!AAS_FloodClusterAreas_r(i, clusternum)) - return qfalse; - i = 0; - break; - } //end if - } //end for - } //end for - return qtrue; -} //end of the function AAS_FloodClusterAreasUsingReachabilities -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_NumberClusterPortals(int clusternum) -{ - int i, portalnum; - aas_cluster_t *cluster; - aas_portal_t *portal; - - cluster = &aasworld.clusters[clusternum]; - for (i = 0; i < cluster->numportals; i++) - { - portalnum = aasworld.portalindex[cluster->firstportal + i]; - portal = &aasworld.portals[portalnum]; - if (portal->frontcluster == clusternum) - { - portal->clusterareanum[0] = cluster->numareas++; - } //end if - else - { - portal->clusterareanum[1] = cluster->numareas++; - } //end else - } //end for -} //end of the function AAS_NumberClusterPortals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_NumberClusterAreas(int clusternum) -{ - int i, portalnum; - aas_cluster_t *cluster; - aas_portal_t *portal; - - aasworld.clusters[clusternum].numareas = 0; - aasworld.clusters[clusternum].numreachabilityareas = 0; - //number all areas in this cluster WITH reachabilities - for (i = 1; i < aasworld.numareas; i++) - { - // - if (aasworld.areasettings[i].cluster != clusternum) continue; - // - if (!AAS_AreaReachability(i)) continue; - // - aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas; - //the cluster has an extra area - aasworld.clusters[clusternum].numareas++; - aasworld.clusters[clusternum].numreachabilityareas++; - } //end for - //number all portals in this cluster WITH reachabilities - cluster = &aasworld.clusters[clusternum]; - for (i = 0; i < cluster->numportals; i++) - { - portalnum = aasworld.portalindex[cluster->firstportal + i]; - portal = &aasworld.portals[portalnum]; - if (!AAS_AreaReachability(portal->areanum)) continue; - if (portal->frontcluster == clusternum) - { - portal->clusterareanum[0] = cluster->numareas++; - aasworld.clusters[clusternum].numreachabilityareas++; - } //end if - else - { - portal->clusterareanum[1] = cluster->numareas++; - aasworld.clusters[clusternum].numreachabilityareas++; - } //end else - } //end for - //number all areas in this cluster WITHOUT reachabilities - for (i = 1; i < aasworld.numareas; i++) - { - // - if (aasworld.areasettings[i].cluster != clusternum) continue; - // - if (AAS_AreaReachability(i)) continue; - // - aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas; - //the cluster has an extra area - aasworld.clusters[clusternum].numareas++; - } //end for - //number all portals in this cluster WITHOUT reachabilities - cluster = &aasworld.clusters[clusternum]; - for (i = 0; i < cluster->numportals; i++) - { - portalnum = aasworld.portalindex[cluster->firstportal + i]; - portal = &aasworld.portals[portalnum]; - if (AAS_AreaReachability(portal->areanum)) continue; - if (portal->frontcluster == clusternum) - { - portal->clusterareanum[0] = cluster->numareas++; - } //end if - else - { - portal->clusterareanum[1] = cluster->numareas++; - } //end else - } //end for -} //end of the function AAS_NumberClusterAreas -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_FindClusters(void) -{ - int i; - aas_cluster_t *cluster; - - AAS_RemoveClusterAreas(); - // - for (i = 1; i < aasworld.numareas; i++) - { - //if the area is already part of a cluster - if (aasworld.areasettings[i].cluster) - continue; - // if not flooding through faces only use areas that have reachabilities - if (nofaceflood) - { - if (!aasworld.areasettings[i].numreachableareas) - continue; - } //end if - //if the area is a cluster portal - if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) - continue; - if (aasworld.numclusters >= AAS_MAX_CLUSTERS) - { - AAS_Error("AAS_MAX_CLUSTERS"); - return qfalse; - } //end if - cluster = &aasworld.clusters[aasworld.numclusters]; - cluster->numareas = 0; - cluster->numreachabilityareas = 0; - cluster->firstportal = aasworld.portalindexsize; - cluster->numportals = 0; - //flood the areas in this cluster - if (!AAS_FloodClusterAreas_r(i, aasworld.numclusters)) - return qfalse; - if (!AAS_FloodClusterAreasUsingReachabilities(aasworld.numclusters)) - return qfalse; - //number the cluster areas - //AAS_NumberClusterPortals(aasworld.numclusters); - AAS_NumberClusterAreas(aasworld.numclusters); - //Log_Write("cluster %d has %d areas\r\n", aasworld.numclusters, cluster->numareas); - aasworld.numclusters++; - } //end for - return qtrue; -} //end of the function AAS_FindClusters -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CreatePortals(void) -{ - int i; - aas_portal_t *portal; - - for (i = 1; i < aasworld.numareas; i++) - { - //if the area is a cluster portal - if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) - { - if (aasworld.numportals >= AAS_MAX_PORTALS) - { - AAS_Error("AAS_MAX_PORTALS"); - return; - } //end if - portal = &aasworld.portals[aasworld.numportals]; - portal->areanum = i; - portal->frontcluster = 0; - portal->backcluster = 0; - aasworld.numportals++; - } //end if - } //end for -} //end of the function AAS_CreatePortals -/* -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_MapContainsTeleporters(void) -{ - bsp_entity_t *entities, *ent; - char *classname; - - entities = AAS_ParseBSPEntities(); - - for (ent = entities; ent; ent = ent->next) - { - classname = AAS_ValueForBSPEpairKey(ent, "classname"); - if (classname && !strcmp(classname, "misc_teleporter")) - { - AAS_FreeBSPEntities(entities); - return qtrue; - } //end if - } //end for - return qfalse; -} //end of the function AAS_MapContainsTeleporters -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_NonConvexFaces(aas_face_t *face1, aas_face_t *face2, int side1, int side2) -{ - int i, j, edgenum; - aas_plane_t *plane1, *plane2; - aas_edge_t *edge; - - - plane1 = &aasworld.planes[face1->planenum ^ side1]; - plane2 = &aasworld.planes[face2->planenum ^ side2]; - - //check if one of the points of face1 is at the back of the plane of face2 - for (i = 0; i < face1->numedges; i++) - { - edgenum = abs(aasworld.edgeindex[face1->firstedge + i]); - edge = &aasworld.edges[edgenum]; - for (j = 0; j < 2; j++) - { - if (DotProduct(plane2->normal, aasworld.vertexes[edge->v[j]]) - - plane2->dist < -0.01) return qtrue; - } //end for - } //end for - for (i = 0; i < face2->numedges; i++) - { - edgenum = abs(aasworld.edgeindex[face2->firstedge + i]); - edge = &aasworld.edges[edgenum]; - for (j = 0; j < 2; j++) - { - if (DotProduct(plane1->normal, aasworld.vertexes[edge->v[j]]) - - plane1->dist < -0.01) return qtrue; - } //end for - } //end for - - return qfalse; -} //end of the function AAS_NonConvexFaces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_CanMergeAreas(int *areanums, int numareas) -{ - int i, j, s, face1num, face2num, side1, side2, fn1, fn2; - aas_face_t *face1, *face2; - aas_area_t *area1, *area2; - - for (i = 0; i < numareas; i++) - { - area1 = &aasworld.areas[areanums[i]]; - for (fn1 = 0; fn1 < area1->numfaces; fn1++) - { - face1num = abs(aasworld.faceindex[area1->firstface + fn1]); - face1 = &aasworld.faces[face1num]; - side1 = face1->frontarea != areanums[i]; - //check if the face isn't a shared one with one of the other areas - for (s = 0; s < numareas; s++) - { - if (s == i) continue; - if (face1->frontarea == s || face1->backarea == s) break; - } //end for - //if the face was a shared one - if (s != numareas) continue; - // - for (j = 0; j < numareas; j++) - { - if (j == i) continue; - area2 = &aasworld.areas[areanums[j]]; - for (fn2 = 0; fn2 < area2->numfaces; fn2++) - { - face2num = abs(aasworld.faceindex[area2->firstface + fn2]); - face2 = &aasworld.faces[face2num]; - side2 = face2->frontarea != areanums[j]; - //check if the face isn't a shared one with one of the other areas - for (s = 0; s < numareas; s++) - { - if (s == j) continue; - if (face2->frontarea == s || face2->backarea == s) break; - } //end for - //if the face was a shared one - if (s != numareas) continue; - // - if (AAS_NonConvexFaces(face1, face2, side1, side2)) return qfalse; - } //end for - } //end for - } //end for - } //end for - return qtrue; -} //end of the function AAS_CanMergeAreas -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_NonConvexEdges(aas_edge_t *edge1, aas_edge_t *edge2, int side1, int side2, int planenum) -{ - int i; - vec3_t edgevec1, edgevec2, normal1, normal2; - float dist1, dist2; - aas_plane_t *plane; - - plane = &aasworld.planes[planenum]; - VectorSubtract(aasworld.vertexes[edge1->v[1]], aasworld.vertexes[edge1->v[0]], edgevec1); - VectorSubtract(aasworld.vertexes[edge2->v[1]], aasworld.vertexes[edge2->v[0]], edgevec2); - if (side1) VectorInverse(edgevec1); - if (side2) VectorInverse(edgevec2); - // - CrossProduct(edgevec1, plane->normal, normal1); - dist1 = DotProduct(normal1, aasworld.vertexes[edge1->v[0]]); - CrossProduct(edgevec2, plane->normal, normal2); - dist2 = DotProduct(normal2, aasworld.vertexes[edge2->v[0]]); - - for (i = 0; i < 2; i++) - { - if (DotProduct(aasworld.vertexes[edge1->v[i]], normal2) - dist2 < -0.01) return qfalse; - } //end for - for (i = 0; i < 2; i++) - { - if (DotProduct(aasworld.vertexes[edge2->v[i]], normal1) - dist1 < -0.01) return qfalse; - } //end for - return qtrue; -} //end of the function AAS_NonConvexEdges -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_CanMergeFaces(int *facenums, int numfaces, int planenum) -{ - int i, j, s, edgenum1, edgenum2, side1, side2, en1, en2, ens; - aas_face_t *face1, *face2, *otherface; - aas_edge_t *edge1, *edge2; - - for (i = 0; i < numfaces; i++) - { - face1 = &aasworld.faces[facenums[i]]; - for (en1 = 0; en1 < face1->numedges; en1++) - { - edgenum1 = aasworld.edgeindex[face1->firstedge + en1]; - side1 = (edgenum1 < 0) ^ (face1->planenum != planenum); - edgenum1 = abs(edgenum1); - edge1 = &aasworld.edges[edgenum1]; - //check if the edge is shared with another face - for (s = 0; s < numfaces; s++) - { - if (s == i) continue; - otherface = &aasworld.faces[facenums[s]]; - for (ens = 0; ens < otherface->numedges; ens++) - { - if (edgenum1 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break; - } //end for - if (ens != otherface->numedges) break; - } //end for - //if the edge was shared - if (s != numfaces) continue; - // - for (j = 0; j < numfaces; j++) - { - if (j == i) continue; - face2 = &aasworld.faces[facenums[j]]; - for (en2 = 0; en2 < face2->numedges; en2++) - { - edgenum2 = aasworld.edgeindex[face2->firstedge + en2]; - side2 = (edgenum2 < 0) ^ (face2->planenum != planenum); - edgenum2 = abs(edgenum2); - edge2 = &aasworld.edges[edgenum2]; - //check if the edge is shared with another face - for (s = 0; s < numfaces; s++) - { - if (s == i) continue; - otherface = &aasworld.faces[facenums[s]]; - for (ens = 0; ens < otherface->numedges; ens++) - { - if (edgenum2 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break; - } //end for - if (ens != otherface->numedges) break; - } //end for - //if the edge was shared - if (s != numfaces) continue; - // - if (AAS_NonConvexEdges(edge1, edge2, side1, side2, planenum)) return qfalse; - } //end for - } //end for - } //end for - } //end for - return qtrue; -} //end of the function AAS_CanMergeFaces*/ -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ConnectedAreas_r(int *areanums, int numareas, int *connectedareas, int curarea) -{ - int i, j, otherareanum, facenum; - aas_area_t *area; - aas_face_t *face; - - connectedareas[curarea] = qtrue; - area = &aasworld.areas[areanums[curarea]]; - for (i = 0; i < area->numfaces; i++) - { - facenum = abs(aasworld.faceindex[area->firstface + i]); - face = &aasworld.faces[facenum]; - //if the face is solid - if (face->faceflags & FACE_SOLID) continue; - //get the area at the other side of the face - if (face->frontarea != areanums[curarea]) otherareanum = face->frontarea; - else otherareanum = face->backarea; - //check if the face is leading to one of the other areas - for (j = 0; j < numareas; j++) - { - if (areanums[j] == otherareanum) break; - } //end for - //if the face isn't leading to one of the other areas - if (j == numareas) continue; - //if the other area is already connected - if (connectedareas[j]) continue; - //recursively proceed with the other area - AAS_ConnectedAreas_r(areanums, numareas, connectedareas, j); - } //end for -} //end of the function AAS_ConnectedAreas_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_ConnectedAreas(int *areanums, int numareas) -{ - int connectedareas[MAX_PORTALAREAS], i; - - Com_Memset(connectedareas, 0, sizeof(connectedareas)); - if (numareas < 1) return qfalse; - if (numareas == 1) return qtrue; - AAS_ConnectedAreas_r(areanums, numareas, connectedareas, 0); - for (i = 0; i < numareas; i++) - { - if (!connectedareas[i]) return qfalse; - } //end for - return qtrue; -} //end of the function AAS_ConnectedAreas -//=========================================================================== -// gets adjacent areas with less presence types recursively -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_GetAdjacentAreasWithLessPresenceTypes_r(int *areanums, int numareas, int curareanum) -{ - int i, j, presencetype, otherpresencetype, otherareanum, facenum; - aas_area_t *area; - aas_face_t *face; - - areanums[numareas++] = curareanum; - area = &aasworld.areas[curareanum]; - presencetype = aasworld.areasettings[curareanum].presencetype; - for (i = 0; i < area->numfaces; i++) - { - facenum = abs(aasworld.faceindex[area->firstface + i]); - face = &aasworld.faces[facenum]; - //if the face is solid - if (face->faceflags & FACE_SOLID) continue; - //the area at the other side of the face - if (face->frontarea != curareanum) otherareanum = face->frontarea; - else otherareanum = face->backarea; - // - otherpresencetype = aasworld.areasettings[otherareanum].presencetype; - //if the other area has less presence types - if ((presencetype & ~otherpresencetype) && - !(otherpresencetype & ~presencetype)) - { - //check if the other area isn't already in the list - for (j = 0; j < numareas; j++) - { - if (otherareanum == areanums[j]) break; - } //end for - //if the other area isn't already in the list - if (j == numareas) - { - if (numareas >= MAX_PORTALAREAS) - { - AAS_Error("MAX_PORTALAREAS"); - return numareas; - } //end if - numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, numareas, otherareanum); - } //end if - } //end if - } //end for - return numareas; -} //end of the function AAS_GetAdjacentAreasWithLessPresenceTypes_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_CheckAreaForPossiblePortals(int areanum) -{ - int i, j, k, fen, ben, frontedgenum, backedgenum, facenum; - int areanums[MAX_PORTALAREAS], numareas, otherareanum; - int numareafrontfaces[MAX_PORTALAREAS], numareabackfaces[MAX_PORTALAREAS]; - int frontfacenums[MAX_PORTALAREAS], backfacenums[MAX_PORTALAREAS]; - int numfrontfaces, numbackfaces; - int frontareanums[MAX_PORTALAREAS], backareanums[MAX_PORTALAREAS]; - int numfrontareas, numbackareas; - int frontplanenum, backplanenum, faceplanenum; - aas_area_t *area; - aas_face_t *frontface, *backface, *face; - - //if it isn't already a portal - if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0; - //it must be a grounded area - if (!(aasworld.areasettings[areanum].areaflags & AREA_GROUNDED)) return 0; - // - Com_Memset(numareafrontfaces, 0, sizeof(numareafrontfaces)); - Com_Memset(numareabackfaces, 0, sizeof(numareabackfaces)); - numareas = numfrontfaces = numbackfaces = 0; - numfrontareas = numbackareas = 0; - frontplanenum = backplanenum = -1; - //add any adjacent areas with less presence types - numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, 0, areanum); - // - for (i = 0; i < numareas; i++) - { - area = &aasworld.areas[areanums[i]]; - for (j = 0; j < area->numfaces; j++) - { - facenum = abs(aasworld.faceindex[area->firstface + j]); - face = &aasworld.faces[facenum]; - //if the face is solid - if (face->faceflags & FACE_SOLID) continue; - //check if the face is shared with one of the other areas - for (k = 0; k < numareas; k++) - { - if (k == i) continue; - if (face->frontarea == areanums[k] || face->backarea == areanums[k]) break; - } //end for - //if the face is shared - if (k != numareas) continue; - //the number of the area at the other side of the face - if (face->frontarea == areanums[i]) otherareanum = face->backarea; - else otherareanum = face->frontarea; - //if the other area already is a cluter portal - if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0; - //number of the plane of the area - faceplanenum = face->planenum & ~1; - // - if (frontplanenum < 0 || faceplanenum == frontplanenum) - { - frontplanenum = faceplanenum; - frontfacenums[numfrontfaces++] = facenum; - for (k = 0; k < numfrontareas; k++) - { - if (frontareanums[k] == otherareanum) break; - } //end for - if (k == numfrontareas) frontareanums[numfrontareas++] = otherareanum; - numareafrontfaces[i]++; - } //end if - else if (backplanenum < 0 || faceplanenum == backplanenum) - { - backplanenum = faceplanenum; - backfacenums[numbackfaces++] = facenum; - for (k = 0; k < numbackareas; k++) - { - if (backareanums[k] == otherareanum) break; - } //end for - if (k == numbackareas) backareanums[numbackareas++] = otherareanum; - numareabackfaces[i]++; - } //end else - else - { - return 0; - } //end else - } //end for - } //end for - //every area should have at least one front face and one back face - for (i = 0; i < numareas; i++) - { - if (!numareafrontfaces[i] || !numareabackfaces[i]) return 0; - } //end for - //the front areas should all be connected - if (!AAS_ConnectedAreas(frontareanums, numfrontareas)) return 0; - //the back areas should all be connected - if (!AAS_ConnectedAreas(backareanums, numbackareas)) return 0; - //none of the front faces should have a shared edge with a back face - for (i = 0; i < numfrontfaces; i++) - { - frontface = &aasworld.faces[frontfacenums[i]]; - for (fen = 0; fen < frontface->numedges; fen++) - { - frontedgenum = abs(aasworld.edgeindex[frontface->firstedge + fen]); - for (j = 0; j < numbackfaces; j++) - { - backface = &aasworld.faces[backfacenums[j]]; - for (ben = 0; ben < backface->numedges; ben++) - { - backedgenum = abs(aasworld.edgeindex[backface->firstedge + ben]); - if (frontedgenum == backedgenum) break; - } //end for - if (ben != backface->numedges) break; - } //end for - if (j != numbackfaces) break; - } //end for - if (fen != frontface->numedges) break; - } //end for - if (i != numfrontfaces) return 0; - //set the cluster portal contents - for (i = 0; i < numareas; i++) - { - aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_CLUSTERPORTAL; - //this area can be used as a route portal - aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_ROUTEPORTAL; - Log_Write("possible portal: %d\r\n", areanums[i]); - } //end for - // - return numareas; -} //end of the function AAS_CheckAreaForPossiblePortals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FindPossiblePortals(void) -{ - int i, numpossibleportals; - - numpossibleportals = 0; - for (i = 1; i < aasworld.numareas; i++) - { - numpossibleportals += AAS_CheckAreaForPossiblePortals(i); - } //end for - botimport.Print(PRT_MESSAGE, "\r%6d possible portal areas\n", numpossibleportals); -} //end of the function AAS_FindPossiblePortals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_RemoveAllPortals(void) -{ - int i; - - for (i = 1; i < aasworld.numareas; i++) - { - aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; - } //end for -} //end of the function AAS_RemoveAllPortals - -#if 0 -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FloodCluster_r(int areanum, int clusternum) -{ - int i, otherareanum; - aas_face_t *face; - aas_area_t *area; - - //set cluster mark - aasworld.areasettings[areanum].cluster = clusternum; - //if the area is a portal - //if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return; - // - area = &aasworld.areas[areanum]; - //use area faces to flood into adjacent areas - for (i = 0; i < area->numfaces; i++) - { - face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; - // - if (face->frontarea != areanum) otherareanum = face->frontarea; - else otherareanum = face->backarea; - //if there's no area at the other side - if (!otherareanum) continue; - //if the area is a portal - if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; - //if the area is already marked - if (aasworld.areasettings[otherareanum].cluster) continue; - // - AAS_FloodCluster_r(otherareanum, clusternum); - } //end for - //use the reachabilities to flood into other areas - for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++) - { - otherareanum = aasworld.reachability[ - aasworld.areasettings[areanum].firstreachablearea + i].areanum; - if (!otherareanum) - { - continue; - AAS_Error("reachability %d has zero area\n", aasworld.areasettings[areanum].firstreachablearea + i); - } //end if - //if the area is a portal - if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; - //if the area is already marked - if (aasworld.areasettings[otherareanum].cluster) continue; - // - AAS_FloodCluster_r(otherareanum, clusternum); - } //end for -} //end of the function AAS_FloodCluster_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_RemoveTeleporterPortals(void) -{ - int i, j, areanum; - - for (i = 1; i < aasworld.numareas; i++) - { - for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) - { - areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; - if (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype == TRAVEL_TELEPORT) - { - aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; - aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; - break; - } //end if - } //end for - } //end for -} //end of the function AAS_RemoveTeleporterPortals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FloodClusterReachabilities(int clusternum) -{ - int i, j, areanum; - - for (i = 1; i < aasworld.numareas; i++) - { - //if this area already has a cluster set - if (aasworld.areasettings[i].cluster) continue; - //if this area is a cluster portal - if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) continue; - //loop over the reachable areas from this area - for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) - { - //the reachable area - areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; - //if this area is a cluster portal - if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; - //if this area has a cluster set - if (aasworld.areasettings[areanum].cluster == clusternum) - { - AAS_FloodCluster_r(i, clusternum); - i = 0; - break; - } //end if - } //end for - } //end for -} //end of the function AAS_FloodClusterReachabilities - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_RemoveNotClusterClosingPortals(void) -{ - int i, j, k, facenum, otherareanum, nonclosingportals; - aas_area_t *area; - aas_face_t *face; - - AAS_RemoveTeleporterPortals(); - // - nonclosingportals = 0; - for (i = 1; i < aasworld.numareas; i++) - { - if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue; - //find a non-portal area adjacent to the portal area and flood - //the cluster from there - area = &aasworld.areas[i]; - for (j = 0; j < area->numfaces; j++) - { - facenum = abs(aasworld.faceindex[area->firstface + j]); - face = &aasworld.faces[facenum]; - // - if (face->frontarea != i) otherareanum = face->frontarea; - else otherareanum = face->backarea; - // - if (!otherareanum) continue; - // - if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) - { - continue; - } //end if - //reset all cluster fields - AAS_RemoveClusterAreas(); - // - AAS_FloodCluster_r(otherareanum, 1); - AAS_FloodClusterReachabilities(1); - //check if all adjacent non-portal areas have a cluster set - for (k = 0; k < area->numfaces; k++) - { - facenum = abs(aasworld.faceindex[area->firstface + k]); - face = &aasworld.faces[facenum]; - // - if (face->frontarea != i) otherareanum = face->frontarea; - else otherareanum = face->backarea; - // - if (!otherareanum) continue; - // - if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) - { - continue; - } //end if - // - if (!aasworld.areasettings[otherareanum].cluster) break; - } //end for - //if all adjacent non-portal areas have a cluster set then the portal - //didn't seal a cluster - if (k >= area->numfaces) - { - aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; - nonclosingportals++; - //recheck all the other portals again - i = 0; - break; - } //end if - } //end for - } //end for - botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals); -} //end of the function AAS_RemoveNotClusterClosingPortals - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== - -void AAS_RemoveNotClusterClosingPortals(void) -{ - int i, j, facenum, otherareanum, nonclosingportals, numseperatedclusters; - aas_area_t *area; - aas_face_t *face; - - AAS_RemoveTeleporterPortals(); - // - nonclosingportals = 0; - for (i = 1; i < aasworld.numareas; i++) - { - if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue; - // - numseperatedclusters = 0; - //reset all cluster fields - AAS_RemoveClusterAreas(); - //find a non-portal area adjacent to the portal area and flood - //the cluster from there - area = &aasworld.areas[i]; - for (j = 0; j < area->numfaces; j++) - { - facenum = abs(aasworld.faceindex[area->firstface + j]); - face = &aasworld.faces[facenum]; - // - if (face->frontarea != i) otherareanum = face->frontarea; - else otherareanum = face->backarea; - //if not solid at the other side of the face - if (!otherareanum) continue; - //don't flood into other portals - if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; - //if the area already has a cluster set - if (aasworld.areasettings[otherareanum].cluster) continue; - //another cluster is seperated by this portal - numseperatedclusters++; - //flood the cluster - AAS_FloodCluster_r(otherareanum, numseperatedclusters); - AAS_FloodClusterReachabilities(numseperatedclusters); - } //end for - //use the reachabilities to flood into other areas - for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) - { - otherareanum = aasworld.reachability[ - aasworld.areasettings[i].firstreachablearea + j].areanum; - //this should never be qtrue but we check anyway - if (!otherareanum) continue; - //don't flood into other portals - if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; - //if the area already has a cluster set - if (aasworld.areasettings[otherareanum].cluster) continue; - //another cluster is seperated by this portal - numseperatedclusters++; - //flood the cluster - AAS_FloodCluster_r(otherareanum, numseperatedclusters); - AAS_FloodClusterReachabilities(numseperatedclusters); - } //end for - //a portal must seperate no more and no less than 2 clusters - if (numseperatedclusters != 2) - { - aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; - nonclosingportals++; - //recheck all the other portals again - i = 0; - } //end if - } //end for - botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals); -} //end of the function AAS_RemoveNotClusterClosingPortals - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== - -void AAS_AddTeleporterPortals(void) -{ - int j, area2num, facenum, otherareanum; - char *target, *targetname, *classname; - bsp_entity_t *entities, *ent, *dest; - vec3_t origin, destorigin, mins, maxs, end; - vec3_t bbmins, bbmaxs; - aas_area_t *area; - aas_face_t *face; - aas_trace_t trace; - aas_link_t *areas, *link; - - entities = AAS_ParseBSPEntities(); - - for (ent = entities; ent; ent = ent->next) - { - classname = AAS_ValueForBSPEpairKey(ent, "classname"); - if (classname && !strcmp(classname, "misc_teleporter")) - { - if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) - { - botimport.Print(PRT_ERROR, "teleporter (%s) without origin\n", target); - continue; - } //end if - // - target = AAS_ValueForBSPEpairKey(ent, "target"); - if (!target) - { - botimport.Print(PRT_ERROR, "teleporter (%s) without target\n", target); - continue; - } //end if - for (dest = entities; dest; dest = dest->next) - { - classname = AAS_ValueForBSPEpairKey(dest, "classname"); - if (classname && !strcmp(classname, "misc_teleporter_dest")) - { - targetname = AAS_ValueForBSPEpairKey(dest, "targetname"); - if (targetname && !strcmp(targetname, target)) - { - break; - } //end if - } //end if - } //end for - if (!dest) - { - botimport.Print(PRT_ERROR, "teleporter without destination (%s)\n", target); - continue; - } //end if - if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin)) - { - botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target); - continue; - } //end if - destorigin[2] += 24; //just for q2e1m2, the dork has put the telepads in the ground - VectorCopy(destorigin, end); - end[2] -= 100; - trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1); - if (trace.startsolid) - { - botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target); - continue; - } //end if - VectorCopy(trace.endpos, destorigin); - area2num = AAS_PointAreaNum(destorigin); - //reset all cluster fields - for (j = 0; j < aasworld.numareas; j++) - { - aasworld.areasettings[j].cluster = 0; - } //end for - // - VectorSet(mins, -8, -8, 8); - VectorSet(maxs, 8, 8, 24); - // - AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs); - // - VectorAdd(origin, mins, mins); - VectorAdd(origin, maxs, maxs); - //add bounding box size - VectorSubtract(mins, bbmaxs, mins); - VectorSubtract(maxs, bbmins, maxs); - //link an invalid (-1) entity - areas = AAS_AASLinkEntity(mins, maxs, -1); - // - for (link = areas; link; link = link->next_area) - { - if (!AAS_AreaGrounded(link->areanum)) continue; - //add the teleporter portal mark - aasworld.areasettings[link->areanum].contents |= AREACONTENTS_CLUSTERPORTAL | - AREACONTENTS_TELEPORTAL; - } //end for - // - for (link = areas; link; link = link->next_area) - { - if (!AAS_AreaGrounded(link->areanum)) continue; - //find a non-portal area adjacent to the portal area and flood - //the cluster from there - area = &aasworld.areas[link->areanum]; - for (j = 0; j < area->numfaces; j++) - { - facenum = abs(aasworld.faceindex[area->firstface + j]); - face = &aasworld.faces[facenum]; - // - if (face->frontarea != link->areanum) otherareanum = face->frontarea; - else otherareanum = face->backarea; - // - if (!otherareanum) continue; - // - if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) - { - continue; - } //end if - // - AAS_FloodCluster_r(otherareanum, 1); - } //end for - } //end for - //if the teleport destination IS in the same cluster - if (aasworld.areasettings[area2num].cluster) - { - for (link = areas; link; link = link->next_area) - { - if (!AAS_AreaGrounded(link->areanum)) continue; - //add the teleporter portal mark - aasworld.areasettings[link->areanum].contents &= ~(AREACONTENTS_CLUSTERPORTAL | - AREACONTENTS_TELEPORTAL); - } //end for - } //end if - } //end if - } //end for - AAS_FreeBSPEntities(entities); -} //end of the function AAS_AddTeleporterPortals - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_AddTeleporterPortals(void) -{ - int i, j, areanum; - - for (i = 1; i < aasworld.numareas; i++) - { - for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) - { - if (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype != TRAVEL_TELEPORT) continue; - areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; - aasworld.areasettings[areanum].contents |= AREACONTENTS_CLUSTERPORTAL; - } //end for - } //end for -} //end of the function AAS_AddTeleporterPortals - -#endif - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_TestPortals(void) -{ - int i; - aas_portal_t *portal; - - for (i = 1; i < aasworld.numportals; i++) - { - portal = &aasworld.portals[i]; - if (!portal->frontcluster) - { - aasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; - Log_Write("portal area %d has no front cluster\r\n", portal->areanum); - return qfalse; - } //end if - if (!portal->backcluster) - { - aasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; - Log_Write("portal area %d has no back cluster\r\n", portal->areanum); - return qfalse; - } //end if - } //end for - return qtrue; -} //end of the function -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CountForcedClusterPortals(void) -{ - int num, i; - - num = 0; - for (i = 1; i < aasworld.numareas; i++) - { - if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) - { - Log_Write("area %d is a forced portal area\r\n", i); - num++; - } //end if - } //end for - botimport.Print(PRT_MESSAGE, "%6d forced portal areas\n", num); -} //end of the function AAS_CountForcedClusterPortals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CreateViewPortals(void) -{ - int i; - - for (i = 1; i < aasworld.numareas; i++) - { - if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) - { - aasworld.areasettings[i].contents |= AREACONTENTS_VIEWPORTAL; - } //end if - } //end for -} //end of the function AAS_CreateViewPortals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_SetViewPortalsAsClusterPortals(void) -{ - int i; - - for (i = 1; i < aasworld.numareas; i++) - { - if (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL) - { - aasworld.areasettings[i].contents |= AREACONTENTS_CLUSTERPORTAL; - } //end if - } //end for -} //end of the function AAS_SetViewPortalsAsClusterPortals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitClustering(void) -{ - int i, removedPortalAreas; - int n, total, numreachabilityareas; - - if (!aasworld.loaded) return; - //if there are clusters - if (aasworld.numclusters >= 1) - { -#ifndef BSPC - //if clustering isn't forced - if (!((int)LibVarGetValue("forceclustering")) && - !((int)LibVarGetValue("forcereachability"))) return; -#endif - } //end if - //set all view portals as cluster portals in case we re-calculate the reachabilities and clusters (with -reach) - AAS_SetViewPortalsAsClusterPortals(); - //count the number of forced cluster portals - AAS_CountForcedClusterPortals(); - //remove all area cluster marks - AAS_RemoveClusterAreas(); - //find possible cluster portals - AAS_FindPossiblePortals(); - //craete portals to for the bot view - AAS_CreateViewPortals(); - //remove all portals that are not closing a cluster - //AAS_RemoveNotClusterClosingPortals(); - //initialize portal memory - if (aasworld.portals) FreeMemory(aasworld.portals); - aasworld.portals = (aas_portal_t *) GetClearedMemory(AAS_MAX_PORTALS * sizeof(aas_portal_t)); - //initialize portal index memory - if (aasworld.portalindex) FreeMemory(aasworld.portalindex); - aasworld.portalindex = (aas_portalindex_t *) GetClearedMemory(AAS_MAX_PORTALINDEXSIZE * sizeof(aas_portalindex_t)); - //initialize cluster memory - if (aasworld.clusters) FreeMemory(aasworld.clusters); - aasworld.clusters = (aas_cluster_t *) GetClearedMemory(AAS_MAX_CLUSTERS * sizeof(aas_cluster_t)); - // - removedPortalAreas = 0; - botimport.Print(PRT_MESSAGE, "\r%6d removed portal areas", removedPortalAreas); - while(1) - { - botimport.Print(PRT_MESSAGE, "\r%6d", removedPortalAreas); - //initialize the number of portals and clusters - aasworld.numportals = 1; //portal 0 is a dummy - aasworld.portalindexsize = 0; - aasworld.numclusters = 1; //cluster 0 is a dummy - //create the portals from the portal areas - AAS_CreatePortals(); - // - removedPortalAreas++; - //find the clusters - if (!AAS_FindClusters()) - continue; - //test the portals - if (!AAS_TestPortals()) - continue; - // - break; - } //end while - botimport.Print(PRT_MESSAGE, "\n"); - //the AAS file should be saved - aasworld.savefile = qtrue; - //write the portal areas to the log file - for (i = 1; i < aasworld.numportals; i++) - { - Log_Write("portal %d: area %d\r\n", i, aasworld.portals[i].areanum); - } //end for - // report cluster info - botimport.Print(PRT_MESSAGE, "%6d portals created\n", aasworld.numportals); - botimport.Print(PRT_MESSAGE, "%6d clusters created\n", aasworld.numclusters); - for (i = 1; i < aasworld.numclusters; i++) - { - botimport.Print(PRT_MESSAGE, "cluster %d has %d reachability areas\n", i, - aasworld.clusters[i].numreachabilityareas); - } //end for - // report AAS file efficiency - numreachabilityareas = 0; - total = 0; - for (i = 0; i < aasworld.numclusters; i++) { - n = aasworld.clusters[i].numreachabilityareas; - numreachabilityareas += n; - total += n * n; - } - total += numreachabilityareas * aasworld.numportals; - // - botimport.Print(PRT_MESSAGE, "%6i total reachability areas\n", numreachabilityareas); - botimport.Print(PRT_MESSAGE, "%6i AAS memory/CPU usage (the lower the better)\n", total * 3); -} //end of the function AAS_InitClustering diff --git a/tools/quake3/bspc/deps/botlib/be_aas_cluster.h b/tools/quake3/bspc/deps/botlib/be_aas_cluster.h deleted file mode 100644 index e36697d1..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_cluster.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_cluster.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_cluster.h $ - * - *****************************************************************************/ - -#ifdef AASINTERN -//initialize the AAS clustering -void AAS_InitClustering(void); -// -void AAS_SetViewPortalsAsClusterPortals(void); -#endif //AASINTERN - diff --git a/tools/quake3/bspc/deps/botlib/be_aas_debug.c b/tools/quake3/bspc/deps/botlib/be_aas_debug.c deleted file mode 100644 index 1e04c7f5..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_debug.c +++ /dev/null @@ -1,777 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_debug.c - * - * desc: AAS debug code - * - * $Archive: /MissionPack/code/botlib/be_aas_debug.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "l_libvar.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_interface.h" -#include "be_aas_funcs.h" -#include "be_aas_def.h" - -#define MAX_DEBUGLINES 1024 -#define MAX_DEBUGPOLYGONS 8192 - -int debuglines[MAX_DEBUGLINES]; -int debuglinevisible[MAX_DEBUGLINES]; -int numdebuglines; - -static int debugpolygons[MAX_DEBUGPOLYGONS]; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ClearShownPolygons(void) -{ - int i; -//* - for (i = 0; i < MAX_DEBUGPOLYGONS; i++) - { - if (debugpolygons[i]) botimport.DebugPolygonDelete(debugpolygons[i]); - debugpolygons[i] = 0; - } //end for -//*/ -/* - for (i = 0; i < MAX_DEBUGPOLYGONS; i++) - { - botimport.DebugPolygonDelete(i); - debugpolygons[i] = 0; - } //end for -*/ -} //end of the function AAS_ClearShownPolygons -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShowPolygon(int color, int numpoints, vec3_t *points) -{ - int i; - - for (i = 0; i < MAX_DEBUGPOLYGONS; i++) - { - if (!debugpolygons[i]) - { - debugpolygons[i] = botimport.DebugPolygonCreate(color, numpoints, points); - break; - } //end if - } //end for -} //end of the function AAS_ShowPolygon -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ClearShownDebugLines(void) -{ - int i; - - //make all lines invisible - for (i = 0; i < MAX_DEBUGLINES; i++) - { - if (debuglines[i]) - { - //botimport.DebugLineShow(debuglines[i], NULL, NULL, LINECOLOR_NONE); - botimport.DebugLineDelete(debuglines[i]); - debuglines[i] = 0; - debuglinevisible[i] = qfalse; - } //end if - } //end for -} //end of the function AAS_ClearShownDebugLines -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DebugLine(vec3_t start, vec3_t end, int color) -{ - int line; - - for (line = 0; line < MAX_DEBUGLINES; line++) - { - if (!debuglines[line]) - { - debuglines[line] = botimport.DebugLineCreate(); - debuglinevisible[line] = qfalse; - numdebuglines++; - } //end if - if (!debuglinevisible[line]) - { - botimport.DebugLineShow(debuglines[line], start, end, color); - debuglinevisible[line] = qtrue; - return; - } //end else - } //end for -} //end of the function AAS_DebugLine -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_PermanentLine(vec3_t start, vec3_t end, int color) -{ - int line; - - line = botimport.DebugLineCreate(); - botimport.DebugLineShow(line, start, end, color); -} //end of the function AAS_PermenentLine -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DrawPermanentCross(vec3_t origin, float size, int color) -{ - int i, debugline; - vec3_t start, end; - - for (i = 0; i < 3; i++) - { - VectorCopy(origin, start); - start[i] += size; - VectorCopy(origin, end); - end[i] -= size; - AAS_DebugLine(start, end, color); - debugline = botimport.DebugLineCreate(); - botimport.DebugLineShow(debugline, start, end, color); - } //end for -} //end of the function AAS_DrawPermanentCross -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color) -{ - int n0, n1, n2, j, line, lines[2]; - vec3_t start1, end1, start2, end2; - - //make a cross in the hit plane at the hit point - VectorCopy(point, start1); - VectorCopy(point, end1); - VectorCopy(point, start2); - VectorCopy(point, end2); - - n0 = type % 3; - n1 = (type + 1) % 3; - n2 = (type + 2) % 3; - start1[n1] -= 6; - start1[n2] -= 6; - end1[n1] += 6; - end1[n2] += 6; - start2[n1] += 6; - start2[n2] -= 6; - end2[n1] -= 6; - end2[n2] += 6; - - start1[n0] = (dist - (start1[n1] * normal[n1] + - start1[n2] * normal[n2])) / normal[n0]; - end1[n0] = (dist - (end1[n1] * normal[n1] + - end1[n2] * normal[n2])) / normal[n0]; - start2[n0] = (dist - (start2[n1] * normal[n1] + - start2[n2] * normal[n2])) / normal[n0]; - end2[n0] = (dist - (end2[n1] * normal[n1] + - end2[n2] * normal[n2])) / normal[n0]; - - for (j = 0, line = 0; j < 2 && line < MAX_DEBUGLINES; line++) - { - if (!debuglines[line]) - { - debuglines[line] = botimport.DebugLineCreate(); - lines[j++] = debuglines[line]; - debuglinevisible[line] = qtrue; - numdebuglines++; - } //end if - else if (!debuglinevisible[line]) - { - lines[j++] = debuglines[line]; - debuglinevisible[line] = qtrue; - } //end else - } //end for - botimport.DebugLineShow(lines[0], start1, end1, color); - botimport.DebugLineShow(lines[1], start2, end2, color); -} //end of the function AAS_DrawPlaneCross -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs) -{ - vec3_t bboxcorners[8]; - int lines[3]; - int i, j, line; - - //upper corners - bboxcorners[0][0] = origin[0] + maxs[0]; - bboxcorners[0][1] = origin[1] + maxs[1]; - bboxcorners[0][2] = origin[2] + maxs[2]; - // - bboxcorners[1][0] = origin[0] + mins[0]; - bboxcorners[1][1] = origin[1] + maxs[1]; - bboxcorners[1][2] = origin[2] + maxs[2]; - // - bboxcorners[2][0] = origin[0] + mins[0]; - bboxcorners[2][1] = origin[1] + mins[1]; - bboxcorners[2][2] = origin[2] + maxs[2]; - // - bboxcorners[3][0] = origin[0] + maxs[0]; - bboxcorners[3][1] = origin[1] + mins[1]; - bboxcorners[3][2] = origin[2] + maxs[2]; - //lower corners - Com_Memcpy(bboxcorners[4], bboxcorners[0], sizeof(vec3_t) * 4); - for (i = 0; i < 4; i++) bboxcorners[4 + i][2] = origin[2] + mins[2]; - //draw bounding box - for (i = 0; i < 4; i++) - { - for (j = 0, line = 0; j < 3 && line < MAX_DEBUGLINES; line++) - { - if (!debuglines[line]) - { - debuglines[line] = botimport.DebugLineCreate(); - lines[j++] = debuglines[line]; - debuglinevisible[line] = qtrue; - numdebuglines++; - } //end if - else if (!debuglinevisible[line]) - { - lines[j++] = debuglines[line]; - debuglinevisible[line] = qtrue; - } //end else - } //end for - //top plane - botimport.DebugLineShow(lines[0], bboxcorners[i], - bboxcorners[(i+1)&3], LINECOLOR_RED); - //bottom plane - botimport.DebugLineShow(lines[1], bboxcorners[4+i], - bboxcorners[4+((i+1)&3)], LINECOLOR_RED); - //vertical lines - botimport.DebugLineShow(lines[2], bboxcorners[i], - bboxcorners[4+i], LINECOLOR_RED); - } //end for -} //end of the function AAS_ShowBoundingBox -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShowFace(int facenum) -{ - int i, color, edgenum; - aas_edge_t *edge; - aas_face_t *face; - aas_plane_t *plane; - vec3_t start, end; - - color = LINECOLOR_YELLOW; - //check if face number is in range - if (facenum >= aasworld.numfaces) - { - botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); - } //end if - face = &aasworld.faces[facenum]; - //walk through the edges of the face - for (i = 0; i < face->numedges; i++) - { - //edge number - edgenum = abs(aasworld.edgeindex[face->firstedge + i]); - //check if edge number is in range - if (edgenum >= aasworld.numedges) - { - botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum); - } //end if - edge = &aasworld.edges[edgenum]; - if (color == LINECOLOR_RED) color = LINECOLOR_GREEN; - else if (color == LINECOLOR_GREEN) color = LINECOLOR_BLUE; - else if (color == LINECOLOR_BLUE) color = LINECOLOR_YELLOW; - else color = LINECOLOR_RED; - AAS_DebugLine(aasworld.vertexes[edge->v[0]], - aasworld.vertexes[edge->v[1]], - color); - } //end for - plane = &aasworld.planes[face->planenum]; - edgenum = abs(aasworld.edgeindex[face->firstedge]); - edge = &aasworld.edges[edgenum]; - VectorCopy(aasworld.vertexes[edge->v[0]], start); - VectorMA(start, 20, plane->normal, end); - AAS_DebugLine(start, end, LINECOLOR_RED); -} //end of the function AAS_ShowFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShowFacePolygon(int facenum, int color, int flip) -{ - int i, edgenum, numpoints; - vec3_t points[128]; - aas_edge_t *edge; - aas_face_t *face; - - //check if face number is in range - if (facenum >= aasworld.numfaces) - { - botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); - } //end if - face = &aasworld.faces[facenum]; - //walk through the edges of the face - numpoints = 0; - if (flip) - { - for (i = face->numedges-1; i >= 0; i--) - { - //edge number - edgenum = aasworld.edgeindex[face->firstedge + i]; - edge = &aasworld.edges[abs(edgenum)]; - VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]); - numpoints++; - } //end for - } //end if - else - { - for (i = 0; i < face->numedges; i++) - { - //edge number - edgenum = aasworld.edgeindex[face->firstedge + i]; - edge = &aasworld.edges[abs(edgenum)]; - VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]); - numpoints++; - } //end for - } //end else - AAS_ShowPolygon(color, numpoints, points); -} //end of the function AAS_ShowFacePolygon -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShowArea(int areanum, int groundfacesonly) -{ - int areaedges[MAX_DEBUGLINES]; - int numareaedges, i, j, n, color = 0, line; - int facenum, edgenum; - aas_area_t *area; - aas_face_t *face; - aas_edge_t *edge; - - // - numareaedges = 0; - // - if (areanum < 0 || areanum >= aasworld.numareas) - { - botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n", - areanum, aasworld.numareas); - return; - } //end if - //pointer to the convex area - area = &aasworld.areas[areanum]; - //walk through the faces of the area - for (i = 0; i < area->numfaces; i++) - { - facenum = abs(aasworld.faceindex[area->firstface + i]); - //check if face number is in range - if (facenum >= aasworld.numfaces) - { - botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); - } //end if - face = &aasworld.faces[facenum]; - //ground faces only - if (groundfacesonly) - { - if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue; - } //end if - //walk through the edges of the face - for (j = 0; j < face->numedges; j++) - { - //edge number - edgenum = abs(aasworld.edgeindex[face->firstedge + j]); - //check if edge number is in range - if (edgenum >= aasworld.numedges) - { - botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum); - } //end if - //check if the edge is stored already - for (n = 0; n < numareaedges; n++) - { - if (areaedges[n] == edgenum) break; - } //end for - if (n == numareaedges && numareaedges < MAX_DEBUGLINES) - { - areaedges[numareaedges++] = edgenum; - } //end if - } //end for - //AAS_ShowFace(facenum); - } //end for - //draw all the edges - for (n = 0; n < numareaedges; n++) - { - for (line = 0; line < MAX_DEBUGLINES; line++) - { - if (!debuglines[line]) - { - debuglines[line] = botimport.DebugLineCreate(); - debuglinevisible[line] = qfalse; - numdebuglines++; - } //end if - if (!debuglinevisible[line]) - { - break; - } //end else - } //end for - if (line >= MAX_DEBUGLINES) return; - edge = &aasworld.edges[areaedges[n]]; - if (color == LINECOLOR_RED) color = LINECOLOR_BLUE; - else if (color == LINECOLOR_BLUE) color = LINECOLOR_GREEN; - else if (color == LINECOLOR_GREEN) color = LINECOLOR_YELLOW; - else color = LINECOLOR_RED; - botimport.DebugLineShow(debuglines[line], - aasworld.vertexes[edge->v[0]], - aasworld.vertexes[edge->v[1]], - color); - debuglinevisible[line] = qtrue; - } //end for*/ -} //end of the function AAS_ShowArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly) -{ - int i, facenum; - aas_area_t *area; - aas_face_t *face; - - // - if (areanum < 0 || areanum >= aasworld.numareas) - { - botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n", - areanum, aasworld.numareas); - return; - } //end if - //pointer to the convex area - area = &aasworld.areas[areanum]; - //walk through the faces of the area - for (i = 0; i < area->numfaces; i++) - { - facenum = abs(aasworld.faceindex[area->firstface + i]); - //check if face number is in range - if (facenum >= aasworld.numfaces) - { - botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); - } //end if - face = &aasworld.faces[facenum]; - //ground faces only - if (groundfacesonly) - { - if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue; - } //end if - AAS_ShowFacePolygon(facenum, color, face->frontarea != areanum); - } //end for -} //end of the function AAS_ShowAreaPolygons -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DrawCross(vec3_t origin, float size, int color) -{ - int i; - vec3_t start, end; - - for (i = 0; i < 3; i++) - { - VectorCopy(origin, start); - start[i] += size; - VectorCopy(origin, end); - end[i] -= size; - AAS_DebugLine(start, end, color); - } //end for -} //end of the function AAS_DrawCross -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_PrintTravelType(int traveltype) -{ -#ifdef DEBUG - char *str; - // - switch(traveltype & TRAVELTYPE_MASK) - { - case TRAVEL_INVALID: str = "TRAVEL_INVALID"; break; - case TRAVEL_WALK: str = "TRAVEL_WALK"; break; - case TRAVEL_CROUCH: str = "TRAVEL_CROUCH"; break; - case TRAVEL_BARRIERJUMP: str = "TRAVEL_BARRIERJUMP"; break; - case TRAVEL_JUMP: str = "TRAVEL_JUMP"; break; - case TRAVEL_LADDER: str = "TRAVEL_LADDER"; break; - case TRAVEL_WALKOFFLEDGE: str = "TRAVEL_WALKOFFLEDGE"; break; - case TRAVEL_SWIM: str = "TRAVEL_SWIM"; break; - case TRAVEL_WATERJUMP: str = "TRAVEL_WATERJUMP"; break; - case TRAVEL_TELEPORT: str = "TRAVEL_TELEPORT"; break; - case TRAVEL_ELEVATOR: str = "TRAVEL_ELEVATOR"; break; - case TRAVEL_ROCKETJUMP: str = "TRAVEL_ROCKETJUMP"; break; - case TRAVEL_BFGJUMP: str = "TRAVEL_BFGJUMP"; break; - case TRAVEL_GRAPPLEHOOK: str = "TRAVEL_GRAPPLEHOOK"; break; - case TRAVEL_JUMPPAD: str = "TRAVEL_JUMPPAD"; break; - case TRAVEL_FUNCBOB: str = "TRAVEL_FUNCBOB"; break; - default: str = "UNKNOWN TRAVEL TYPE"; break; - } //end switch - botimport.Print(PRT_MESSAGE, "%s", str); -#endif -} //end of the function AAS_PrintTravelType -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor) -{ - vec3_t dir, cross, p1, p2, up = {0, 0, 1}; - float dot; - - VectorSubtract(end, start, dir); - VectorNormalize(dir); - dot = DotProduct(dir, up); - if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0); - else CrossProduct(dir, up, cross); - - VectorMA(end, -6, dir, p1); - VectorCopy(p1, p2); - VectorMA(p1, 6, cross, p1); - VectorMA(p2, -6, cross, p2); - - AAS_DebugLine(start, end, linecolor); - AAS_DebugLine(p1, end, arrowcolor); - AAS_DebugLine(p2, end, arrowcolor); -} //end of the function AAS_DrawArrow -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShowReachability(aas_reachability_t *reach) -{ - vec3_t dir, cmdmove, velocity; - float speed, zvel; - aas_clientmove_t move; - - AAS_ShowAreaPolygons(reach->areanum, 5, qtrue); - //AAS_ShowArea(reach->areanum, qtrue); - AAS_DrawArrow(reach->start, reach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW); - // - if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP || - (reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE) - { - AAS_HorizontalVelocityForJump(aassettings.phys_jumpvel, reach->start, reach->end, &speed); - // - VectorSubtract(reach->end, reach->start, dir); - dir[2] = 0; - VectorNormalize(dir); - //set the velocity - VectorScale(dir, speed, velocity); - //set the command movement - VectorClear(cmdmove); - cmdmove[2] = aassettings.phys_jumpvel; - // - AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, - velocity, cmdmove, 3, 30, 0.1f, - SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| - SE_ENTERLAVA|SE_HITGROUNDDAMAGE, 0, qtrue); - // - if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) - { - AAS_JumpReachRunStart(reach, dir); - AAS_DrawCross(dir, 4, LINECOLOR_BLUE); - } //end if - } //end if - else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) - { - zvel = AAS_RocketJumpZVelocity(reach->start); - AAS_HorizontalVelocityForJump(zvel, reach->start, reach->end, &speed); - // - VectorSubtract(reach->end, reach->start, dir); - dir[2] = 0; - VectorNormalize(dir); - //get command movement - VectorScale(dir, speed, cmdmove); - VectorSet(velocity, 0, 0, zvel); - // - AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, - velocity, cmdmove, 30, 30, 0.1f, - SE_ENTERWATER|SE_ENTERSLIME| - SE_ENTERLAVA|SE_HITGROUNDDAMAGE| - SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue); - } //end else if - else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) - { - VectorSet(cmdmove, 0, 0, 0); - // - VectorSubtract(reach->end, reach->start, dir); - dir[2] = 0; - VectorNormalize(dir); - //set the velocity - //NOTE: the edgenum is the horizontal velocity - VectorScale(dir, reach->edgenum, velocity); - //NOTE: the facenum is the Z velocity - velocity[2] = reach->facenum; - // - AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, - velocity, cmdmove, 30, 30, 0.1f, - SE_ENTERWATER|SE_ENTERSLIME| - SE_ENTERLAVA|SE_HITGROUNDDAMAGE| - SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue); - } //end else if -} //end of the function AAS_ShowReachability -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShowReachableAreas(int areanum) -{ - aas_areasettings_t *settings; - static aas_reachability_t reach; - static int index, lastareanum; - static float lasttime; - - if (areanum != lastareanum) - { - index = 0; - lastareanum = areanum; - } //end if - settings = &aasworld.areasettings[areanum]; - // - if (!settings->numreachableareas) return; - // - if (index >= settings->numreachableareas) index = 0; - // - if (AAS_Time() - lasttime > 1.5) - { - Com_Memcpy(&reach, &aasworld.reachability[settings->firstreachablearea + index], sizeof(aas_reachability_t)); - index++; - lasttime = AAS_Time(); - AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); - botimport.Print(PRT_MESSAGE, "\n"); - } //end if - AAS_ShowReachability(&reach); -} //end of the function ShowReachableAreas - -void AAS_FloodAreas_r(int areanum, int cluster, int *done) -{ - int nextareanum, i, facenum; - aas_area_t *area; - aas_face_t *face; - aas_areasettings_t *settings; - aas_reachability_t *reach; - - AAS_ShowAreaPolygons(areanum, 1, qtrue); - //pointer to the convex area - area = &aasworld.areas[areanum]; - settings = &aasworld.areasettings[areanum]; - //walk through the faces of the area - for (i = 0; i < area->numfaces; i++) - { - facenum = abs(aasworld.faceindex[area->firstface + i]); - face = &aasworld.faces[facenum]; - if (face->frontarea == areanum) - nextareanum = face->backarea; - else - nextareanum = face->frontarea; - if (!nextareanum) - continue; - if (done[nextareanum]) - continue; - done[nextareanum] = qtrue; - if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL) - continue; - if (AAS_AreaCluster(nextareanum) != cluster) - continue; - AAS_FloodAreas_r(nextareanum, cluster, done); - } //end for - // - for (i = 0; i < settings->numreachableareas; i++) - { - reach = &aasworld.reachability[settings->firstreachablearea + i]; - nextareanum = reach->areanum; - if (!nextareanum) - continue; - if (done[nextareanum]) - continue; - done[nextareanum] = qtrue; - if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL) - continue; - if (AAS_AreaCluster(nextareanum) != cluster) - continue; - /* - if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE) - { - AAS_DebugLine(reach->start, reach->end, 1); - } - */ - AAS_FloodAreas_r(nextareanum, cluster, done); - } -} - -void AAS_FloodAreas(vec3_t origin) -{ - int areanum, cluster, *done; - - done = (int *) GetClearedMemory(aasworld.numareas * sizeof(int)); - areanum = AAS_PointAreaNum(origin); - cluster = AAS_AreaCluster(areanum); - AAS_FloodAreas_r(areanum, cluster, done); -} diff --git a/tools/quake3/bspc/deps/botlib/be_aas_debug.h b/tools/quake3/bspc/deps/botlib/be_aas_debug.h deleted file mode 100644 index 008eeba6..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_debug.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_debug.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_debug.h $ - * - *****************************************************************************/ - -//clear the shown debug lines -void AAS_ClearShownDebugLines(void); -// -void AAS_ClearShownPolygons(void); -//show a debug line -void AAS_DebugLine(vec3_t start, vec3_t end, int color); -//show a permenent line -void AAS_PermanentLine(vec3_t start, vec3_t end, int color); -//show a permanent cross -void AAS_DrawPermanentCross(vec3_t origin, float size, int color); -//draw a cross in the plane -void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color); -//show a bounding box -void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs); -//show a face -void AAS_ShowFace(int facenum); -//show an area -void AAS_ShowArea(int areanum, int groundfacesonly); -// -void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly); -//draw a cros -void AAS_DrawCross(vec3_t origin, float size, int color); -//print the travel type -void AAS_PrintTravelType(int traveltype); -//draw an arrow -void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor); -//visualize the given reachability -void AAS_ShowReachability(struct aas_reachability_s *reach); -//show the reachable areas from the given area -void AAS_ShowReachableAreas(int areanum); - diff --git a/tools/quake3/bspc/deps/botlib/be_aas_def.h b/tools/quake3/bspc/deps/botlib/be_aas_def.h deleted file mode 100644 index eb995d6c..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_def.h +++ /dev/null @@ -1,306 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_def.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_def.h $ - * - *****************************************************************************/ - -//debugging on -#define AAS_DEBUG - -#define MAX_CLIENTS 64 -#define MAX_MODELS 256 // these are sent over the net as 8 bits -#define MAX_SOUNDS 256 // so they cannot be blindly increased -#define MAX_CONFIGSTRINGS 1024 - -#define CS_SCORES 32 -#define CS_MODELS (CS_SCORES+MAX_CLIENTS) -#define CS_SOUNDS (CS_MODELS+MAX_MODELS) - -#define DF_AASENTNUMBER(x) (x - aasworld.entities) -#define DF_NUMBERAASENT(x) (&aasworld.entities[x]) -#define DF_AASENTCLIENT(x) (x - aasworld.entities - 1) -#define DF_CLIENTAASENT(x) (&aasworld.entities[x + 1]) - -#ifndef MAX_PATH - #define MAX_PATH MAX_QPATH -#endif - -//string index (for model, sound and image index) -typedef struct aas_stringindex_s -{ - int numindexes; - char **index; -} aas_stringindex_t; - -//structure to link entities to areas and areas to entities -typedef struct aas_link_s -{ - int entnum; - int areanum; - struct aas_link_s *next_ent, *prev_ent; - struct aas_link_s *next_area, *prev_area; -} aas_link_t; - -//structure to link entities to leaves and leaves to entities -typedef struct bsp_link_s -{ - int entnum; - int leafnum; - struct bsp_link_s *next_ent, *prev_ent; - struct bsp_link_s *next_leaf, *prev_leaf; -} bsp_link_t; - -typedef struct bsp_entdata_s -{ - vec3_t origin; - vec3_t angles; - vec3_t absmins; - vec3_t absmaxs; - int solid; - int modelnum; -} bsp_entdata_t; - -//entity -typedef struct aas_entity_s -{ - //entity info - aas_entityinfo_t i; - //links into the AAS areas - aas_link_t *areas; - //links into the BSP leaves - bsp_link_t *leaves; -} aas_entity_t; - -typedef struct aas_settings_s -{ - vec3_t phys_gravitydirection; - float phys_friction; - float phys_stopspeed; - float phys_gravity; - float phys_waterfriction; - float phys_watergravity; - float phys_maxvelocity; - float phys_maxwalkvelocity; - float phys_maxcrouchvelocity; - float phys_maxswimvelocity; - float phys_walkaccelerate; - float phys_airaccelerate; - float phys_swimaccelerate; - float phys_maxstep; - float phys_maxsteepness; - float phys_maxwaterjump; - float phys_maxbarrier; - float phys_jumpvel; - float phys_falldelta5; - float phys_falldelta10; - float rs_waterjump; - float rs_teleport; - float rs_barrierjump; - float rs_startcrouch; - float rs_startgrapple; - float rs_startwalkoffledge; - float rs_startjump; - float rs_rocketjump; - float rs_bfgjump; - float rs_jumppad; - float rs_aircontrolledjumppad; - float rs_funcbob; - float rs_startelevator; - float rs_falldamage5; - float rs_falldamage10; - float rs_maxfallheight; - float rs_maxjumpfallheight; -} aas_settings_t; - -#define CACHETYPE_PORTAL 0 -#define CACHETYPE_AREA 1 - -//routing cache -typedef struct aas_routingcache_s -{ - byte type; //portal or area cache - float time; //last time accessed or updated - int size; //size of the routing cache - int cluster; //cluster the cache is for - int areanum; //area the cache is created for - vec3_t origin; //origin within the area - float starttraveltime; //travel time to start with - int travelflags; //combinations of the travel flags - struct aas_routingcache_s *prev, *next; - struct aas_routingcache_s *time_prev, *time_next; - unsigned char *reachabilities; //reachabilities used for routing - unsigned short int traveltimes[1]; //travel time for every area (variable sized) -} aas_routingcache_t; - -//fields for the routing algorithm -typedef struct aas_routingupdate_s -{ - int cluster; - int areanum; //area number of the update - vec3_t start; //start point the area was entered - unsigned short int tmptraveltime; //temporary travel time - unsigned short int *areatraveltimes; //travel times within the area - qboolean inlist; //true if the update is in the list - struct aas_routingupdate_s *next; - struct aas_routingupdate_s *prev; -} aas_routingupdate_t; - -//reversed reachability link -typedef struct aas_reversedlink_s -{ - int linknum; //the aas_areareachability_t - int areanum; //reachable from this area - struct aas_reversedlink_s *next; //next link -} aas_reversedlink_t; - -//reversed area reachability -typedef struct aas_reversedreachability_s -{ - int numlinks; - aas_reversedlink_t *first; -} aas_reversedreachability_t; - -//areas a reachability goes through -typedef struct aas_reachabilityareas_s -{ - int firstarea, numareas; -} aas_reachabilityareas_t; - -typedef struct aas_s -{ - int loaded; //true when an AAS file is loaded - int initialized; //true when AAS has been initialized - int savefile; //set true when file should be saved - int bspchecksum; - //current time - float time; - int numframes; - //name of the aas file - char filename[MAX_PATH]; - char mapname[MAX_PATH]; - //bounding boxes - int numbboxes; - aas_bbox_t *bboxes; - //vertexes - int numvertexes; - aas_vertex_t *vertexes; - //planes - int numplanes; - aas_plane_t *planes; - //edges - int numedges; - aas_edge_t *edges; - //edge index - int edgeindexsize; - aas_edgeindex_t *edgeindex; - //faces - int numfaces; - aas_face_t *faces; - //face index - int faceindexsize; - aas_faceindex_t *faceindex; - //convex areas - int numareas; - aas_area_t *areas; - //convex area settings - int numareasettings; - aas_areasettings_t *areasettings; - //reachablity list - int reachabilitysize; - aas_reachability_t *reachability; - //nodes of the bsp tree - int numnodes; - aas_node_t *nodes; - //cluster portals - int numportals; - aas_portal_t *portals; - //cluster portal index - int portalindexsize; - aas_portalindex_t *portalindex; - //clusters - int numclusters; - aas_cluster_t *clusters; - // - int numreachabilityareas; - float reachabilitytime; - //enities linked in the areas - aas_link_t *linkheap; //heap with link structures - int linkheapsize; //size of the link heap - aas_link_t *freelinks; //first free link - aas_link_t **arealinkedentities; //entities linked into areas - //entities - int maxentities; - int maxclients; - aas_entity_t *entities; - //string indexes - char *configstrings[MAX_CONFIGSTRINGS]; - int indexessetup; - //index to retrieve travel flag for a travel type - int travelflagfortype[MAX_TRAVELTYPES]; - //travel flags for each area based on contents - int *areacontentstravelflags; - //routing update - aas_routingupdate_t *areaupdate; - aas_routingupdate_t *portalupdate; - //number of routing updates during a frame (reset every frame) - int frameroutingupdates; - //reversed reachability links - aas_reversedreachability_t *reversedreachability; - //travel times within the areas - unsigned short ***areatraveltimes; - //array of size numclusters with cluster cache - aas_routingcache_t ***clusterareacache; - aas_routingcache_t **portalcache; - //cache list sorted on time - aas_routingcache_t *oldestcache; // start of cache list sorted on time - aas_routingcache_t *newestcache; // end of cache list sorted on time - //maximum travel time through portal areas - int *portalmaxtraveltimes; - //areas the reachabilities go through - int *reachabilityareaindex; - aas_reachabilityareas_t *reachabilityareas; -} aas_t; - -#define AASINTERN - -#ifndef BSPCINCLUDE - -#include "be_aas_main.h" -#include "be_aas_entity.h" -#include "be_aas_sample.h" -#include "be_aas_cluster.h" -#include "be_aas_reach.h" -#include "be_aas_route.h" -#include "be_aas_routealt.h" -#include "be_aas_debug.h" -#include "be_aas_file.h" -#include "be_aas_optimize.h" -#include "be_aas_bsp.h" -#include "be_aas_move.h" - -#endif //BSPCINCLUDE diff --git a/tools/quake3/bspc/deps/botlib/be_aas_entity.c b/tools/quake3/bspc/deps/botlib/be_aas_entity.c deleted file mode 100644 index 99ed9b2c..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_entity.c +++ /dev/null @@ -1,437 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_entity.c - * - * desc: AAS entities - * - * $Archive: /MissionPack/code/botlib/be_aas_entity.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "l_utils.h" -#include "l_log.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_aas_def.h" - -#define MASK_SOLID CONTENTS_PLAYERCLIP - -//FIXME: these might change -enum { - ET_GENERAL, - ET_PLAYER, - ET_ITEM, - ET_MISSILE, - ET_MOVER -}; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_UpdateEntity(int entnum, bot_entitystate_t *state) -{ - int relink; - aas_entity_t *ent; - vec3_t absmins, absmaxs; - - if (!aasworld.loaded) - { - botimport.Print(PRT_MESSAGE, "AAS_UpdateEntity: not loaded\n"); - return BLERR_NOAASFILE; - } //end if - - ent = &aasworld.entities[entnum]; - - if (!state) { - //unlink the entity - AAS_UnlinkFromAreas(ent->areas); - //unlink the entity from the BSP leaves - AAS_UnlinkFromBSPLeaves(ent->leaves); - // - ent->areas = NULL; - // - ent->leaves = NULL; - return BLERR_NOERROR; - } - - ent->i.update_time = AAS_Time() - ent->i.ltime; - ent->i.type = state->type; - ent->i.flags = state->flags; - ent->i.ltime = AAS_Time(); - VectorCopy(ent->i.origin, ent->i.lastvisorigin); - VectorCopy(state->old_origin, ent->i.old_origin); - ent->i.solid = state->solid; - ent->i.groundent = state->groundent; - ent->i.modelindex = state->modelindex; - ent->i.modelindex2 = state->modelindex2; - ent->i.frame = state->frame; - ent->i.event = state->event; - ent->i.eventParm = state->eventParm; - ent->i.powerups = state->powerups; - ent->i.weapon = state->weapon; - ent->i.legsAnim = state->legsAnim; - ent->i.torsoAnim = state->torsoAnim; - //number of the entity - ent->i.number = entnum; - //updated so set valid flag - ent->i.valid = qtrue; - //link everything the first frame - if (aasworld.numframes == 1) relink = qtrue; - else relink = qfalse; - // - if (ent->i.solid == SOLID_BSP) - { - //if the angles of the model changed - if (!VectorCompare(state->angles, ent->i.angles)) - { - VectorCopy(state->angles, ent->i.angles); - relink = qtrue; - } //end if - //get the mins and maxs of the model - //FIXME: rotate mins and maxs - AAS_BSPModelMinsMaxsOrigin(ent->i.modelindex, ent->i.angles, ent->i.mins, ent->i.maxs, NULL); - } //end if - else if (ent->i.solid == SOLID_BBOX) - { - //if the bounding box size changed - if (!VectorCompare(state->mins, ent->i.mins) || - !VectorCompare(state->maxs, ent->i.maxs)) - { - VectorCopy(state->mins, ent->i.mins); - VectorCopy(state->maxs, ent->i.maxs); - relink = qtrue; - } //end if - VectorCopy(state->angles, ent->i.angles); - } //end if - //if the origin changed - if (!VectorCompare(state->origin, ent->i.origin)) - { - VectorCopy(state->origin, ent->i.origin); - relink = qtrue; - } //end if - //if the entity should be relinked - if (relink) - { - //don't link the world model - if (entnum != ENTITYNUM_WORLD) - { - //absolute mins and maxs - VectorAdd(ent->i.mins, ent->i.origin, absmins); - VectorAdd(ent->i.maxs, ent->i.origin, absmaxs); - //unlink the entity - AAS_UnlinkFromAreas(ent->areas); - //relink the entity to the AAS areas (use the larges bbox) - ent->areas = AAS_LinkEntityClientBBox(absmins, absmaxs, entnum, PRESENCE_NORMAL); - //unlink the entity from the BSP leaves - AAS_UnlinkFromBSPLeaves(ent->leaves); - //link the entity to the world BSP tree - ent->leaves = AAS_BSPLinkEntity(absmins, absmaxs, entnum, 0); - } //end if - } //end if - return BLERR_NOERROR; -} //end of the function AAS_UpdateEntity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_EntityInfo(int entnum, aas_entityinfo_t *info) -{ - if (!aasworld.initialized) - { - botimport.Print(PRT_FATAL, "AAS_EntityInfo: aasworld not initialized\n"); - Com_Memset(info, 0, sizeof(aas_entityinfo_t)); - return; - } //end if - - if (entnum < 0 || entnum >= aasworld.maxentities) - { - botimport.Print(PRT_FATAL, "AAS_EntityInfo: entnum %d out of range\n", entnum); - Com_Memset(info, 0, sizeof(aas_entityinfo_t)); - return; - } //end if - - Com_Memcpy(info, &aasworld.entities[entnum].i, sizeof(aas_entityinfo_t)); -} //end of the function AAS_EntityInfo -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_EntityOrigin(int entnum, vec3_t origin) -{ - if (entnum < 0 || entnum >= aasworld.maxentities) - { - botimport.Print(PRT_FATAL, "AAS_EntityOrigin: entnum %d out of range\n", entnum); - VectorClear(origin); - return; - } //end if - - VectorCopy(aasworld.entities[entnum].i.origin, origin); -} //end of the function AAS_EntityOrigin -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_EntityModelindex(int entnum) -{ - if (entnum < 0 || entnum >= aasworld.maxentities) - { - botimport.Print(PRT_FATAL, "AAS_EntityModelindex: entnum %d out of range\n", entnum); - return 0; - } //end if - return aasworld.entities[entnum].i.modelindex; -} //end of the function AAS_EntityModelindex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_EntityType(int entnum) -{ - if (!aasworld.initialized) return 0; - - if (entnum < 0 || entnum >= aasworld.maxentities) - { - botimport.Print(PRT_FATAL, "AAS_EntityType: entnum %d out of range\n", entnum); - return 0; - } //end if - return aasworld.entities[entnum].i.type; -} //end of the AAS_EntityType -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_EntityModelNum(int entnum) -{ - if (!aasworld.initialized) return 0; - - if (entnum < 0 || entnum >= aasworld.maxentities) - { - botimport.Print(PRT_FATAL, "AAS_EntityModelNum: entnum %d out of range\n", entnum); - return 0; - } //end if - return aasworld.entities[entnum].i.modelindex; -} //end of the function AAS_EntityModelNum -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin) -{ - int i; - aas_entity_t *ent; - - for (i = 0; i < aasworld.maxentities; i++) - { - ent = &aasworld.entities[i]; - if (ent->i.type == ET_MOVER) - { - if (ent->i.modelindex == modelnum) - { - VectorCopy(ent->i.origin, origin); - return qtrue; - } //end if - } //end if - } //end for - return qfalse; -} //end of the function AAS_OriginOfMoverWithModelNum -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs) -{ - aas_entity_t *ent; - - if (!aasworld.initialized) return; - - if (entnum < 0 || entnum >= aasworld.maxentities) - { - botimport.Print(PRT_FATAL, "AAS_EntitySize: entnum %d out of range\n", entnum); - return; - } //end if - - ent = &aasworld.entities[entnum]; - VectorCopy(ent->i.mins, mins); - VectorCopy(ent->i.maxs, maxs); -} //end of the function AAS_EntitySize -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata) -{ - aas_entity_t *ent; - - ent = &aasworld.entities[entnum]; - VectorCopy(ent->i.origin, entdata->origin); - VectorCopy(ent->i.angles, entdata->angles); - VectorAdd(ent->i.origin, ent->i.mins, entdata->absmins); - VectorAdd(ent->i.origin, ent->i.maxs, entdata->absmaxs); - entdata->solid = ent->i.solid; - entdata->modelnum = ent->i.modelindex - 1; -} //end of the function AAS_EntityBSPData -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ResetEntityLinks(void) -{ - int i; - for (i = 0; i < aasworld.maxentities; i++) - { - aasworld.entities[i].areas = NULL; - aasworld.entities[i].leaves = NULL; - } //end for -} //end of the function AAS_ResetEntityLinks -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InvalidateEntities(void) -{ - int i; - for (i = 0; i < aasworld.maxentities; i++) - { - aasworld.entities[i].i.valid = qfalse; - aasworld.entities[i].i.number = i; - } //end for -} //end of the function AAS_InvalidateEntities -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_UnlinkInvalidEntities(void) -{ - int i; - aas_entity_t *ent; - - for (i = 0; i < aasworld.maxentities; i++) - { - ent = &aasworld.entities[i]; - if (!ent->i.valid) - { - AAS_UnlinkFromAreas( ent->areas ); - ent->areas = NULL; - AAS_UnlinkFromBSPLeaves( ent->leaves ); - ent->leaves = NULL; - } //end for - } //end for -} //end of the function AAS_UnlinkInvalidEntities -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_NearestEntity(vec3_t origin, int modelindex) -{ - int i, bestentnum; - float dist, bestdist; - aas_entity_t *ent; - vec3_t dir; - - bestentnum = 0; - bestdist = 99999; - for (i = 0; i < aasworld.maxentities; i++) - { - ent = &aasworld.entities[i]; - if (ent->i.modelindex != modelindex) continue; - VectorSubtract(ent->i.origin, origin, dir); - if (abs(dir[0]) < 40) - { - if (abs(dir[1]) < 40) - { - dist = VectorLength(dir); - if (dist < bestdist) - { - bestdist = dist; - bestentnum = i; - } //end if - } //end if - } //end if - } //end for - return bestentnum; -} //end of the function AAS_NearestEntity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_BestReachableEntityArea(int entnum) -{ - aas_entity_t *ent; - - ent = &aasworld.entities[entnum]; - return AAS_BestReachableLinkArea(ent->areas); -} //end of the function AAS_BestReachableEntityArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_NextEntity(int entnum) -{ - if (!aasworld.loaded) return 0; - - if (entnum < 0) entnum = -1; - while(++entnum < aasworld.maxentities) - { - if (aasworld.entities[entnum].i.valid) return entnum; - } //end while - return 0; -} //end of the function AAS_NextEntity diff --git a/tools/quake3/bspc/deps/botlib/be_aas_entity.h b/tools/quake3/bspc/deps/botlib/be_aas_entity.h deleted file mode 100644 index 01ec54c3..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_entity.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_entity.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_entity.h $ - * - *****************************************************************************/ - -#ifdef AASINTERN -//invalidates all entity infos -void AAS_InvalidateEntities(void); -//unlink not updated entities -void AAS_UnlinkInvalidEntities(void); -//resets the entity AAS and BSP links (sets areas and leaves pointers to NULL) -void AAS_ResetEntityLinks(void); -//updates an entity -int AAS_UpdateEntity(int ent, bot_entitystate_t *state); -//gives the entity data used for collision detection -void AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata); -#endif //AASINTERN - -//returns the size of the entity bounding box in mins and maxs -void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs); -//returns the BSP model number of the entity -int AAS_EntityModelNum(int entnum); -//returns the origin of an entity with the given model number -int AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin); -//returns the best reachable area the entity is situated in -int AAS_BestReachableEntityArea(int entnum); -//returns the info of the given entity -void AAS_EntityInfo(int entnum, aas_entityinfo_t *info); -//returns the next entity -int AAS_NextEntity(int entnum); -//returns the origin of the entity -void AAS_EntityOrigin(int entnum, vec3_t origin); -//returns the entity type -int AAS_EntityType(int entnum); -//returns the model index of the entity -int AAS_EntityModelindex(int entnum); - diff --git a/tools/quake3/bspc/deps/botlib/be_aas_file.c b/tools/quake3/bspc/deps/botlib/be_aas_file.c deleted file mode 100644 index 62359ba7..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_file.c +++ /dev/null @@ -1,582 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_file.c - * - * desc: AAS file loading/writing - * - * $Archive: /MissionPack/code/botlib/be_aas_file.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "l_libvar.h" -#include "l_utils.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_aas_def.h" - -//#define AASFILEDEBUG - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_SwapAASData(void) -{ - int i, j; - //bounding boxes - for (i = 0; i < aasworld.numbboxes; i++) - { - aasworld.bboxes[i].presencetype = LittleLong(aasworld.bboxes[i].presencetype); - aasworld.bboxes[i].flags = LittleLong(aasworld.bboxes[i].flags); - for (j = 0; j < 3; j++) - { - aasworld.bboxes[i].mins[j] = LittleLong(aasworld.bboxes[i].mins[j]); - aasworld.bboxes[i].maxs[j] = LittleLong(aasworld.bboxes[i].maxs[j]); - } //end for - } //end for - //vertexes - for (i = 0; i < aasworld.numvertexes; i++) - { - for (j = 0; j < 3; j++) - aasworld.vertexes[i][j] = LittleFloat(aasworld.vertexes[i][j]); - } //end for - //planes - for (i = 0; i < aasworld.numplanes; i++) - { - for (j = 0; j < 3; j++) - aasworld.planes[i].normal[j] = LittleFloat(aasworld.planes[i].normal[j]); - aasworld.planes[i].dist = LittleFloat(aasworld.planes[i].dist); - aasworld.planes[i].type = LittleLong(aasworld.planes[i].type); - } //end for - //edges - for (i = 0; i < aasworld.numedges; i++) - { - aasworld.edges[i].v[0] = LittleLong(aasworld.edges[i].v[0]); - aasworld.edges[i].v[1] = LittleLong(aasworld.edges[i].v[1]); - } //end for - //edgeindex - for (i = 0; i < aasworld.edgeindexsize; i++) - { - aasworld.edgeindex[i] = LittleLong(aasworld.edgeindex[i]); - } //end for - //faces - for (i = 0; i < aasworld.numfaces; i++) - { - aasworld.faces[i].planenum = LittleLong(aasworld.faces[i].planenum); - aasworld.faces[i].faceflags = LittleLong(aasworld.faces[i].faceflags); - aasworld.faces[i].numedges = LittleLong(aasworld.faces[i].numedges); - aasworld.faces[i].firstedge = LittleLong(aasworld.faces[i].firstedge); - aasworld.faces[i].frontarea = LittleLong(aasworld.faces[i].frontarea); - aasworld.faces[i].backarea = LittleLong(aasworld.faces[i].backarea); - } //end for - //face index - for (i = 0; i < aasworld.faceindexsize; i++) - { - aasworld.faceindex[i] = LittleLong(aasworld.faceindex[i]); - } //end for - //convex areas - for (i = 0; i < aasworld.numareas; i++) - { - aasworld.areas[i].areanum = LittleLong(aasworld.areas[i].areanum); - aasworld.areas[i].numfaces = LittleLong(aasworld.areas[i].numfaces); - aasworld.areas[i].firstface = LittleLong(aasworld.areas[i].firstface); - for (j = 0; j < 3; j++) - { - aasworld.areas[i].mins[j] = LittleFloat(aasworld.areas[i].mins[j]); - aasworld.areas[i].maxs[j] = LittleFloat(aasworld.areas[i].maxs[j]); - aasworld.areas[i].center[j] = LittleFloat(aasworld.areas[i].center[j]); - } //end for - } //end for - //area settings - for (i = 0; i < aasworld.numareasettings; i++) - { - aasworld.areasettings[i].contents = LittleLong(aasworld.areasettings[i].contents); - aasworld.areasettings[i].areaflags = LittleLong(aasworld.areasettings[i].areaflags); - aasworld.areasettings[i].presencetype = LittleLong(aasworld.areasettings[i].presencetype); - aasworld.areasettings[i].cluster = LittleLong(aasworld.areasettings[i].cluster); - aasworld.areasettings[i].clusterareanum = LittleLong(aasworld.areasettings[i].clusterareanum); - aasworld.areasettings[i].numreachableareas = LittleLong(aasworld.areasettings[i].numreachableareas); - aasworld.areasettings[i].firstreachablearea = LittleLong(aasworld.areasettings[i].firstreachablearea); - } //end for - //area reachability - for (i = 0; i < aasworld.reachabilitysize; i++) - { - aasworld.reachability[i].areanum = LittleLong(aasworld.reachability[i].areanum); - aasworld.reachability[i].facenum = LittleLong(aasworld.reachability[i].facenum); - aasworld.reachability[i].edgenum = LittleLong(aasworld.reachability[i].edgenum); - for (j = 0; j < 3; j++) - { - aasworld.reachability[i].start[j] = LittleFloat(aasworld.reachability[i].start[j]); - aasworld.reachability[i].end[j] = LittleFloat(aasworld.reachability[i].end[j]); - } //end for - aasworld.reachability[i].traveltype = LittleLong(aasworld.reachability[i].traveltype); - aasworld.reachability[i].traveltime = LittleShort(aasworld.reachability[i].traveltime); - } //end for - //nodes - for (i = 0; i < aasworld.numnodes; i++) - { - aasworld.nodes[i].planenum = LittleLong(aasworld.nodes[i].planenum); - aasworld.nodes[i].children[0] = LittleLong(aasworld.nodes[i].children[0]); - aasworld.nodes[i].children[1] = LittleLong(aasworld.nodes[i].children[1]); - } //end for - //cluster portals - for (i = 0; i < aasworld.numportals; i++) - { - aasworld.portals[i].areanum = LittleLong(aasworld.portals[i].areanum); - aasworld.portals[i].frontcluster = LittleLong(aasworld.portals[i].frontcluster); - aasworld.portals[i].backcluster = LittleLong(aasworld.portals[i].backcluster); - aasworld.portals[i].clusterareanum[0] = LittleLong(aasworld.portals[i].clusterareanum[0]); - aasworld.portals[i].clusterareanum[1] = LittleLong(aasworld.portals[i].clusterareanum[1]); - } //end for - //cluster portal index - for (i = 0; i < aasworld.portalindexsize; i++) - { - aasworld.portalindex[i] = LittleLong(aasworld.portalindex[i]); - } //end for - //cluster - for (i = 0; i < aasworld.numclusters; i++) - { - aasworld.clusters[i].numareas = LittleLong(aasworld.clusters[i].numareas); - aasworld.clusters[i].numreachabilityareas = LittleLong(aasworld.clusters[i].numreachabilityareas); - aasworld.clusters[i].numportals = LittleLong(aasworld.clusters[i].numportals); - aasworld.clusters[i].firstportal = LittleLong(aasworld.clusters[i].firstportal); - } //end for -} //end of the function AAS_SwapAASData -//=========================================================================== -// dump the current loaded aas file -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DumpAASData(void) -{ - aasworld.numbboxes = 0; - if (aasworld.bboxes) FreeMemory(aasworld.bboxes); - aasworld.bboxes = NULL; - aasworld.numvertexes = 0; - if (aasworld.vertexes) FreeMemory(aasworld.vertexes); - aasworld.vertexes = NULL; - aasworld.numplanes = 0; - if (aasworld.planes) FreeMemory(aasworld.planes); - aasworld.planes = NULL; - aasworld.numedges = 0; - if (aasworld.edges) FreeMemory(aasworld.edges); - aasworld.edges = NULL; - aasworld.edgeindexsize = 0; - if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex); - aasworld.edgeindex = NULL; - aasworld.numfaces = 0; - if (aasworld.faces) FreeMemory(aasworld.faces); - aasworld.faces = NULL; - aasworld.faceindexsize = 0; - if (aasworld.faceindex) FreeMemory(aasworld.faceindex); - aasworld.faceindex = NULL; - aasworld.numareas = 0; - if (aasworld.areas) FreeMemory(aasworld.areas); - aasworld.areas = NULL; - aasworld.numareasettings = 0; - if (aasworld.areasettings) FreeMemory(aasworld.areasettings); - aasworld.areasettings = NULL; - aasworld.reachabilitysize = 0; - if (aasworld.reachability) FreeMemory(aasworld.reachability); - aasworld.reachability = NULL; - aasworld.numnodes = 0; - if (aasworld.nodes) FreeMemory(aasworld.nodes); - aasworld.nodes = NULL; - aasworld.numportals = 0; - if (aasworld.portals) FreeMemory(aasworld.portals); - aasworld.portals = NULL; - aasworld.numportals = 0; - if (aasworld.portalindex) FreeMemory(aasworld.portalindex); - aasworld.portalindex = NULL; - aasworld.portalindexsize = 0; - if (aasworld.clusters) FreeMemory(aasworld.clusters); - aasworld.clusters = NULL; - aasworld.numclusters = 0; - // - aasworld.loaded = qfalse; - aasworld.initialized = qfalse; - aasworld.savefile = qfalse; -} //end of the function AAS_DumpAASData -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef AASFILEDEBUG -void AAS_FileInfo(void) -{ - int i, n, optimized; - - botimport.Print(PRT_MESSAGE, "version = %d\n", AASVERSION); - botimport.Print(PRT_MESSAGE, "numvertexes = %d\n", aasworld.numvertexes); - botimport.Print(PRT_MESSAGE, "numplanes = %d\n", aasworld.numplanes); - botimport.Print(PRT_MESSAGE, "numedges = %d\n", aasworld.numedges); - botimport.Print(PRT_MESSAGE, "edgeindexsize = %d\n", aasworld.edgeindexsize); - botimport.Print(PRT_MESSAGE, "numfaces = %d\n", aasworld.numfaces); - botimport.Print(PRT_MESSAGE, "faceindexsize = %d\n", aasworld.faceindexsize); - botimport.Print(PRT_MESSAGE, "numareas = %d\n", aasworld.numareas); - botimport.Print(PRT_MESSAGE, "numareasettings = %d\n", aasworld.numareasettings); - botimport.Print(PRT_MESSAGE, "reachabilitysize = %d\n", aasworld.reachabilitysize); - botimport.Print(PRT_MESSAGE, "numnodes = %d\n", aasworld.numnodes); - botimport.Print(PRT_MESSAGE, "numportals = %d\n", aasworld.numportals); - botimport.Print(PRT_MESSAGE, "portalindexsize = %d\n", aasworld.portalindexsize); - botimport.Print(PRT_MESSAGE, "numclusters = %d\n", aasworld.numclusters); - // - for (n = 0, i = 0; i < aasworld.numareasettings; i++) - { - if (aasworld.areasettings[i].areaflags & AREA_GROUNDED) n++; - } //end for - botimport.Print(PRT_MESSAGE, "num grounded areas = %d\n", n); - // - botimport.Print(PRT_MESSAGE, "planes size %d bytes\n", aasworld.numplanes * sizeof(aas_plane_t)); - botimport.Print(PRT_MESSAGE, "areas size %d bytes\n", aasworld.numareas * sizeof(aas_area_t)); - botimport.Print(PRT_MESSAGE, "areasettings size %d bytes\n", aasworld.numareasettings * sizeof(aas_areasettings_t)); - botimport.Print(PRT_MESSAGE, "nodes size %d bytes\n", aasworld.numnodes * sizeof(aas_node_t)); - botimport.Print(PRT_MESSAGE, "reachability size %d bytes\n", aasworld.reachabilitysize * sizeof(aas_reachability_t)); - botimport.Print(PRT_MESSAGE, "portals size %d bytes\n", aasworld.numportals * sizeof(aas_portal_t)); - botimport.Print(PRT_MESSAGE, "clusters size %d bytes\n", aasworld.numclusters * sizeof(aas_cluster_t)); - - optimized = aasworld.numplanes * sizeof(aas_plane_t) + - aasworld.numareas * sizeof(aas_area_t) + - aasworld.numareasettings * sizeof(aas_areasettings_t) + - aasworld.numnodes * sizeof(aas_node_t) + - aasworld.reachabilitysize * sizeof(aas_reachability_t) + - aasworld.numportals * sizeof(aas_portal_t) + - aasworld.numclusters * sizeof(aas_cluster_t); - botimport.Print(PRT_MESSAGE, "optimzed size %d KB\n", optimized >> 10); -} //end of the function AAS_FileInfo -#endif //AASFILEDEBUG -//=========================================================================== -// allocate memory and read a lump of a AAS file -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *AAS_LoadAASLump(fileHandle_t fp, int offset, int length, int *lastoffset, int size) -{ - char *buf; - // - if (!length) - { - //just alloc a dummy - return (char *) GetClearedHunkMemory(size+1); - } //end if - //seek to the data - if (offset != *lastoffset) - { - botimport.Print(PRT_WARNING, "AAS file not sequentially read\n"); - if (botimport.FS_Seek(fp, offset, FS_SEEK_SET)) - { - AAS_Error("can't seek to aas lump\n"); - AAS_DumpAASData(); - botimport.FS_FCloseFile(fp); - return 0; - } //end if - } //end if - //allocate memory - buf = (char *) GetClearedHunkMemory(length+1); - //read the data - if (length) - { - botimport.FS_Read(buf, length, fp ); - *lastoffset += length; - } //end if - return buf; -} //end of the function AAS_LoadAASLump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DData(unsigned char *data, int size) -{ - int i; - - for (i = 0; i < size; i++) - { - data[i] ^= (unsigned char) i * 119; - } //end for -} //end of the function AAS_DData -//=========================================================================== -// load an aas file -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_LoadAASFile(char *filename) -{ - fileHandle_t fp; - aas_header_t header; - int offset, length, lastoffset; - - botimport.Print(PRT_MESSAGE, "trying to load %s\n", filename); - //dump current loaded aas file - AAS_DumpAASData(); - //open the file - botimport.FS_FOpenFile( filename, &fp, FS_READ ); - if (!fp) - { - AAS_Error("can't open %s\n", filename); - return BLERR_CANNOTOPENAASFILE; - } //end if - //read the header - botimport.FS_Read(&header, sizeof(aas_header_t), fp ); - lastoffset = sizeof(aas_header_t); - //check header identification - header.ident = LittleLong(header.ident); - if (header.ident != AASID) - { - AAS_Error("%s is not an AAS file\n", filename); - botimport.FS_FCloseFile(fp); - return BLERR_WRONGAASFILEID; - } //end if - //check the version - header.version = LittleLong(header.version); - // - if (header.version != AASVERSION_OLD && header.version != AASVERSION) - { - AAS_Error("aas file %s is version %i, not %i\n", filename, header.version, AASVERSION); - botimport.FS_FCloseFile(fp); - return BLERR_WRONGAASFILEVERSION; - } //end if - // - if (header.version == AASVERSION) - { - AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8); - } //end if - // - aasworld.bspchecksum = atoi(LibVarGetString( "sv_mapChecksum")); - if (LittleLong(header.bspchecksum) != aasworld.bspchecksum) - { - AAS_Error("aas file %s is out of date\n", filename); - botimport.FS_FCloseFile(fp); - return BLERR_WRONGAASFILEVERSION; - } //end if - //load the lumps: - //bounding boxes - offset = LittleLong(header.lumps[AASLUMP_BBOXES].fileofs); - length = LittleLong(header.lumps[AASLUMP_BBOXES].filelen); - aasworld.bboxes = (aas_bbox_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_bbox_t)); - aasworld.numbboxes = length / sizeof(aas_bbox_t); - if (aasworld.numbboxes && !aasworld.bboxes) return BLERR_CANNOTREADAASLUMP; - //vertexes - offset = LittleLong(header.lumps[AASLUMP_VERTEXES].fileofs); - length = LittleLong(header.lumps[AASLUMP_VERTEXES].filelen); - aasworld.vertexes = (aas_vertex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_vertex_t)); - aasworld.numvertexes = length / sizeof(aas_vertex_t); - if (aasworld.numvertexes && !aasworld.vertexes) return BLERR_CANNOTREADAASLUMP; - //planes - offset = LittleLong(header.lumps[AASLUMP_PLANES].fileofs); - length = LittleLong(header.lumps[AASLUMP_PLANES].filelen); - aasworld.planes = (aas_plane_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_plane_t)); - aasworld.numplanes = length / sizeof(aas_plane_t); - if (aasworld.numplanes && !aasworld.planes) return BLERR_CANNOTREADAASLUMP; - //edges - offset = LittleLong(header.lumps[AASLUMP_EDGES].fileofs); - length = LittleLong(header.lumps[AASLUMP_EDGES].filelen); - aasworld.edges = (aas_edge_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edge_t)); - aasworld.numedges = length / sizeof(aas_edge_t); - if (aasworld.numedges && !aasworld.edges) return BLERR_CANNOTREADAASLUMP; - //edgeindex - offset = LittleLong(header.lumps[AASLUMP_EDGEINDEX].fileofs); - length = LittleLong(header.lumps[AASLUMP_EDGEINDEX].filelen); - aasworld.edgeindex = (aas_edgeindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edgeindex_t)); - aasworld.edgeindexsize = length / sizeof(aas_edgeindex_t); - if (aasworld.edgeindexsize && !aasworld.edgeindex) return BLERR_CANNOTREADAASLUMP; - //faces - offset = LittleLong(header.lumps[AASLUMP_FACES].fileofs); - length = LittleLong(header.lumps[AASLUMP_FACES].filelen); - aasworld.faces = (aas_face_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_face_t)); - aasworld.numfaces = length / sizeof(aas_face_t); - if (aasworld.numfaces && !aasworld.faces) return BLERR_CANNOTREADAASLUMP; - //faceindex - offset = LittleLong(header.lumps[AASLUMP_FACEINDEX].fileofs); - length = LittleLong(header.lumps[AASLUMP_FACEINDEX].filelen); - aasworld.faceindex = (aas_faceindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_faceindex_t)); - aasworld.faceindexsize = length / sizeof(aas_faceindex_t); - if (aasworld.faceindexsize && !aasworld.faceindex) return BLERR_CANNOTREADAASLUMP; - //convex areas - offset = LittleLong(header.lumps[AASLUMP_AREAS].fileofs); - length = LittleLong(header.lumps[AASLUMP_AREAS].filelen); - aasworld.areas = (aas_area_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_area_t)); - aasworld.numareas = length / sizeof(aas_area_t); - if (aasworld.numareas && !aasworld.areas) return BLERR_CANNOTREADAASLUMP; - //area settings - offset = LittleLong(header.lumps[AASLUMP_AREASETTINGS].fileofs); - length = LittleLong(header.lumps[AASLUMP_AREASETTINGS].filelen); - aasworld.areasettings = (aas_areasettings_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_areasettings_t)); - aasworld.numareasettings = length / sizeof(aas_areasettings_t); - if (aasworld.numareasettings && !aasworld.areasettings) return BLERR_CANNOTREADAASLUMP; - //reachability list - offset = LittleLong(header.lumps[AASLUMP_REACHABILITY].fileofs); - length = LittleLong(header.lumps[AASLUMP_REACHABILITY].filelen); - aasworld.reachability = (aas_reachability_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_reachability_t)); - aasworld.reachabilitysize = length / sizeof(aas_reachability_t); - if (aasworld.reachabilitysize && !aasworld.reachability) return BLERR_CANNOTREADAASLUMP; - //nodes - offset = LittleLong(header.lumps[AASLUMP_NODES].fileofs); - length = LittleLong(header.lumps[AASLUMP_NODES].filelen); - aasworld.nodes = (aas_node_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_node_t)); - aasworld.numnodes = length / sizeof(aas_node_t); - if (aasworld.numnodes && !aasworld.nodes) return BLERR_CANNOTREADAASLUMP; - //cluster portals - offset = LittleLong(header.lumps[AASLUMP_PORTALS].fileofs); - length = LittleLong(header.lumps[AASLUMP_PORTALS].filelen); - aasworld.portals = (aas_portal_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portal_t)); - aasworld.numportals = length / sizeof(aas_portal_t); - if (aasworld.numportals && !aasworld.portals) return BLERR_CANNOTREADAASLUMP; - //cluster portal index - offset = LittleLong(header.lumps[AASLUMP_PORTALINDEX].fileofs); - length = LittleLong(header.lumps[AASLUMP_PORTALINDEX].filelen); - aasworld.portalindex = (aas_portalindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portalindex_t)); - aasworld.portalindexsize = length / sizeof(aas_portalindex_t); - if (aasworld.portalindexsize && !aasworld.portalindex) return BLERR_CANNOTREADAASLUMP; - //clusters - offset = LittleLong(header.lumps[AASLUMP_CLUSTERS].fileofs); - length = LittleLong(header.lumps[AASLUMP_CLUSTERS].filelen); - aasworld.clusters = (aas_cluster_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_cluster_t)); - aasworld.numclusters = length / sizeof(aas_cluster_t); - if (aasworld.numclusters && !aasworld.clusters) return BLERR_CANNOTREADAASLUMP; - //swap everything - AAS_SwapAASData(); - //aas file is loaded - aasworld.loaded = qtrue; - //close the file - botimport.FS_FCloseFile(fp); - // -#ifdef AASFILEDEBUG - AAS_FileInfo(); -#endif //AASFILEDEBUG - // - return BLERR_NOERROR; -} //end of the function AAS_LoadAASFile -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -static int AAS_WriteAASLump_offset; - -int AAS_WriteAASLump(fileHandle_t fp, aas_header_t *h, int lumpnum, void *data, int length) -{ - aas_lump_t *lump; - - lump = &h->lumps[lumpnum]; - - lump->fileofs = LittleLong(AAS_WriteAASLump_offset); //LittleLong(ftell(fp)); - lump->filelen = LittleLong(length); - - if (length > 0) - { - botimport.FS_Write(data, length, fp ); - } //end if - - AAS_WriteAASLump_offset += length; - - return qtrue; -} //end of the function AAS_WriteAASLump -//=========================================================================== -// aas data is useless after writing to file because it is byte swapped -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_WriteAASFile(char *filename) -{ - aas_header_t header; - fileHandle_t fp; - - botimport.Print(PRT_MESSAGE, "writing %s\n", filename); - //swap the aas data - AAS_SwapAASData(); - //initialize the file header - Com_Memset(&header, 0, sizeof(aas_header_t)); - header.ident = LittleLong(AASID); - header.version = LittleLong(AASVERSION); - header.bspchecksum = LittleLong(aasworld.bspchecksum); - //open a new file - botimport.FS_FOpenFile( filename, &fp, FS_WRITE ); - if (!fp) - { - botimport.Print(PRT_ERROR, "error opening %s\n", filename); - return qfalse; - } //end if - //write the header - botimport.FS_Write(&header, sizeof(aas_header_t), fp); - AAS_WriteAASLump_offset = sizeof(aas_header_t); - //add the data lumps to the file - if (!AAS_WriteAASLump(fp, &header, AASLUMP_BBOXES, aasworld.bboxes, - aasworld.numbboxes * sizeof(aas_bbox_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_VERTEXES, aasworld.vertexes, - aasworld.numvertexes * sizeof(aas_vertex_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_PLANES, aasworld.planes, - aasworld.numplanes * sizeof(aas_plane_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGES, aasworld.edges, - aasworld.numedges * sizeof(aas_edge_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGEINDEX, aasworld.edgeindex, - aasworld.edgeindexsize * sizeof(aas_edgeindex_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACES, aasworld.faces, - aasworld.numfaces * sizeof(aas_face_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACEINDEX, aasworld.faceindex, - aasworld.faceindexsize * sizeof(aas_faceindex_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREAS, aasworld.areas, - aasworld.numareas * sizeof(aas_area_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREASETTINGS, aasworld.areasettings, - aasworld.numareasettings * sizeof(aas_areasettings_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_REACHABILITY, aasworld.reachability, - aasworld.reachabilitysize * sizeof(aas_reachability_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_NODES, aasworld.nodes, - aasworld.numnodes * sizeof(aas_node_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALS, aasworld.portals, - aasworld.numportals * sizeof(aas_portal_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALINDEX, aasworld.portalindex, - aasworld.portalindexsize * sizeof(aas_portalindex_t))) return qfalse; - if (!AAS_WriteAASLump(fp, &header, AASLUMP_CLUSTERS, aasworld.clusters, - aasworld.numclusters * sizeof(aas_cluster_t))) return qfalse; - //rewrite the header with the added lumps - botimport.FS_Seek(fp, 0, FS_SEEK_SET); - AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8); - botimport.FS_Write(&header, sizeof(aas_header_t), fp); - //close the file - botimport.FS_FCloseFile(fp); - return qtrue; -} //end of the function AAS_WriteAASFile diff --git a/tools/quake3/bspc/deps/botlib/be_aas_file.h b/tools/quake3/bspc/deps/botlib/be_aas_file.h deleted file mode 100644 index c2271fcf..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_file.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_file.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_file.h $ - * - *****************************************************************************/ - -#ifdef AASINTERN -//loads the AAS file with the given name -int AAS_LoadAASFile(char *filename); -//writes an AAS file with the given name -qboolean AAS_WriteAASFile(char *filename); -//dumps the loaded AAS data -void AAS_DumpAASData(void); -//print AAS file information -void AAS_FileInfo(void); -#endif //AASINTERN - diff --git a/tools/quake3/bspc/deps/botlib/be_aas_funcs.h b/tools/quake3/bspc/deps/botlib/be_aas_funcs.h deleted file mode 100644 index 87c7636f..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_funcs.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_funcs.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_funcs.h $ - * - *****************************************************************************/ - -#ifndef BSPCINCLUDE - -#include "be_aas_main.h" -#include "be_aas_entity.h" -#include "be_aas_sample.h" -#include "be_aas_cluster.h" -#include "be_aas_reach.h" -#include "be_aas_route.h" -#include "be_aas_routealt.h" -#include "be_aas_debug.h" -#include "be_aas_file.h" -#include "be_aas_optimize.h" -#include "be_aas_bsp.h" -#include "be_aas_move.h" - -#endif //BSPCINCLUDE diff --git a/tools/quake3/bspc/deps/botlib/be_aas_main.c b/tools/quake3/bspc/deps/botlib/be_aas_main.c deleted file mode 100644 index 2560bf18..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_main.c +++ /dev/null @@ -1,429 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_main.c - * - * desc: AAS - * - * $Archive: /MissionPack/code/botlib/be_aas_main.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_libvar.h" -#include "l_utils.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "l_log.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_aas_def.h" - -aas_t aasworld; - -libvar_t *saveroutingcache; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void QDECL AAS_Error(char *fmt, ...) -{ - char str[1024]; - va_list arglist; - - va_start(arglist, fmt); - vsprintf(str, fmt, arglist); - va_end(arglist); - botimport.Print(PRT_FATAL, str); -} //end of the function AAS_Error -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *AAS_StringFromIndex(char *indexname, char *stringindex[], int numindexes, int index) -{ - if (!aasworld.indexessetup) - { - botimport.Print(PRT_ERROR, "%s: index %d not setup\n", indexname, index); - return ""; - } //end if - if (index < 0 || index >= numindexes) - { - botimport.Print(PRT_ERROR, "%s: index %d out of range\n", indexname, index); - return ""; - } //end if - if (!stringindex[index]) - { - if (index) - { - botimport.Print(PRT_ERROR, "%s: reference to unused index %d\n", indexname, index); - } //end if - return ""; - } //end if - return stringindex[index]; -} //end of the function AAS_StringFromIndex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_IndexFromString(char *indexname, char *stringindex[], int numindexes, char *string) -{ - int i; - if (!aasworld.indexessetup) - { - botimport.Print(PRT_ERROR, "%s: index not setup \"%s\"\n", indexname, string); - return 0; - } //end if - for (i = 0; i < numindexes; i++) - { - if (!stringindex[i]) continue; - if (!Q_stricmp(stringindex[i], string)) return i; - } //end for - return 0; -} //end of the function AAS_IndexFromString -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *AAS_ModelFromIndex(int index) -{ - return AAS_StringFromIndex("ModelFromIndex", &aasworld.configstrings[CS_MODELS], MAX_MODELS, index); -} //end of the function AAS_ModelFromIndex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_IndexFromModel(char *modelname) -{ - return AAS_IndexFromString("IndexFromModel", &aasworld.configstrings[CS_MODELS], MAX_MODELS, modelname); -} //end of the function AAS_IndexFromModel -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_UpdateStringIndexes(int numconfigstrings, char *configstrings[]) -{ - int i; - //set string pointers and copy the strings - for (i = 0; i < numconfigstrings; i++) - { - if (configstrings[i]) - { - //if (aasworld.configstrings[i]) FreeMemory(aasworld.configstrings[i]); - aasworld.configstrings[i] = (char *) GetMemory(strlen(configstrings[i]) + 1); - strcpy(aasworld.configstrings[i], configstrings[i]); - } //end if - } //end for - aasworld.indexessetup = qtrue; -} //end of the function AAS_UpdateStringIndexes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_Loaded(void) -{ - return aasworld.loaded; -} //end of the function AAS_Loaded -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_Initialized(void) -{ - return aasworld.initialized; -} //end of the function AAS_Initialized -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_SetInitialized(void) -{ - aasworld.initialized = qtrue; - botimport.Print(PRT_MESSAGE, "AAS initialized.\n"); -#ifdef DEBUG - //create all the routing cache - //AAS_CreateAllRoutingCache(); - // - //AAS_RoutingInfo(); -#endif -} //end of the function AAS_SetInitialized -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ContinueInit(float time) -{ - //if no AAS file loaded - if (!aasworld.loaded) return; - //if AAS is already initialized - if (aasworld.initialized) return; - //calculate reachability, if not finished return - if (AAS_ContinueInitReachability(time)) return; - //initialize clustering for the new map - AAS_InitClustering(); - //if reachability has been calculated and an AAS file should be written - //or there is a forced data optimization - if (aasworld.savefile || ((int)LibVarGetValue("forcewrite"))) - { - //optimize the AAS data - if ((int)LibVarValue("aasoptimize", "0")) AAS_Optimize(); - //save the AAS file - if (AAS_WriteAASFile(aasworld.filename)) - { - botimport.Print(PRT_MESSAGE, "%s written succesfully\n", aasworld.filename); - } //end if - else - { - botimport.Print(PRT_ERROR, "couldn't write %s\n", aasworld.filename); - } //end else - } //end if - //initialize the routing - AAS_InitRouting(); - //at this point AAS is initialized - AAS_SetInitialized(); -} //end of the function AAS_ContinueInit -//=========================================================================== -// called at the start of every frame -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_StartFrame(float time) -{ - aasworld.time = time; - //unlink all entities that were not updated last frame - AAS_UnlinkInvalidEntities(); - //invalidate the entities - AAS_InvalidateEntities(); - //initialize AAS - AAS_ContinueInit(time); - // - aasworld.frameroutingupdates = 0; - // - if (bot_developer) - { - if (LibVarGetValue("showcacheupdates")) - { - AAS_RoutingInfo(); - LibVarSet("showcacheupdates", "0"); - } //end if - if (LibVarGetValue("showmemoryusage")) - { - PrintUsedMemorySize(); - LibVarSet("showmemoryusage", "0"); - } //end if - if (LibVarGetValue("memorydump")) - { - PrintMemoryLabels(); - LibVarSet("memorydump", "0"); - } //end if - } //end if - // - if (saveroutingcache->value) - { - AAS_WriteRouteCache(); - LibVarSet("saveroutingcache", "0"); - } //end if - // - aasworld.numframes++; - return BLERR_NOERROR; -} //end of the function AAS_StartFrame -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float AAS_Time(void) -{ - return aasworld.time; -} //end of the function AAS_Time -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj ) -{ - vec3_t pVec, vec; - - VectorSubtract( point, vStart, pVec ); - VectorSubtract( vEnd, vStart, vec ); - VectorNormalize( vec ); - // project onto the directional vector for this segment - VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj ); -} //end of the function AAS_ProjectPointOntoVector -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_LoadFiles(const char *mapname) -{ - int errnum; - char aasfile[MAX_PATH]; -// char bspfile[MAX_PATH]; - - strcpy(aasworld.mapname, mapname); - //NOTE: first reset the entity links into the AAS areas and BSP leaves - // the AAS link heap and BSP link heap are reset after respectively the - // AAS file and BSP file are loaded - AAS_ResetEntityLinks(); - // load bsp info - AAS_LoadBSPFile(); - - //load the aas file - Com_sprintf(aasfile, MAX_PATH, "maps/%s.aas", mapname); - errnum = AAS_LoadAASFile(aasfile); - if (errnum != BLERR_NOERROR) - return errnum; - - botimport.Print(PRT_MESSAGE, "loaded %s\n", aasfile); - strncpy(aasworld.filename, aasfile, MAX_PATH); - return BLERR_NOERROR; -} //end of the function AAS_LoadFiles -//=========================================================================== -// called everytime a map changes -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_LoadMap(const char *mapname) -{ - int errnum; - - //if no mapname is provided then the string indexes are updated - if (!mapname) - { - return 0; - } //end if - // - aasworld.initialized = qfalse; - //NOTE: free the routing caches before loading a new map because - // to free the caches the old number of areas, number of clusters - // and number of areas in a clusters must be available - AAS_FreeRoutingCaches(); - //load the map - errnum = AAS_LoadFiles(mapname); - if (errnum != BLERR_NOERROR) - { - aasworld.loaded = qfalse; - return errnum; - } //end if - // - AAS_InitSettings(); - //initialize the AAS link heap for the new map - AAS_InitAASLinkHeap(); - //initialize the AAS linked entities for the new map - AAS_InitAASLinkedEntities(); - //initialize reachability for the new map - AAS_InitReachability(); - //initialize the alternative routing - AAS_InitAlternativeRouting(); - //everything went ok - return 0; -} //end of the function AAS_LoadMap -//=========================================================================== -// called when the library is first loaded -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_Setup(void) -{ - aasworld.maxclients = (int) LibVarValue("maxclients", "128"); - aasworld.maxentities = (int) LibVarValue("maxentities", "1024"); - // as soon as it's set to 1 the routing cache will be saved - saveroutingcache = LibVar("saveroutingcache", "0"); - //allocate memory for the entities - if (aasworld.entities) FreeMemory(aasworld.entities); - aasworld.entities = (aas_entity_t *) GetClearedHunkMemory(aasworld.maxentities * sizeof(aas_entity_t)); - //invalidate all the entities - AAS_InvalidateEntities(); - //force some recalculations - //LibVarSet("forceclustering", "1"); //force clustering calculation - //LibVarSet("forcereachability", "1"); //force reachability calculation - aasworld.numframes = 0; - return BLERR_NOERROR; -} //end of the function AAS_Setup -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_Shutdown(void) -{ - AAS_ShutdownAlternativeRouting(); - // - AAS_DumpBSPData(); - //free routing caches - AAS_FreeRoutingCaches(); - //free aas link heap - AAS_FreeAASLinkHeap(); - //free aas linked entities - AAS_FreeAASLinkedEntities(); - //free the aas data - AAS_DumpAASData(); - //free the entities - if (aasworld.entities) FreeMemory(aasworld.entities); - //clear the aasworld structure - Com_Memset(&aasworld, 0, sizeof(aas_t)); - //aas has not been initialized - aasworld.initialized = qfalse; - //NOTE: as soon as a new .bsp file is loaded the .bsp file memory is - // freed an reallocated, so there's no need to free that memory here - //print shutdown - botimport.Print(PRT_MESSAGE, "AAS shutdown.\n"); -} //end of the function AAS_Shutdown diff --git a/tools/quake3/bspc/deps/botlib/be_aas_main.h b/tools/quake3/bspc/deps/botlib/be_aas_main.h deleted file mode 100644 index 1d0bef10..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_main.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_main.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_main.h $ - * - *****************************************************************************/ - -#ifdef AASINTERN - -extern aas_t aasworld; - -//AAS error message -void QDECL AAS_Error(char *fmt, ...); -//set AAS initialized -void AAS_SetInitialized(void); -//setup AAS with the given number of entities and clients -int AAS_Setup(void); -//shutdown AAS -void AAS_Shutdown(void); -//start a new map -int AAS_LoadMap(const char *mapname); -//start a new time frame -int AAS_StartFrame(float time); -#endif //AASINTERN - -//returns true if AAS is initialized -int AAS_Initialized(void); -//returns true if the AAS file is loaded -int AAS_Loaded(void); -//returns the model name from the given index -char *AAS_ModelFromIndex(int index); -//returns the index from the given model name -int AAS_IndexFromModel(char *modelname); -//returns the current time -float AAS_Time(void); -// -void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj ); diff --git a/tools/quake3/bspc/deps/botlib/be_aas_move.c b/tools/quake3/bspc/deps/botlib/be_aas_move.c deleted file mode 100644 index f869c8c6..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_move.c +++ /dev/null @@ -1,1101 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_move.c - * - * desc: AAS - * - * $Archive: /MissionPack/code/botlib/be_aas_move.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "l_libvar.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_aas_def.h" - -extern botlib_import_t botimport; - -aas_settings_t aassettings; - -//#define AAS_MOVE_DEBUG - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs) -{ - vec3_t end; - bsp_trace_t trace; - - VectorCopy(origin, end); - end[2] -= 100; - trace = AAS_Trace(origin, mins, maxs, end, 0, CONTENTS_SOLID); - if (trace.startsolid) return qfalse; - VectorCopy(trace.endpos, origin); - return qtrue; -} //end of the function AAS_DropToFloor -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitSettings(void) -{ - aassettings.phys_gravitydirection[0] = 0; - aassettings.phys_gravitydirection[1] = 0; - aassettings.phys_gravitydirection[2] = -1; - aassettings.phys_friction = LibVarValue("phys_friction", "6"); - aassettings.phys_stopspeed = LibVarValue("phys_stopspeed", "100"); - aassettings.phys_gravity = LibVarValue("phys_gravity", "800"); - aassettings.phys_waterfriction = LibVarValue("phys_waterfriction", "1"); - aassettings.phys_watergravity = LibVarValue("phys_watergravity", "400"); - aassettings.phys_maxvelocity = LibVarValue("phys_maxvelocity", "320"); - aassettings.phys_maxwalkvelocity = LibVarValue("phys_maxwalkvelocity", "320"); - aassettings.phys_maxcrouchvelocity = LibVarValue("phys_maxcrouchvelocity", "100"); - aassettings.phys_maxswimvelocity = LibVarValue("phys_maxswimvelocity", "150"); - aassettings.phys_walkaccelerate = LibVarValue("phys_walkaccelerate", "10"); - aassettings.phys_airaccelerate = LibVarValue("phys_airaccelerate", "1"); - aassettings.phys_swimaccelerate = LibVarValue("phys_swimaccelerate", "4"); - aassettings.phys_maxstep = LibVarValue("phys_maxstep", "19"); - aassettings.phys_maxsteepness = LibVarValue("phys_maxsteepness", "0.7"); - aassettings.phys_maxwaterjump = LibVarValue("phys_maxwaterjump", "18"); - aassettings.phys_maxbarrier = LibVarValue("phys_maxbarrier", "33"); - aassettings.phys_jumpvel = LibVarValue("phys_jumpvel", "270"); - aassettings.phys_falldelta5 = LibVarValue("phys_falldelta5", "40"); - aassettings.phys_falldelta10 = LibVarValue("phys_falldelta10", "60"); - aassettings.rs_waterjump = LibVarValue("rs_waterjump", "400"); - aassettings.rs_teleport = LibVarValue("rs_teleport", "50"); - aassettings.rs_barrierjump = LibVarValue("rs_barrierjump", "100"); - aassettings.rs_startcrouch = LibVarValue("rs_startcrouch", "300"); - aassettings.rs_startgrapple = LibVarValue("rs_startgrapple", "500"); - aassettings.rs_startwalkoffledge = LibVarValue("rs_startwalkoffledge", "70"); - aassettings.rs_startjump = LibVarValue("rs_startjump", "300"); - aassettings.rs_rocketjump = LibVarValue("rs_rocketjump", "500"); - aassettings.rs_bfgjump = LibVarValue("rs_bfgjump", "500"); - aassettings.rs_jumppad = LibVarValue("rs_jumppad", "250"); - aassettings.rs_aircontrolledjumppad = LibVarValue("rs_aircontrolledjumppad", "300"); - aassettings.rs_funcbob = LibVarValue("rs_funcbob", "300"); - aassettings.rs_startelevator = LibVarValue("rs_startelevator", "50"); - aassettings.rs_falldamage5 = LibVarValue("rs_falldamage5", "300"); - aassettings.rs_falldamage10 = LibVarValue("rs_falldamage10", "500"); - aassettings.rs_maxfallheight = LibVarValue("rs_maxfallheight", "0"); - aassettings.rs_maxjumpfallheight = LibVarValue("rs_maxjumpfallheight", "450"); -} //end of the function AAS_InitSettings -//=========================================================================== -// returns qtrue if the bot is against a ladder -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AgainstLadder(vec3_t origin) -{ - int areanum, i, facenum, side; - vec3_t org; - aas_plane_t *plane; - aas_face_t *face; - aas_area_t *area; - - VectorCopy(origin, org); - areanum = AAS_PointAreaNum(org); - if (!areanum) - { - org[0] += 1; - areanum = AAS_PointAreaNum(org); - if (!areanum) - { - org[1] += 1; - areanum = AAS_PointAreaNum(org); - if (!areanum) - { - org[0] -= 2; - areanum = AAS_PointAreaNum(org); - if (!areanum) - { - org[1] -= 2; - areanum = AAS_PointAreaNum(org); - } //end if - } //end if - } //end if - } //end if - //if in solid... wrrr shouldn't happen - if (!areanum) return qfalse; - //if not in a ladder area - if (!(aasworld.areasettings[areanum].areaflags & AREA_LADDER)) return qfalse; - //if a crouch only area - if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qfalse; - // - area = &aasworld.areas[areanum]; - for (i = 0; i < area->numfaces; i++) - { - facenum = aasworld.faceindex[area->firstface + i]; - side = facenum < 0; - face = &aasworld.faces[abs(facenum)]; - //if the face isn't a ladder face - if (!(face->faceflags & FACE_LADDER)) continue; - //get the plane the face is in - plane = &aasworld.planes[face->planenum ^ side]; - //if the origin is pretty close to the plane - if (abs(DotProduct(plane->normal, origin) - plane->dist) < 3) - { - if (AAS_PointInsideFace(abs(facenum), origin, 0.1f)) return qtrue; - } //end if - } //end for - return qfalse; -} //end of the function AAS_AgainstLadder -//=========================================================================== -// returns qtrue if the bot is on the ground -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_OnGround(vec3_t origin, int presencetype, int passent) -{ - aas_trace_t trace; - vec3_t end, up = {0, 0, 1}; - aas_plane_t *plane; - - VectorCopy(origin, end); - end[2] -= 10; - - trace = AAS_TraceClientBBox(origin, end, presencetype, passent); - - //if in solid - if (trace.startsolid) return qfalse; - //if nothing hit at all - if (trace.fraction >= 1.0) return qfalse; - //if too far from the hit plane - if (origin[2] - trace.endpos[2] > 10) return qfalse; - //check if the plane isn't too steep - plane = AAS_PlaneFromNum(trace.planenum); - if (DotProduct(plane->normal, up) < aassettings.phys_maxsteepness) return qfalse; - //the bot is on the ground - return qtrue; -} //end of the function AAS_OnGround -//=========================================================================== -// returns qtrue if a bot at the given position is swimming -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_Swimming(vec3_t origin) -{ - vec3_t testorg; - - VectorCopy(origin, testorg); - testorg[2] -= 2; - if (AAS_PointContents(testorg) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) return qtrue; - return qfalse; -} //end of the function AAS_Swimming -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -static vec3_t VEC_UP = {0, -1, 0}; -static vec3_t MOVEDIR_UP = {0, 0, 1}; -static vec3_t VEC_DOWN = {0, -2, 0}; -static vec3_t MOVEDIR_DOWN = {0, 0, -1}; - -void AAS_SetMovedir(vec3_t angles, vec3_t movedir) -{ - if (VectorCompare(angles, VEC_UP)) - { - VectorCopy(MOVEDIR_UP, movedir); - } //end if - else if (VectorCompare(angles, VEC_DOWN)) - { - VectorCopy(MOVEDIR_DOWN, movedir); - } //end else if - else - { - AngleVectors(angles, movedir, NULL, NULL); - } //end else -} //end of the function AAS_SetMovedir -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_JumpReachRunStart(aas_reachability_t *reach, vec3_t runstart) -{ - vec3_t hordir, start, cmdmove; - aas_clientmove_t move; - - // - hordir[0] = reach->start[0] - reach->end[0]; - hordir[1] = reach->start[1] - reach->end[1]; - hordir[2] = 0; - VectorNormalize(hordir); - //start point - VectorCopy(reach->start, start); - start[2] += 1; - //get command movement - VectorScale(hordir, 400, cmdmove); - // - AAS_PredictClientMovement(&move, -1, start, PRESENCE_NORMAL, qtrue, - vec3_origin, cmdmove, 1, 2, 0.1f, - SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA| - SE_HITGROUNDDAMAGE|SE_GAP, 0, qfalse); - VectorCopy(move.endpos, runstart); - //don't enter slime or lava and don't fall from too high - if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) - { - VectorCopy(start, runstart); - } //end if -} //end of the function AAS_JumpReachRunStart -//=========================================================================== -// returns the Z velocity when rocket jumping at the origin -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float AAS_WeaponJumpZVelocity(vec3_t origin, float radiusdamage) -{ - vec3_t kvel, v, start, end, forward, right, viewangles, dir; - float mass, knockback, points; - vec3_t rocketoffset = {8, 8, -8}; - vec3_t botmins = {-16, -16, -24}; - vec3_t botmaxs = {16, 16, 32}; - bsp_trace_t bsptrace; - - //look down (90 degrees) - viewangles[PITCH] = 90; - viewangles[YAW] = 0; - viewangles[ROLL] = 0; - //get the start point shooting from - VectorCopy(origin, start); - start[2] += 8; //view offset Z - AngleVectors(viewangles, forward, right, NULL); - start[0] += forward[0] * rocketoffset[0] + right[0] * rocketoffset[1]; - start[1] += forward[1] * rocketoffset[0] + right[1] * rocketoffset[1]; - start[2] += forward[2] * rocketoffset[0] + right[2] * rocketoffset[1] + rocketoffset[2]; - //end point of the trace - VectorMA(start, 500, forward, end); - //trace a line to get the impact point - bsptrace = AAS_Trace(start, NULL, NULL, end, 1, CONTENTS_SOLID); - //calculate the damage the bot will get from the rocket impact - VectorAdd(botmins, botmaxs, v); - VectorMA(origin, 0.5, v, v); - VectorSubtract(bsptrace.endpos, v, v); - // - points = radiusdamage - 0.5 * VectorLength(v); - if (points < 0) points = 0; - //the owner of the rocket gets half the damage - points *= 0.5; - //mass of the bot (p_client.c: PutClientInServer) - mass = 200; - //knockback is the same as the damage points - knockback = points; - //direction of the damage (from trace.endpos to bot origin) - VectorSubtract(origin, bsptrace.endpos, dir); - VectorNormalize(dir); - //damage velocity - VectorScale(dir, 1600.0 * (float)knockback / mass, kvel); //the rocket jump hack... - //rocket impact velocity + jump velocity - return kvel[2] + aassettings.phys_jumpvel; -} //end of the function AAS_WeaponJumpZVelocity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float AAS_RocketJumpZVelocity(vec3_t origin) -{ - //rocket radius damage is 120 (p_weapon.c: Weapon_RocketLauncher_Fire) - return AAS_WeaponJumpZVelocity(origin, 120); -} //end of the function AAS_RocketJumpZVelocity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float AAS_BFGJumpZVelocity(vec3_t origin) -{ - //bfg radius damage is 1000 (p_weapon.c: weapon_bfg_fire) - return AAS_WeaponJumpZVelocity(origin, 120); -} //end of the function AAS_BFGJumpZVelocity -//=========================================================================== -// applies ground friction to the given velocity -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_Accelerate(vec3_t velocity, float frametime, vec3_t wishdir, float wishspeed, float accel) -{ - // q2 style - int i; - float addspeed, accelspeed, currentspeed; - - currentspeed = DotProduct(velocity, wishdir); - addspeed = wishspeed - currentspeed; - if (addspeed <= 0) { - return; - } - accelspeed = accel*frametime*wishspeed; - if (accelspeed > addspeed) { - accelspeed = addspeed; - } - - for (i=0 ; i<3 ; i++) { - velocity[i] += accelspeed*wishdir[i]; - } -} //end of the function AAS_Accelerate -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_AirControl(vec3_t start, vec3_t end, vec3_t velocity, vec3_t cmdmove) -{ - vec3_t dir; - - VectorSubtract(end, start, dir); -} //end of the function AAS_AirControl -//=========================================================================== -// applies ground friction to the given velocity -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ApplyFriction(vec3_t vel, float friction, float stopspeed, - float frametime) -{ - float speed, control, newspeed; - - //horizontal speed - speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]); - if (speed) - { - control = speed < stopspeed ? stopspeed : speed; - newspeed = speed - frametime * control * friction; - if (newspeed < 0) newspeed = 0; - newspeed /= speed; - vel[0] *= newspeed; - vel[1] *= newspeed; - } //end if -} //end of the function AAS_ApplyFriction -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_ClipToBBox(aas_trace_t *trace, vec3_t start, vec3_t end, int presencetype, vec3_t mins, vec3_t maxs) -{ - int i, j, side; - float front, back, frac, planedist; - vec3_t bboxmins, bboxmaxs, absmins, absmaxs, dir, mid; - - AAS_PresenceTypeBoundingBox(presencetype, bboxmins, bboxmaxs); - VectorSubtract(mins, bboxmaxs, absmins); - VectorSubtract(maxs, bboxmins, absmaxs); - // - VectorCopy(end, trace->endpos); - trace->fraction = 1; - for (i = 0; i < 3; i++) - { - if (start[i] < absmins[i] && end[i] < absmins[i]) return qfalse; - if (start[i] > absmaxs[i] && end[i] > absmaxs[i]) return qfalse; - } //end for - //check bounding box collision - VectorSubtract(end, start, dir); - frac = 1; - for (i = 0; i < 3; i++) - { - //get plane to test collision with for the current axis direction - if (dir[i] > 0) planedist = absmins[i]; - else planedist = absmaxs[i]; - //calculate collision fraction - front = start[i] - planedist; - back = end[i] - planedist; - frac = front / (front-back); - //check if between bounding planes of next axis - side = i + 1; - if (side > 2) side = 0; - mid[side] = start[side] + dir[side] * frac; - if (mid[side] > absmins[side] && mid[side] < absmaxs[side]) - { - //check if between bounding planes of next axis - side++; - if (side > 2) side = 0; - mid[side] = start[side] + dir[side] * frac; - if (mid[side] > absmins[side] && mid[side] < absmaxs[side]) - { - mid[i] = planedist; - break; - } //end if - } //end if - } //end for - //if there was a collision - if (i != 3) - { - trace->startsolid = qfalse; - trace->fraction = frac; - trace->ent = 0; - trace->planenum = 0; - trace->area = 0; - trace->lastarea = 0; - //trace endpos - for (j = 0; j < 3; j++) trace->endpos[j] = start[j] + dir[j] * frac; - return qtrue; - } //end if - return qfalse; -} //end of the function AAS_ClipToBBox -//=========================================================================== -// predicts the movement -// assumes regular bounding box sizes -// NOTE: out of water jumping is not included -// NOTE: grappling hook is not included -// -// Parameter: origin : origin to start with -// presencetype : presence type to start with -// velocity : velocity to start with -// cmdmove : client command movement -// cmdframes : number of frame cmdmove is valid -// maxframes : maximum number of predicted frames -// frametime : duration of one predicted frame -// stopevent : events that stop the prediction -// stopareanum : stop as soon as entered this area -// Returns: aas_clientmove_t -// Changes Globals: - -//=========================================================================== -int AAS_ClientMovementPrediction(struct aas_clientmove_s *move, - int entnum, vec3_t origin, - int presencetype, int onground, - vec3_t velocity, vec3_t cmdmove, - int cmdframes, - int maxframes, float frametime, - int stopevent, int stopareanum, - vec3_t mins, vec3_t maxs, int visualize) -{ - float phys_friction, phys_stopspeed, phys_gravity, phys_waterfriction; - float phys_watergravity; - float phys_walkaccelerate, phys_airaccelerate, phys_swimaccelerate; - float phys_maxwalkvelocity, phys_maxcrouchvelocity, phys_maxswimvelocity; - float phys_maxstep, phys_maxsteepness, phys_jumpvel, friction; - float gravity, delta, maxvel, wishspeed, accelerate; - //float velchange, newvel; - int n, i, j, pc, step, swimming, ax, crouch, event, jump_frame, areanum; - int areas[20], numareas; - vec3_t points[20]; - vec3_t org, end, feet, start, stepend, lastorg, wishdir; - vec3_t frame_test_vel, old_frame_test_vel, left_test_vel; - vec3_t up = {0, 0, 1}; - aas_plane_t *plane, *plane2; - aas_trace_t trace, steptrace; - - if (frametime <= 0) frametime = 0.1f; - // - phys_friction = aassettings.phys_friction; - phys_stopspeed = aassettings.phys_stopspeed; - phys_gravity = aassettings.phys_gravity; - phys_waterfriction = aassettings.phys_waterfriction; - phys_watergravity = aassettings.phys_watergravity; - phys_maxwalkvelocity = aassettings.phys_maxwalkvelocity;// * frametime; - phys_maxcrouchvelocity = aassettings.phys_maxcrouchvelocity;// * frametime; - phys_maxswimvelocity = aassettings.phys_maxswimvelocity;// * frametime; - phys_walkaccelerate = aassettings.phys_walkaccelerate; - phys_airaccelerate = aassettings.phys_airaccelerate; - phys_swimaccelerate = aassettings.phys_swimaccelerate; - phys_maxstep = aassettings.phys_maxstep; - phys_maxsteepness = aassettings.phys_maxsteepness; - phys_jumpvel = aassettings.phys_jumpvel * frametime; - // - Com_Memset(move, 0, sizeof(aas_clientmove_t)); - Com_Memset(&trace, 0, sizeof(aas_trace_t)); - //start at the current origin - VectorCopy(origin, org); - org[2] += 0.25; - //velocity to test for the first frame - VectorScale(velocity, frametime, frame_test_vel); - // - jump_frame = -1; - //predict a maximum of 'maxframes' ahead - for (n = 0; n < maxframes; n++) - { - swimming = AAS_Swimming(org); - //get gravity depending on swimming or not - gravity = swimming ? phys_watergravity : phys_gravity; - //apply gravity at the START of the frame - frame_test_vel[2] = frame_test_vel[2] - (gravity * 0.1 * frametime); - //if on the ground or swimming - if (onground || swimming) - { - friction = swimming ? phys_friction : phys_waterfriction; - //apply friction - VectorScale(frame_test_vel, 1/frametime, frame_test_vel); - AAS_ApplyFriction(frame_test_vel, friction, phys_stopspeed, frametime); - VectorScale(frame_test_vel, frametime, frame_test_vel); - } //end if - crouch = qfalse; - //apply command movement - if (n < cmdframes) - { - ax = 0; - maxvel = phys_maxwalkvelocity; - accelerate = phys_airaccelerate; - VectorCopy(cmdmove, wishdir); - if (onground) - { - if (cmdmove[2] < -300) - { - crouch = qtrue; - maxvel = phys_maxcrouchvelocity; - } //end if - //if not swimming and upmove is positive then jump - if (!swimming && cmdmove[2] > 1) - { - //jump velocity minus the gravity for one frame + 5 for safety - frame_test_vel[2] = phys_jumpvel - (gravity * 0.1 * frametime) + 5; - jump_frame = n; - //jumping so air accelerate - accelerate = phys_airaccelerate; - } //end if - else - { - accelerate = phys_walkaccelerate; - } //end else - ax = 2; - } //end if - if (swimming) - { - maxvel = phys_maxswimvelocity; - accelerate = phys_swimaccelerate; - ax = 3; - } //end if - else - { - wishdir[2] = 0; - } //end else - // - wishspeed = VectorNormalize(wishdir); - if (wishspeed > maxvel) wishspeed = maxvel; - VectorScale(frame_test_vel, 1/frametime, frame_test_vel); - AAS_Accelerate(frame_test_vel, frametime, wishdir, wishspeed, accelerate); - VectorScale(frame_test_vel, frametime, frame_test_vel); - /* - for (i = 0; i < ax; i++) - { - velchange = (cmdmove[i] * frametime) - frame_test_vel[i]; - if (velchange > phys_maxacceleration) velchange = phys_maxacceleration; - else if (velchange < -phys_maxacceleration) velchange = -phys_maxacceleration; - newvel = frame_test_vel[i] + velchange; - // - if (frame_test_vel[i] <= maxvel && newvel > maxvel) frame_test_vel[i] = maxvel; - else if (frame_test_vel[i] >= -maxvel && newvel < -maxvel) frame_test_vel[i] = -maxvel; - else frame_test_vel[i] = newvel; - } //end for - */ - } //end if - if (crouch) - { - presencetype = PRESENCE_CROUCH; - } //end if - else if (presencetype == PRESENCE_CROUCH) - { - if (AAS_PointPresenceType(org) & PRESENCE_NORMAL) - { - presencetype = PRESENCE_NORMAL; - } //end if - } //end else - //save the current origin - VectorCopy(org, lastorg); - //move linear during one frame - VectorCopy(frame_test_vel, left_test_vel); - j = 0; - do - { - VectorAdd(org, left_test_vel, end); - //trace a bounding box - trace = AAS_TraceClientBBox(org, end, presencetype, entnum); - // -//#ifdef AAS_MOVE_DEBUG - if (visualize) - { - if (trace.startsolid) botimport.Print(PRT_MESSAGE, "PredictMovement: start solid\n"); - AAS_DebugLine(org, trace.endpos, LINECOLOR_RED); - } //end if -//#endif //AAS_MOVE_DEBUG - // - if (stopevent & (SE_ENTERAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_TOUCHCLUSTERPORTAL)) - { - numareas = AAS_TraceAreas(org, trace.endpos, areas, points, 20); - for (i = 0; i < numareas; i++) - { - if (stopevent & SE_ENTERAREA) - { - if (areas[i] == stopareanum) - { - VectorCopy(points[i], move->endpos); - VectorScale(frame_test_vel, 1/frametime, move->velocity); - move->endarea = areas[i]; - move->trace = trace; - move->stopevent = SE_ENTERAREA; - move->presencetype = presencetype; - move->endcontents = 0; - move->time = n * frametime; - move->frames = n; - return qtrue; - } //end if - } //end if - //NOTE: if not the first frame - if ((stopevent & SE_TOUCHJUMPPAD) && n) - { - if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_JUMPPAD) - { - VectorCopy(points[i], move->endpos); - VectorScale(frame_test_vel, 1/frametime, move->velocity); - move->endarea = areas[i]; - move->trace = trace; - move->stopevent = SE_TOUCHJUMPPAD; - move->presencetype = presencetype; - move->endcontents = 0; - move->time = n * frametime; - move->frames = n; - return qtrue; - } //end if - } //end if - if (stopevent & SE_TOUCHTELEPORTER) - { - if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_TELEPORTER) - { - VectorCopy(points[i], move->endpos); - move->endarea = areas[i]; - VectorScale(frame_test_vel, 1/frametime, move->velocity); - move->trace = trace; - move->stopevent = SE_TOUCHTELEPORTER; - move->presencetype = presencetype; - move->endcontents = 0; - move->time = n * frametime; - move->frames = n; - return qtrue; - } //end if - } //end if - if (stopevent & SE_TOUCHCLUSTERPORTAL) - { - if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_CLUSTERPORTAL) - { - VectorCopy(points[i], move->endpos); - move->endarea = areas[i]; - VectorScale(frame_test_vel, 1/frametime, move->velocity); - move->trace = trace; - move->stopevent = SE_TOUCHCLUSTERPORTAL; - move->presencetype = presencetype; - move->endcontents = 0; - move->time = n * frametime; - move->frames = n; - return qtrue; - } //end if - } //end if - } //end for - } //end if - // - if (stopevent & SE_HITBOUNDINGBOX) - { - if (AAS_ClipToBBox(&trace, org, trace.endpos, presencetype, mins, maxs)) - { - VectorCopy(trace.endpos, move->endpos); - move->endarea = AAS_PointAreaNum(move->endpos); - VectorScale(frame_test_vel, 1/frametime, move->velocity); - move->trace = trace; - move->stopevent = SE_HITBOUNDINGBOX; - move->presencetype = presencetype; - move->endcontents = 0; - move->time = n * frametime; - move->frames = n; - return qtrue; - } //end if - } //end if - //move the entity to the trace end point - VectorCopy(trace.endpos, org); - //if there was a collision - if (trace.fraction < 1.0) - { - //get the plane the bounding box collided with - plane = AAS_PlaneFromNum(trace.planenum); - // - if (stopevent & SE_HITGROUNDAREA) - { - if (DotProduct(plane->normal, up) > phys_maxsteepness) - { - VectorCopy(org, start); - start[2] += 0.5; - if (AAS_PointAreaNum(start) == stopareanum) - { - VectorCopy(start, move->endpos); - move->endarea = stopareanum; - VectorScale(frame_test_vel, 1/frametime, move->velocity); - move->trace = trace; - move->stopevent = SE_HITGROUNDAREA; - move->presencetype = presencetype; - move->endcontents = 0; - move->time = n * frametime; - move->frames = n; - return qtrue; - } //end if - } //end if - } //end if - //assume there's no step - step = qfalse; - //if it is a vertical plane and the bot didn't jump recently - if (plane->normal[2] == 0 && (jump_frame < 0 || n - jump_frame > 2)) - { - //check for a step - VectorMA(org, -0.25, plane->normal, start); - VectorCopy(start, stepend); - start[2] += phys_maxstep; - steptrace = AAS_TraceClientBBox(start, stepend, presencetype, entnum); - // - if (!steptrace.startsolid) - { - plane2 = AAS_PlaneFromNum(steptrace.planenum); - if (DotProduct(plane2->normal, up) > phys_maxsteepness) - { - VectorSubtract(end, steptrace.endpos, left_test_vel); - left_test_vel[2] = 0; - frame_test_vel[2] = 0; -//#ifdef AAS_MOVE_DEBUG - if (visualize) - { - if (steptrace.endpos[2] - org[2] > 0.125) - { - VectorCopy(org, start); - start[2] = steptrace.endpos[2]; - AAS_DebugLine(org, start, LINECOLOR_BLUE); - } //end if - } //end if -//#endif //AAS_MOVE_DEBUG - org[2] = steptrace.endpos[2]; - step = qtrue; - } //end if - } //end if - } //end if - // - if (!step) - { - //velocity left to test for this frame is the projection - //of the current test velocity into the hit plane - VectorMA(left_test_vel, -DotProduct(left_test_vel, plane->normal), - plane->normal, left_test_vel); - //store the old velocity for landing check - VectorCopy(frame_test_vel, old_frame_test_vel); - //test velocity for the next frame is the projection - //of the velocity of the current frame into the hit plane - VectorMA(frame_test_vel, -DotProduct(frame_test_vel, plane->normal), - plane->normal, frame_test_vel); - //check for a landing on an almost horizontal floor - if (DotProduct(plane->normal, up) > phys_maxsteepness) - { - onground = qtrue; - } //end if - if (stopevent & SE_HITGROUNDDAMAGE) - { - delta = 0; - if (old_frame_test_vel[2] < 0 && - frame_test_vel[2] > old_frame_test_vel[2] && - !onground) - { - delta = old_frame_test_vel[2]; - } //end if - else if (onground) - { - delta = frame_test_vel[2] - old_frame_test_vel[2]; - } //end else - if (delta) - { - delta = delta * 10; - delta = delta * delta * 0.0001; - if (swimming) delta = 0; - // never take falling damage if completely underwater - /* - if (ent->waterlevel == 3) return; - if (ent->waterlevel == 2) delta *= 0.25; - if (ent->waterlevel == 1) delta *= 0.5; - */ - if (delta > 40) - { - VectorCopy(org, move->endpos); - move->endarea = AAS_PointAreaNum(org); - VectorCopy(frame_test_vel, move->velocity); - move->trace = trace; - move->stopevent = SE_HITGROUNDDAMAGE; - move->presencetype = presencetype; - move->endcontents = 0; - move->time = n * frametime; - move->frames = n; - return qtrue; - } //end if - } //end if - } //end if - } //end if - } //end if - //extra check to prevent endless loop - if (++j > 20) return qfalse; - //while there is a plane hit - } while(trace.fraction < 1.0); - //if going down - if (frame_test_vel[2] <= 10) - { - //check for a liquid at the feet of the bot - VectorCopy(org, feet); - feet[2] -= 22; - pc = AAS_PointContents(feet); - //get event from pc - event = SE_NONE; - if (pc & CONTENTS_LAVA) event |= SE_ENTERLAVA; - if (pc & CONTENTS_SLIME) event |= SE_ENTERSLIME; - if (pc & CONTENTS_WATER) event |= SE_ENTERWATER; - // - areanum = AAS_PointAreaNum(org); - if (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA) - event |= SE_ENTERLAVA; - if (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME) - event |= SE_ENTERSLIME; - if (aasworld.areasettings[areanum].contents & AREACONTENTS_WATER) - event |= SE_ENTERWATER; - //if in lava or slime - if (event & stopevent) - { - VectorCopy(org, move->endpos); - move->endarea = areanum; - VectorScale(frame_test_vel, 1/frametime, move->velocity); - move->stopevent = event & stopevent; - move->presencetype = presencetype; - move->endcontents = pc; - move->time = n * frametime; - move->frames = n; - return qtrue; - } //end if - } //end if - // - onground = AAS_OnGround(org, presencetype, entnum); - //if onground and on the ground for at least one whole frame - if (onground) - { - if (stopevent & SE_HITGROUND) - { - VectorCopy(org, move->endpos); - move->endarea = AAS_PointAreaNum(org); - VectorScale(frame_test_vel, 1/frametime, move->velocity); - move->trace = trace; - move->stopevent = SE_HITGROUND; - move->presencetype = presencetype; - move->endcontents = 0; - move->time = n * frametime; - move->frames = n; - return qtrue; - } //end if - } //end if - else if (stopevent & SE_LEAVEGROUND) - { - VectorCopy(org, move->endpos); - move->endarea = AAS_PointAreaNum(org); - VectorScale(frame_test_vel, 1/frametime, move->velocity); - move->trace = trace; - move->stopevent = SE_LEAVEGROUND; - move->presencetype = presencetype; - move->endcontents = 0; - move->time = n * frametime; - move->frames = n; - return qtrue; - } //end else if - else if (stopevent & SE_GAP) - { - aas_trace_t gaptrace; - - VectorCopy(org, start); - VectorCopy(start, end); - end[2] -= 48 + aassettings.phys_maxbarrier; - gaptrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); - //if solid is found the bot cannot walk any further and will not fall into a gap - if (!gaptrace.startsolid) - { - //if it is a gap (lower than one step height) - if (gaptrace.endpos[2] < org[2] - aassettings.phys_maxstep - 1) - { - if (!(AAS_PointContents(end) & CONTENTS_WATER)) - { - VectorCopy(lastorg, move->endpos); - move->endarea = AAS_PointAreaNum(lastorg); - VectorScale(frame_test_vel, 1/frametime, move->velocity); - move->trace = trace; - move->stopevent = SE_GAP; - move->presencetype = presencetype; - move->endcontents = 0; - move->time = n * frametime; - move->frames = n; - return qtrue; - } //end if - } //end if - } //end if - } //end else if - } //end for - // - VectorCopy(org, move->endpos); - move->endarea = AAS_PointAreaNum(org); - VectorScale(frame_test_vel, 1/frametime, move->velocity); - move->stopevent = SE_NONE; - move->presencetype = presencetype; - move->endcontents = 0; - move->time = n * frametime; - move->frames = n; - // - return qtrue; -} //end of the function AAS_ClientMovementPrediction -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_PredictClientMovement(struct aas_clientmove_s *move, - int entnum, vec3_t origin, - int presencetype, int onground, - vec3_t velocity, vec3_t cmdmove, - int cmdframes, - int maxframes, float frametime, - int stopevent, int stopareanum, int visualize) -{ - vec3_t mins, maxs; - return AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground, - velocity, cmdmove, cmdframes, maxframes, - frametime, stopevent, stopareanum, - mins, maxs, visualize); -} //end of the function AAS_PredictClientMovement -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_ClientMovementHitBBox(struct aas_clientmove_s *move, - int entnum, vec3_t origin, - int presencetype, int onground, - vec3_t velocity, vec3_t cmdmove, - int cmdframes, - int maxframes, float frametime, - vec3_t mins, vec3_t maxs, int visualize) -{ - return AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground, - velocity, cmdmove, cmdframes, maxframes, - frametime, SE_HITBOUNDINGBOX, 0, - mins, maxs, visualize); -} //end of the function AAS_ClientMovementHitBBox -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir) -{ - vec3_t velocity, cmdmove; - aas_clientmove_t move; - - VectorClear(velocity); - if (!AAS_Swimming(origin)) dir[2] = 0; - VectorNormalize(dir); - VectorScale(dir, 400, cmdmove); - cmdmove[2] = 224; - AAS_ClearShownDebugLines(); - AAS_PredictClientMovement(&move, entnum, origin, PRESENCE_NORMAL, qtrue, - velocity, cmdmove, 13, 13, 0.1f, SE_HITGROUND, 0, qtrue);//SE_LEAVEGROUND); - if (move.stopevent & SE_LEAVEGROUND) - { - botimport.Print(PRT_MESSAGE, "leave ground\n"); - } //end if -} //end of the function TestMovementPrediction -//=========================================================================== -// calculates the horizontal velocity needed to perform a jump from start -// to end -// -// Parameter: zvel : z velocity for jump -// start : start position of jump -// end : end position of jump -// *speed : returned speed for jump -// Returns: qfalse if too high or too far from start to end -// Changes Globals: - -//=========================================================================== -int AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity) -{ - float phys_gravity, phys_maxvelocity; - float maxjump, height2fall, t, top; - vec3_t dir; - - phys_gravity = aassettings.phys_gravity; - phys_maxvelocity = aassettings.phys_maxvelocity; - - //maximum height a player can jump with the given initial z velocity - maxjump = 0.5 * phys_gravity * (zvel / phys_gravity) * (zvel / phys_gravity); - //top of the parabolic jump - top = start[2] + maxjump; - //height the bot will fall from the top - height2fall = top - end[2]; - //if the goal is to high to jump to - if (height2fall < 0) - { - *velocity = phys_maxvelocity; - return 0; - } //end if - //time a player takes to fall the height - t = sqrt(height2fall / (0.5 * phys_gravity)); - //direction from start to end - VectorSubtract(end, start, dir); - // - if ( (t + zvel / phys_gravity) == 0.0f ) { - *velocity = phys_maxvelocity; - return 0; - } - //calculate horizontal speed - *velocity = sqrt(dir[0]*dir[0] + dir[1]*dir[1]) / (t + zvel / phys_gravity); - //the horizontal speed must be lower than the max speed - if (*velocity > phys_maxvelocity) - { - *velocity = phys_maxvelocity; - return 0; - } //end if - return 1; -} //end of the function AAS_HorizontalVelocityForJump diff --git a/tools/quake3/bspc/deps/botlib/be_aas_move.h b/tools/quake3/bspc/deps/botlib/be_aas_move.h deleted file mode 100644 index b00e41ab..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_move.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_move.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_move.h $ - * - *****************************************************************************/ - -#ifdef AASINTERN -extern aas_settings_t aassettings; -#endif //AASINTERN - -//movement prediction -int AAS_PredictClientMovement(struct aas_clientmove_s *move, - int entnum, vec3_t origin, - int presencetype, int onground, - vec3_t velocity, vec3_t cmdmove, - int cmdframes, - int maxframes, float frametime, - int stopevent, int stopareanum, int visualize); -//predict movement until bounding box is hit -int AAS_ClientMovementHitBBox(struct aas_clientmove_s *move, - int entnum, vec3_t origin, - int presencetype, int onground, - vec3_t velocity, vec3_t cmdmove, - int cmdframes, - int maxframes, float frametime, - vec3_t mins, vec3_t maxs, int visualize); -//returns true if on the ground at the given origin -int AAS_OnGround(vec3_t origin, int presencetype, int passent); -//returns true if swimming at the given origin -int AAS_Swimming(vec3_t origin); -//returns the jump reachability run start point -void AAS_JumpReachRunStart(struct aas_reachability_s *reach, vec3_t runstart); -//returns true if against a ladder at the given origin -int AAS_AgainstLadder(vec3_t origin); -//rocket jump Z velocity when rocket-jumping at origin -float AAS_RocketJumpZVelocity(vec3_t origin); -//bfg jump Z velocity when bfg-jumping at origin -float AAS_BFGJumpZVelocity(vec3_t origin); -//calculates the horizontal velocity needed for a jump and returns true this velocity could be calculated -int AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity); -// -void AAS_SetMovedir(vec3_t angles, vec3_t movedir); -// -int AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs); -// -void AAS_InitSettings(void); diff --git a/tools/quake3/bspc/deps/botlib/be_aas_optimize.c b/tools/quake3/bspc/deps/botlib/be_aas_optimize.c deleted file mode 100644 index 97a9b77d..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_optimize.c +++ /dev/null @@ -1,312 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_optimize.c - * - * desc: decreases the .aas file size after the reachabilities have - * been calculated, just dumps all the faces, edges and vertexes - * - * $Archive: /MissionPack/code/botlib/be_aas_optimize.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_libvar.h" -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_aas_def.h" - -typedef struct optimized_s -{ - //vertexes - int numvertexes; - aas_vertex_t *vertexes; - //edges - int numedges; - aas_edge_t *edges; - //edge index - int edgeindexsize; - aas_edgeindex_t *edgeindex; - //faces - int numfaces; - aas_face_t *faces; - //face index - int faceindexsize; - aas_faceindex_t *faceindex; - //convex areas - int numareas; - aas_area_t *areas; - // - int *vertexoptimizeindex; - int *edgeoptimizeindex; - int *faceoptimizeindex; -} optimized_t; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_KeepEdge(aas_edge_t *edge) -{ - return 1; -} //end of the function AAS_KeepFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_OptimizeEdge(optimized_t *optimized, int edgenum) -{ - int i, optedgenum; - aas_edge_t *edge, *optedge; - - edge = &aasworld.edges[abs(edgenum)]; - if (!AAS_KeepEdge(edge)) return 0; - - optedgenum = optimized->edgeoptimizeindex[abs(edgenum)]; - if (optedgenum) - { - //keep the edge reversed sign - if (edgenum > 0) return optedgenum; - else return -optedgenum; - } //end if - - optedge = &optimized->edges[optimized->numedges]; - - for (i = 0; i < 2; i++) - { - if (optimized->vertexoptimizeindex[edge->v[i]]) - { - optedge->v[i] = optimized->vertexoptimizeindex[edge->v[i]]; - } //end if - else - { - VectorCopy(aasworld.vertexes[edge->v[i]], optimized->vertexes[optimized->numvertexes]); - optedge->v[i] = optimized->numvertexes; - optimized->vertexoptimizeindex[edge->v[i]] = optimized->numvertexes; - optimized->numvertexes++; - } //end else - } //end for - optimized->edgeoptimizeindex[abs(edgenum)] = optimized->numedges; - optedgenum = optimized->numedges; - optimized->numedges++; - //keep the edge reversed sign - if (edgenum > 0) return optedgenum; - else return -optedgenum; -} //end of the function AAS_OptimizeEdge -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_KeepFace(aas_face_t *face) -{ - if (!(face->faceflags & FACE_LADDER)) return 0; - else return 1; -} //end of the function AAS_KeepFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_OptimizeFace(optimized_t *optimized, int facenum) -{ - int i, edgenum, optedgenum, optfacenum; - aas_face_t *face, *optface; - - face = &aasworld.faces[abs(facenum)]; - if (!AAS_KeepFace(face)) return 0; - - optfacenum = optimized->faceoptimizeindex[abs(facenum)]; - if (optfacenum) - { - //keep the face side sign - if (facenum > 0) return optfacenum; - else return -optfacenum; - } //end if - - optface = &optimized->faces[optimized->numfaces]; - Com_Memcpy(optface, face, sizeof(aas_face_t)); - - optface->numedges = 0; - optface->firstedge = optimized->edgeindexsize; - for (i = 0; i < face->numedges; i++) - { - edgenum = aasworld.edgeindex[face->firstedge + i]; - optedgenum = AAS_OptimizeEdge(optimized, edgenum); - if (optedgenum) - { - optimized->edgeindex[optface->firstedge + optface->numedges] = optedgenum; - optface->numedges++; - optimized->edgeindexsize++; - } //end if - } //end for - optimized->faceoptimizeindex[abs(facenum)] = optimized->numfaces; - optfacenum = optimized->numfaces; - optimized->numfaces++; - //keep the face side sign - if (facenum > 0) return optfacenum; - else return -optfacenum; -} //end of the function AAS_OptimizeFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_OptimizeArea(optimized_t *optimized, int areanum) -{ - int i, facenum, optfacenum; - aas_area_t *area, *optarea; - - area = &aasworld.areas[areanum]; - optarea = &optimized->areas[areanum]; - Com_Memcpy(optarea, area, sizeof(aas_area_t)); - - optarea->numfaces = 0; - optarea->firstface = optimized->faceindexsize; - for (i = 0; i < area->numfaces; i++) - { - facenum = aasworld.faceindex[area->firstface + i]; - optfacenum = AAS_OptimizeFace(optimized, facenum); - if (optfacenum) - { - optimized->faceindex[optarea->firstface + optarea->numfaces] = optfacenum; - optarea->numfaces++; - optimized->faceindexsize++; - } //end if - } //end for -} //end of the function AAS_OptimizeArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_OptimizeAlloc(optimized_t *optimized) -{ - optimized->vertexes = (aas_vertex_t *) GetClearedMemory(aasworld.numvertexes * sizeof(aas_vertex_t)); - optimized->numvertexes = 0; - optimized->edges = (aas_edge_t *) GetClearedMemory(aasworld.numedges * sizeof(aas_edge_t)); - optimized->numedges = 1; //edge zero is a dummy - optimized->edgeindex = (aas_edgeindex_t *) GetClearedMemory(aasworld.edgeindexsize * sizeof(aas_edgeindex_t)); - optimized->edgeindexsize = 0; - optimized->faces = (aas_face_t *) GetClearedMemory(aasworld.numfaces * sizeof(aas_face_t)); - optimized->numfaces = 1; //face zero is a dummy - optimized->faceindex = (aas_faceindex_t *) GetClearedMemory(aasworld.faceindexsize * sizeof(aas_faceindex_t)); - optimized->faceindexsize = 0; - optimized->areas = (aas_area_t *) GetClearedMemory(aasworld.numareas * sizeof(aas_area_t)); - optimized->numareas = aasworld.numareas; - // - optimized->vertexoptimizeindex = (int *) GetClearedMemory(aasworld.numvertexes * sizeof(int)); - optimized->edgeoptimizeindex = (int *) GetClearedMemory(aasworld.numedges * sizeof(int)); - optimized->faceoptimizeindex = (int *) GetClearedMemory(aasworld.numfaces * sizeof(int)); -} //end of the function AAS_OptimizeAlloc -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_OptimizeStore(optimized_t *optimized) -{ - //store the optimized vertexes - if (aasworld.vertexes) FreeMemory(aasworld.vertexes); - aasworld.vertexes = optimized->vertexes; - aasworld.numvertexes = optimized->numvertexes; - //store the optimized edges - if (aasworld.edges) FreeMemory(aasworld.edges); - aasworld.edges = optimized->edges; - aasworld.numedges = optimized->numedges; - //store the optimized edge index - if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex); - aasworld.edgeindex = optimized->edgeindex; - aasworld.edgeindexsize = optimized->edgeindexsize; - //store the optimized faces - if (aasworld.faces) FreeMemory(aasworld.faces); - aasworld.faces = optimized->faces; - aasworld.numfaces = optimized->numfaces; - //store the optimized face index - if (aasworld.faceindex) FreeMemory(aasworld.faceindex); - aasworld.faceindex = optimized->faceindex; - aasworld.faceindexsize = optimized->faceindexsize; - //store the optimized areas - if (aasworld.areas) FreeMemory(aasworld.areas); - aasworld.areas = optimized->areas; - aasworld.numareas = optimized->numareas; - //free optimize indexes - FreeMemory(optimized->vertexoptimizeindex); - FreeMemory(optimized->edgeoptimizeindex); - FreeMemory(optimized->faceoptimizeindex); -} //end of the function AAS_OptimizeStore -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_Optimize(void) -{ - int i, sign; - optimized_t optimized; - - AAS_OptimizeAlloc(&optimized); - for (i = 1; i < aasworld.numareas; i++) - { - AAS_OptimizeArea(&optimized, i); - } //end for - //reset the reachability face pointers - for (i = 0; i < aasworld.reachabilitysize; i++) - { - //NOTE: for TRAVEL_ELEVATOR the facenum is the model number of - // the elevator - if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) continue; - //NOTE: for TRAVEL_JUMPPAD the facenum is the Z velocity and the edgenum is the hor velocity - if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) continue; - //NOTE: for TRAVEL_FUNCBOB the facenum and edgenum contain other coded information - if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) continue; - // - sign = aasworld.reachability[i].facenum; - aasworld.reachability[i].facenum = optimized.faceoptimizeindex[abs(aasworld.reachability[i].facenum)]; - if (sign < 0) aasworld.reachability[i].facenum = -aasworld.reachability[i].facenum; - sign = aasworld.reachability[i].edgenum; - aasworld.reachability[i].edgenum = optimized.edgeoptimizeindex[abs(aasworld.reachability[i].edgenum)]; - if (sign < 0) aasworld.reachability[i].edgenum = -aasworld.reachability[i].edgenum; - } //end for - //store the optimized AAS data into aasworld - AAS_OptimizeStore(&optimized); - //print some nice stuff :) - botimport.Print(PRT_MESSAGE, "AAS data optimized.\n"); -} //end of the function AAS_Optimize diff --git a/tools/quake3/bspc/deps/botlib/be_aas_optimize.h b/tools/quake3/bspc/deps/botlib/be_aas_optimize.h deleted file mode 100644 index 799c28a3..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_optimize.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_optimize.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_optimize.h $ - * - *****************************************************************************/ - -void AAS_Optimize(void); - diff --git a/tools/quake3/bspc/deps/botlib/be_aas_reach.c b/tools/quake3/bspc/deps/botlib/be_aas_reach.c deleted file mode 100644 index 447431c1..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_reach.c +++ /dev/null @@ -1,4547 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_reach.c - * - * desc: reachability calculations - * - * $Archive: /MissionPack/code/botlib/be_aas_reach.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_log.h" -#include "l_memory.h" -#include "l_script.h" -#include "l_libvar.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_aas_def.h" - -extern int Sys_MilliSeconds(void); - - -extern botlib_import_t botimport; - -//#define REACH_DEBUG - -//NOTE: all travel times are in hundreth of a second -//maximum number of reachability links -#define AAS_MAX_REACHABILITYSIZE 65536 -//number of areas reachability is calculated for each frame -#define REACHABILITYAREASPERCYCLE 15 -//number of units reachability points are placed inside the areas -#define INSIDEUNITS 2 -#define INSIDEUNITS_WALKEND 5 -#define INSIDEUNITS_WALKSTART 0.1 -#define INSIDEUNITS_WATERJUMP 15 -//area flag used for weapon jumping -#define AREA_WEAPONJUMP 8192 //valid area to weapon jump to -//number of reachabilities of each type -int reach_swim; //swim -int reach_equalfloor; //walk on floors with equal height -int reach_step; //step up -int reach_walk; //walk of step -int reach_barrier; //jump up to a barrier -int reach_waterjump; //jump out of water -int reach_walkoffledge; //walk of a ledge -int reach_jump; //jump -int reach_ladder; //climb or descent a ladder -int reach_teleport; //teleport -int reach_elevator; //use an elevator -int reach_funcbob; //use a func bob -int reach_grapple; //grapple hook -int reach_doublejump; //double jump -int reach_rampjump; //ramp jump -int reach_strafejump; //strafe jump (just normal jump but further) -int reach_rocketjump; //rocket jump -int reach_bfgjump; //bfg jump -int reach_jumppad; //jump pads -//if true grapple reachabilities are skipped -int calcgrapplereach; -//linked reachability -typedef struct aas_lreachability_s -{ - int areanum; //number of the reachable area - int facenum; //number of the face towards the other area - int edgenum; //number of the edge towards the other area - vec3_t start; //start point of inter area movement - vec3_t end; //end point of inter area movement - int traveltype; //type of travel required to get to the area - unsigned short int traveltime; //travel time of the inter area movement - // - struct aas_lreachability_s *next; -} aas_lreachability_t; -//temporary reachabilities -aas_lreachability_t *reachabilityheap; //heap with reachabilities -aas_lreachability_t *nextreachability; //next free reachability from the heap -aas_lreachability_t **areareachability; //reachability links for every area -int numlreachabilities; - -//=========================================================================== -// returns the surface area of the given face -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float AAS_FaceArea(aas_face_t *face) -{ - int i, edgenum, side; - float total; - vec_t *v; - vec3_t d1, d2, cross; - aas_edge_t *edge; - - edgenum = aasworld.edgeindex[face->firstedge]; - side = edgenum < 0; - edge = &aasworld.edges[abs(edgenum)]; - v = aasworld.vertexes[edge->v[side]]; - - total = 0; - for (i = 1; i < face->numedges - 1; i++) - { - edgenum = aasworld.edgeindex[face->firstedge + i]; - side = edgenum < 0; - edge = &aasworld.edges[abs(edgenum)]; - VectorSubtract(aasworld.vertexes[edge->v[side]], v, d1); - VectorSubtract(aasworld.vertexes[edge->v[!side]], v, d2); - CrossProduct(d1, d2, cross); - total += 0.5 * VectorLength(cross); - } //end for - return total; -} //end of the function AAS_FaceArea -//=========================================================================== -// returns the volume of an area -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float AAS_AreaVolume(int areanum) -{ - int i, edgenum, facenum, side; - vec_t d, a, volume; - vec3_t corner; - aas_plane_t *plane; - aas_edge_t *edge; - aas_face_t *face; - aas_area_t *area; - - area = &aasworld.areas[areanum]; - facenum = aasworld.faceindex[area->firstface]; - face = &aasworld.faces[abs(facenum)]; - edgenum = aasworld.edgeindex[face->firstedge]; - edge = &aasworld.edges[abs(edgenum)]; - // - VectorCopy(aasworld.vertexes[edge->v[0]], corner); - - //make tetrahedrons to all other faces - volume = 0; - for (i = 0; i < area->numfaces; i++) - { - facenum = abs(aasworld.faceindex[area->firstface + i]); - face = &aasworld.faces[facenum]; - side = face->backarea != areanum; - plane = &aasworld.planes[face->planenum ^ side]; - d = -(DotProduct (corner, plane->normal) - plane->dist); - a = AAS_FaceArea(face); - volume += d * a; - } //end for - - volume /= 3; - return volume; -} //end of the function AAS_AreaVolume -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_BestReachableLinkArea(aas_link_t *areas) -{ - aas_link_t *link; - - for (link = areas; link; link = link->next_area) - { - if (AAS_AreaGrounded(link->areanum) || AAS_AreaSwim(link->areanum)) - { - return link->areanum; - } //end if - } //end for - // - for (link = areas; link; link = link->next_area) - { - if (link->areanum) return link->areanum; - //FIXME: this is a bad idea when the reachability is not yet - // calculated when the level items are loaded - if (AAS_AreaReachability(link->areanum)) - return link->areanum; - } //end for - return 0; -} //end of the function AAS_BestReachableLinkArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_GetJumpPadInfo(int ent, vec3_t areastart, vec3_t absmins, vec3_t absmaxs, vec3_t velocity) -{ - int modelnum, ent2; - float speed, height, gravity, time, dist, forward; - vec3_t origin, angles, teststart, ent2origin; - aas_trace_t trace; - char model[MAX_EPAIRKEY]; - char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY]; - - // - AAS_FloatForBSPEpairKey(ent, "speed", &speed); - if (!speed) speed = 1000; - VectorClear(angles); - //get the mins, maxs and origin of the model - AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); - if (model[0]) modelnum = atoi(model+1); - else modelnum = 0; - AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin); - VectorAdd(origin, absmins, absmins); - VectorAdd(origin, absmaxs, absmaxs); - VectorAdd(absmins, absmaxs, origin); - VectorScale (origin, 0.5, origin); - - //get the start areas - VectorCopy(origin, teststart); - teststart[2] += 64; - trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1); - if (trace.startsolid) - { - botimport.Print(PRT_MESSAGE, "trigger_push start solid\n"); - VectorCopy(origin, areastart); - } //end if - else - { - VectorCopy(trace.endpos, areastart); - } //end else - areastart[2] += 0.125; - // - //AAS_DrawPermanentCross(origin, 4, 4); - //get the target entity - AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY); - for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2)) - { - if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue; - if (!strcmp(targetname, target)) break; - } //end for - if (!ent2) - { - botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target); - return qfalse; - } //end if - AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin); - // - height = ent2origin[2] - origin[2]; - gravity = aassettings.phys_gravity; - time = sqrt( height / ( 0.5 * gravity ) ); - if (!time) - { - botimport.Print(PRT_MESSAGE, "trigger_push without time\n"); - return qfalse; - } //end if - // set s.origin2 to the push velocity - VectorSubtract ( ent2origin, origin, velocity); - dist = VectorNormalize( velocity); - forward = dist / time; - //FIXME: why multiply by 1.1 - forward *= 1.1f; - VectorScale(velocity, forward, velocity); - velocity[2] = time * gravity; - return qtrue; -} //end of the function AAS_GetJumpPadInfo -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs) -{ - int area2num, ent, bot_visualizejumppads, bestareanum; - float volume, bestareavolume; - vec3_t areastart, cmdmove, bboxmins, bboxmaxs; - vec3_t absmins, absmaxs, velocity; - aas_clientmove_t move; - aas_link_t *areas, *link; - char classname[MAX_EPAIRKEY]; - -#ifdef BSPC - bot_visualizejumppads = 0; -#else - bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0"); -#endif - VectorAdd(origin, mins, bboxmins); - VectorAdd(origin, maxs, bboxmaxs); - for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) - { - if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; - if (strcmp(classname, "trigger_push")) continue; - // - if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue; - //get the areas the jump pad brush is in - areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); - for (link = areas; link; link = link->next_area) - { - if (AAS_AreaJumpPad(link->areanum)) break; - } //end for - if (!link) - { - botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n"); - AAS_UnlinkFromAreas(areas); - continue; - } //end if - // - //botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]); - // - VectorSet(cmdmove, 0, 0, 0); - Com_Memset(&move, 0, sizeof(aas_clientmove_t)); - area2num = 0; - AAS_ClientMovementHitBBox(&move, -1, areastart, PRESENCE_NORMAL, qfalse, - velocity, cmdmove, 0, 30, 0.1f, bboxmins, bboxmaxs, bot_visualizejumppads); - if (move.frames < 30) - { - bestareanum = 0; - bestareavolume = 0; - for (link = areas; link; link = link->next_area) - { - if (!AAS_AreaJumpPad(link->areanum)) continue; - volume = AAS_AreaVolume(link->areanum); - if (volume >= bestareavolume) - { - bestareanum = link->areanum; - bestareavolume = volume; - } //end if - } //end if - AAS_UnlinkFromAreas(areas); - return bestareanum; - } //end if - AAS_UnlinkFromAreas(areas); - } //end for - return 0; -} //end of the function AAS_BestReachableFromJumpPadArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin) -{ - int areanum, i, j, k, l; - aas_link_t *areas; - vec3_t absmins, absmaxs; - //vec3_t bbmins, bbmaxs; - vec3_t start, end; - aas_trace_t trace; - - if (!aasworld.loaded) - { - botimport.Print(PRT_ERROR, "AAS_BestReachableArea: aas not loaded\n"); - return 0; - } //end if - //find a point in an area - VectorCopy(origin, start); - areanum = AAS_PointAreaNum(start); - //while no area found fudge around a little - for (i = 0; i < 5 && !areanum; i++) - { - for (j = 0; j < 5 && !areanum; j++) - { - for (k = -1; k <= 1 && !areanum; k++) - { - for (l = -1; l <= 1 && !areanum; l++) - { - VectorCopy(origin, start); - start[0] += (float) j * 4 * k; - start[1] += (float) j * 4 * l; - start[2] += (float) i * 4; - areanum = AAS_PointAreaNum(start); - } //end for - } //end for - } //end for - } //end for - //if an area was found - if (areanum) - { - //drop client bbox down and try again - VectorCopy(start, end); - start[2] += 0.25; - end[2] -= 50; - trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); - if (!trace.startsolid) - { - areanum = AAS_PointAreaNum(trace.endpos); - VectorCopy(trace.endpos, goalorigin); - //FIXME: cannot enable next line right now because the reachability - // does not have to be calculated when the level items are loaded - //if the origin is in an area with reachability - //if (AAS_AreaReachability(areanum)) return areanum; - if (areanum) return areanum; - } //end if - else - { - //it can very well happen that the AAS_PointAreaNum function tells that - //a point is in an area and that starting a AAS_TraceClientBBox from that - //point will return trace.startsolid qtrue -#if 0 - if (AAS_PointAreaNum(start)) - { - Log_Write("point %f %f %f in area %d but trace startsolid", start[0], start[1], start[2], areanum); - AAS_DrawPermanentCross(start, 4, LINECOLOR_RED); - } //end if - botimport.Print(PRT_MESSAGE, "AAS_BestReachableArea: start solid\n"); -#endif - VectorCopy(start, goalorigin); - return areanum; - } //end else - } //end if - // - //AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs); - //NOTE: the goal origin does not have to be in the goal area - // because the bot will have to move towards the item origin anyway - VectorCopy(origin, goalorigin); - // - VectorAdd(origin, mins, absmins); - VectorAdd(origin, maxs, absmaxs); - //add bounding box size - //VectorSubtract(absmins, bbmaxs, absmins); - //VectorSubtract(absmaxs, bbmins, absmaxs); - //link an invalid (-1) entity - areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); - //get the reachable link arae - areanum = AAS_BestReachableLinkArea(areas); - //unlink the invalid entity - AAS_UnlinkFromAreas(areas); - // - return areanum; -} //end of the function AAS_BestReachableArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_SetupReachabilityHeap(void) -{ - int i; - - reachabilityheap = (aas_lreachability_t *) GetClearedMemory( - AAS_MAX_REACHABILITYSIZE * sizeof(aas_lreachability_t)); - for (i = 0; i < AAS_MAX_REACHABILITYSIZE-1; i++) - { - reachabilityheap[i].next = &reachabilityheap[i+1]; - } //end for - reachabilityheap[AAS_MAX_REACHABILITYSIZE-1].next = NULL; - nextreachability = reachabilityheap; - numlreachabilities = 0; -} //end of the function AAS_InitReachabilityHeap -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShutDownReachabilityHeap(void) -{ - FreeMemory(reachabilityheap); - numlreachabilities = 0; -} //end of the function AAS_ShutDownReachabilityHeap -//=========================================================================== -// returns a reachability link -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_lreachability_t *AAS_AllocReachability(void) -{ - aas_lreachability_t *r; - - if (!nextreachability) return NULL; - //make sure the error message only shows up once - if (!nextreachability->next) AAS_Error("AAS_MAX_REACHABILITYSIZE"); - // - r = nextreachability; - nextreachability = nextreachability->next; - numlreachabilities++; - return r; -} //end of the function AAS_AllocReachability -//=========================================================================== -// frees a reachability link -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeReachability(aas_lreachability_t *lreach) -{ - Com_Memset(lreach, 0, sizeof(aas_lreachability_t)); - - lreach->next = nextreachability; - nextreachability = lreach; - numlreachabilities--; -} //end of the function AAS_FreeReachability -//=========================================================================== -// returns qtrue if the area has reachability links -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaReachability(int areanum) -{ - if (areanum < 0 || areanum >= aasworld.numareas) - { - AAS_Error("AAS_AreaReachability: areanum %d out of range", areanum); - return 0; - } //end if - return aasworld.areasettings[areanum].numreachableareas; -} //end of the function AAS_AreaReachability -//=========================================================================== -// returns the surface area of all ground faces together of the area -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float AAS_AreaGroundFaceArea(int areanum) -{ - int i; - float total; - aas_area_t *area; - aas_face_t *face; - - total = 0; - area = &aasworld.areas[areanum]; - for (i = 0; i < area->numfaces; i++) - { - face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; - if (!(face->faceflags & FACE_GROUND)) continue; - // - total += AAS_FaceArea(face); - } //end for - return total; -} //end of the function AAS_AreaGroundFaceArea -//=========================================================================== -// returns the center of a face -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FaceCenter(int facenum, vec3_t center) -{ - int i; - float scale; - aas_face_t *face; - aas_edge_t *edge; - - face = &aasworld.faces[facenum]; - - VectorClear(center); - for (i = 0; i < face->numedges; i++) - { - edge = &aasworld.edges[abs(aasworld.edgeindex[face->firstedge + i])]; - VectorAdd(center, aasworld.vertexes[edge->v[0]], center); - VectorAdd(center, aasworld.vertexes[edge->v[1]], center); - } //end for - scale = 0.5 / face->numedges; - VectorScale(center, scale, center); -} //end of the function AAS_FaceCenter -//=========================================================================== -// returns the maximum distance a player can fall before being damaged -// damage = deltavelocity*deltavelocity * 0.0001 -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_FallDamageDistance(void) -{ - float maxzvelocity, gravity, t; - - maxzvelocity = sqrt(30 * 10000); - gravity = aassettings.phys_gravity; - t = maxzvelocity / gravity; - return 0.5 * gravity * t * t; -} //end of the function AAS_FallDamageDistance -//=========================================================================== -// distance = 0.5 * gravity * t * t -// vel = t * gravity -// damage = vel * vel * 0.0001 -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float AAS_FallDelta(float distance) -{ - float t, delta, gravity; - - gravity = aassettings.phys_gravity; - t = sqrt(fabs(distance) * 2 / gravity); - delta = t * gravity; - return delta * delta * 0.0001; -} //end of the function AAS_FallDelta -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float AAS_MaxJumpHeight(float phys_jumpvel) -{ - float phys_gravity; - - phys_gravity = aassettings.phys_gravity; - //maximum height a player can jump with the given initial z velocity - return 0.5 * phys_gravity * (phys_jumpvel / phys_gravity) * (phys_jumpvel / phys_gravity); -} //end of the function MaxJumpHeight -//=========================================================================== -// returns true if a player can only crouch in the area -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float AAS_MaxJumpDistance(float phys_jumpvel) -{ - float phys_gravity, phys_maxvelocity, t; - - phys_gravity = aassettings.phys_gravity; - phys_maxvelocity = aassettings.phys_maxvelocity; - //time a player takes to fall the height - t = sqrt(aassettings.rs_maxjumpfallheight / (0.5 * phys_gravity)); - //maximum distance - return phys_maxvelocity * (t + phys_jumpvel / phys_gravity); -} //end of the function AAS_MaxJumpDistance -//=========================================================================== -// returns true if a player can only crouch in the area -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaCrouch(int areanum) -{ - if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qtrue; - else return qfalse; -} //end of the function AAS_AreaCrouch -//=========================================================================== -// returns qtrue if it is possible to swim in the area -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaSwim(int areanum) -{ - if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue; - else return qfalse; -} //end of the function AAS_AreaSwim -//=========================================================================== -// returns qtrue if the area contains a liquid -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaLiquid(int areanum) -{ - if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue; - else return qfalse; -} //end of the function AAS_AreaLiquid -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaLava(int areanum) -{ - return (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA); -} //end of the function AAS_AreaLava -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaSlime(int areanum) -{ - return (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME); -} //end of the function AAS_AreaSlime -//=========================================================================== -// returns qtrue if the area contains ground faces -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaGrounded(int areanum) -{ - return (aasworld.areasettings[areanum].areaflags & AREA_GROUNDED); -} //end of the function AAS_AreaGround -//=========================================================================== -// returns true if the area contains ladder faces -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaLadder(int areanum) -{ - return (aasworld.areasettings[areanum].areaflags & AREA_LADDER); -} //end of the function AAS_AreaLadder -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaJumpPad(int areanum) -{ - return (aasworld.areasettings[areanum].contents & AREACONTENTS_JUMPPAD); -} //end of the function AAS_AreaJumpPad -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaTeleporter(int areanum) -{ - return (aasworld.areasettings[areanum].contents & AREACONTENTS_TELEPORTER); -} //end of the function AAS_AreaTeleporter -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaClusterPortal(int areanum) -{ - return (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL); -} //end of the function AAS_AreaClusterPortal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaDoNotEnter(int areanum) -{ - return (aasworld.areasettings[areanum].contents & AREACONTENTS_DONOTENTER); -} //end of the function AAS_AreaDoNotEnter -//=========================================================================== -// returns the time it takes perform a barrier jump -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -unsigned short int AAS_BarrierJumpTravelTime(void) -{ - return aassettings.phys_jumpvel / (aassettings.phys_gravity * 0.1); -} //end op the function AAS_BarrierJumpTravelTime -//=========================================================================== -// returns true if there already exists a reachability from area1 to area2 -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_ReachabilityExists(int area1num, int area2num) -{ - aas_lreachability_t *r; - - for (r = areareachability[area1num]; r; r = r->next) - { - if (r->areanum == area2num) return qtrue; - } //end for - return qfalse; -} //end of the function AAS_ReachabilityExists -//=========================================================================== -// returns true if there is a solid just after the end point when going -// from start to end -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_NearbySolidOrGap(vec3_t start, vec3_t end) -{ - vec3_t dir, testpoint; - int areanum; - - VectorSubtract(end, start, dir); - dir[2] = 0; - VectorNormalize(dir); - VectorMA(end, 48, dir, testpoint); - - areanum = AAS_PointAreaNum(testpoint); - if (!areanum) - { - testpoint[2] += 16; - areanum = AAS_PointAreaNum(testpoint); - if (!areanum) return qtrue; - } //end if - VectorMA(end, 64, dir, testpoint); - areanum = AAS_PointAreaNum(testpoint); - if (areanum) - { - if (!AAS_AreaSwim(areanum) && !AAS_AreaGrounded(areanum)) return qtrue; - } //end if - return qfalse; -} //end of the function AAS_SolidGapTime -//=========================================================================== -// searches for swim reachabilities between adjacent areas -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_Reachability_Swim(int area1num, int area2num) -{ - int i, j, face1num, face2num, side1; - aas_area_t *area1, *area2; - aas_areasettings_t *areasettings; - aas_lreachability_t *lreach; - aas_face_t *face1; - aas_plane_t *plane; - vec3_t start; - - if (!AAS_AreaSwim(area1num) || !AAS_AreaSwim(area2num)) return qfalse; - //if the second area is crouch only - if (!(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) return qfalse; - - area1 = &aasworld.areas[area1num]; - area2 = &aasworld.areas[area2num]; - - //if the areas are not near anough - for (i = 0; i < 3; i++) - { - if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; - if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; - } //end for - //find a shared face and create a reachability link - for (i = 0; i < area1->numfaces; i++) - { - face1num = aasworld.faceindex[area1->firstface + i]; - side1 = face1num < 0; - face1num = abs(face1num); - // - for (j = 0; j < area2->numfaces; j++) - { - face2num = abs(aasworld.faceindex[area2->firstface + j]); - // - if (face1num == face2num) - { - AAS_FaceCenter(face1num, start); - // - if (AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) - { - // - face1 = &aasworld.faces[face1num]; - areasettings = &aasworld.areasettings[area1num]; - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area2num; - lreach->facenum = face1num; - lreach->edgenum = 0; - VectorCopy(start, lreach->start); - plane = &aasworld.planes[face1->planenum ^ side1]; - VectorMA(lreach->start, -INSIDEUNITS, plane->normal, lreach->end); - lreach->traveltype = TRAVEL_SWIM; - lreach->traveltime = 1; - //if the volume of the area is rather small - if (AAS_AreaVolume(area2num) < 800) - lreach->traveltime += 200; - //if (!(AAS_PointContents(start) & MASK_WATER)) lreach->traveltime += 500; - //link the reachability - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - reach_swim++; - return qtrue; - } //end if - } //end if - } //end for - } //end for - return qfalse; -} //end of the function AAS_Reachability_Swim -//=========================================================================== -// searches for reachabilities between adjacent areas with equal floor -// heights -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_Reachability_EqualFloorHeight(int area1num, int area2num) -{ - int i, j, edgenum, edgenum1, edgenum2, foundreach, side; - float height, bestheight, length, bestlength; - vec3_t dir, start, end, normal, invgravity, gravitydirection = {0, 0, -1}; - vec3_t edgevec; - aas_area_t *area1, *area2; - aas_face_t *face1, *face2; - aas_edge_t *edge; - aas_plane_t *plane2; - aas_lreachability_t lr, *lreach; - - if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse; - - area1 = &aasworld.areas[area1num]; - area2 = &aasworld.areas[area2num]; - //if the areas are not near anough in the x-y direction - for (i = 0; i < 2; i++) - { - if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; - if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; - } //end for - //if area 2 is too high above area 1 - if (area2->mins[2] > area1->maxs[2]) return qfalse; - // - VectorCopy(gravitydirection, invgravity); - VectorInverse(invgravity); - // - bestheight = 99999; - bestlength = 0; - foundreach = qfalse; - Com_Memset(&lr, 0, sizeof(aas_lreachability_t)); //make the compiler happy - // - //check if the areas have ground faces with a common edge - //if existing use the lowest common edge for a reachability link - for (i = 0; i < area1->numfaces; i++) - { - face1 = &aasworld.faces[abs(aasworld.faceindex[area1->firstface + i])]; - if (!(face1->faceflags & FACE_GROUND)) continue; - // - for (j = 0; j < area2->numfaces; j++) - { - face2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])]; - if (!(face2->faceflags & FACE_GROUND)) continue; - //if there is a common edge - for (edgenum1 = 0; edgenum1 < face1->numedges; edgenum1++) - { - for (edgenum2 = 0; edgenum2 < face2->numedges; edgenum2++) - { - if (abs(aasworld.edgeindex[face1->firstedge + edgenum1]) != - abs(aasworld.edgeindex[face2->firstedge + edgenum2])) - continue; - edgenum = aasworld.edgeindex[face1->firstedge + edgenum1]; - side = edgenum < 0; - edge = &aasworld.edges[abs(edgenum)]; - //get the length of the edge - VectorSubtract(aasworld.vertexes[edge->v[1]], - aasworld.vertexes[edge->v[0]], dir); - length = VectorLength(dir); - //get the start point - VectorAdd(aasworld.vertexes[edge->v[0]], - aasworld.vertexes[edge->v[1]], start); - VectorScale(start, 0.5, start); - VectorCopy(start, end); - //get the end point several units inside area2 - //and the start point several units inside area1 - //NOTE: normal is pointing into area2 because the - //face edges are stored counter clockwise - VectorSubtract(aasworld.vertexes[edge->v[side]], - aasworld.vertexes[edge->v[!side]], edgevec); - plane2 = &aasworld.planes[face2->planenum]; - CrossProduct(edgevec, plane2->normal, normal); - VectorNormalize(normal); - // - //VectorMA(start, -1, normal, start); - VectorMA(end, INSIDEUNITS_WALKEND, normal, end); - VectorMA(start, INSIDEUNITS_WALKSTART, normal, start); - end[2] += 0.125; - // - height = DotProduct(invgravity, start); - //NOTE: if there's nearby solid or a gap area after this area - //disabled this crap - //if (AAS_NearbySolidOrGap(start, end)) height += 200; - //NOTE: disabled because it disables reachabilities to very small areas - //if (AAS_PointAreaNum(end) != area2num) continue; - //get the longest lowest edge - if (height < bestheight || - (height < bestheight + 1 && length > bestlength)) - { - bestheight = height; - bestlength = length; - //create a new reachability link - lr.areanum = area2num; - lr.facenum = 0; - lr.edgenum = edgenum; - VectorCopy(start, lr.start); - VectorCopy(end, lr.end); - lr.traveltype = TRAVEL_WALK; - lr.traveltime = 1; - foundreach = qtrue; - } //end if - } //end for - } //end for - } //end for - } //end for - if (foundreach) - { - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = lr.areanum; - lreach->facenum = lr.facenum; - lreach->edgenum = lr.edgenum; - VectorCopy(lr.start, lreach->start); - VectorCopy(lr.end, lreach->end); - lreach->traveltype = lr.traveltype; - lreach->traveltime = lr.traveltime; - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - //if going into a crouch area - if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num)) - { - lreach->traveltime += aassettings.rs_startcrouch; - } //end if - /* - //NOTE: if there's nearby solid or a gap area after this area - if (!AAS_NearbySolidOrGap(lreach->start, lreach->end)) - { - lreach->traveltime += 100; - } //end if - */ - //avoid rather small areas - //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100; - // - reach_equalfloor++; - return qtrue; - } //end if - return qfalse; -} //end of the function AAS_Reachability_EqualFloorHeight -//=========================================================================== -// searches step, barrier, waterjump and walk off ledge reachabilities -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(int area1num, int area2num) -{ - int i, j, k, l, edge1num, edge2num, areas[10], numareas; - int ground_bestarea2groundedgenum, ground_foundreach; - int water_bestarea2groundedgenum, water_foundreach; - int side1, area1swim, faceside1, groundface1num; - float dist, dist1, dist2, diff, invgravitydot, ortdot; - float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y; - float length, ground_bestlength, water_bestlength, ground_bestdist, water_bestdist; - vec3_t v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2; - vec3_t normal, ort, edgevec, start, end, dir; - vec3_t ground_beststart, ground_bestend, ground_bestnormal; - vec3_t water_beststart, water_bestend, water_bestnormal; - vec3_t invgravity = {0, 0, 1}; - vec3_t testpoint; - aas_plane_t *plane; - aas_area_t *area1, *area2; - aas_face_t *groundface1, *groundface2, *ground_bestface1, *water_bestface1; - aas_edge_t *edge1, *edge2; - aas_lreachability_t *lreach; - aas_trace_t trace; - - //must be able to walk or swim in the first area - if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse; - // - if (!AAS_AreaGrounded(area2num) && !AAS_AreaSwim(area2num)) return qfalse; - // - area1 = &aasworld.areas[area1num]; - area2 = &aasworld.areas[area2num]; - //if the first area contains a liquid - area1swim = AAS_AreaSwim(area1num); - //if the areas are not near anough in the x-y direction - for (i = 0; i < 2; i++) - { - if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; - if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; - } //end for - // - ground_foundreach = qfalse; - ground_bestdist = 99999; - ground_bestlength = 0; - ground_bestarea2groundedgenum = 0; - // - water_foundreach = qfalse; - water_bestdist = 99999; - water_bestlength = 0; - water_bestarea2groundedgenum = 0; - // - for (i = 0; i < area1->numfaces; i++) - { - groundface1num = aasworld.faceindex[area1->firstface + i]; - faceside1 = groundface1num < 0; - groundface1 = &aasworld.faces[abs(groundface1num)]; - //if this isn't a ground face - if (!(groundface1->faceflags & FACE_GROUND)) - { - //if we can swim in the first area - if (area1swim) - { - //face plane must be more or less horizontal - plane = &aasworld.planes[groundface1->planenum ^ (!faceside1)]; - if (DotProduct(plane->normal, invgravity) < 0.7) continue; - } //end if - else - { - //if we can't swim in the area it must be a ground face - continue; - } //end else - } //end if - // - for (k = 0; k < groundface1->numedges; k++) - { - edge1num = aasworld.edgeindex[groundface1->firstedge + k]; - side1 = (edge1num < 0); - //NOTE: for water faces we must take the side area 1 is - // on into account because the face is shared and doesn't - // have to be oriented correctly - if (!(groundface1->faceflags & FACE_GROUND)) side1 = (side1 == faceside1); - edge1num = abs(edge1num); - edge1 = &aasworld.edges[edge1num]; - //vertexes of the edge - VectorCopy(aasworld.vertexes[edge1->v[!side1]], v1); - VectorCopy(aasworld.vertexes[edge1->v[side1]], v2); - //get a vertical plane through the edge - //NOTE: normal is pointing into area 2 because the - //face edges are stored counter clockwise - VectorSubtract(v2, v1, edgevec); - CrossProduct(edgevec, invgravity, normal); - VectorNormalize(normal); - dist = DotProduct(normal, v1); - //check the faces from the second area - for (j = 0; j < area2->numfaces; j++) - { - groundface2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])]; - //must be a ground face - if (!(groundface2->faceflags & FACE_GROUND)) continue; - //check the edges of this ground face - for (l = 0; l < groundface2->numedges; l++) - { - edge2num = abs(aasworld.edgeindex[groundface2->firstedge + l]); - edge2 = &aasworld.edges[edge2num]; - //vertexes of the edge - VectorCopy(aasworld.vertexes[edge2->v[0]], v3); - VectorCopy(aasworld.vertexes[edge2->v[1]], v4); - //check the distance between the two points and the vertical plane - //through the edge of area1 - diff = DotProduct(normal, v3) - dist; - if (diff < -0.1 || diff > 0.1) continue; - diff = DotProduct(normal, v4) - dist; - if (diff < -0.1 || diff > 0.1) continue; - // - //project the two ground edges into the step side plane - //and calculate the shortest distance between the two - //edges if they overlap in the direction orthogonal to - //the gravity direction - CrossProduct(invgravity, normal, ort); - invgravitydot = DotProduct(invgravity, invgravity); - ortdot = DotProduct(ort, ort); - //projection into the step plane - //NOTE: since gravity is vertical this is just the z coordinate - y1 = v1[2];//DotProduct(v1, invgravity) / invgravitydot; - y2 = v2[2];//DotProduct(v2, invgravity) / invgravitydot; - y3 = v3[2];//DotProduct(v3, invgravity) / invgravitydot; - y4 = v4[2];//DotProduct(v4, invgravity) / invgravitydot; - // - x1 = DotProduct(v1, ort) / ortdot; - x2 = DotProduct(v2, ort) / ortdot; - x3 = DotProduct(v3, ort) / ortdot; - x4 = DotProduct(v4, ort) / ortdot; - // - if (x1 > x2) - { - tmp = x1; x1 = x2; x2 = tmp; - tmp = y1; y1 = y2; y2 = tmp; - VectorCopy(v1, tmpv); VectorCopy(v2, v1); VectorCopy(tmpv, v2); - } //end if - if (x3 > x4) - { - tmp = x3; x3 = x4; x4 = tmp; - tmp = y3; y3 = y4; y4 = tmp; - VectorCopy(v3, tmpv); VectorCopy(v4, v3); VectorCopy(tmpv, v4); - } //end if - //if the two projected edge lines have no overlap - if (x2 <= x3 || x4 <= x1) - { -// Log_Write("lines no overlap: from area %d to %d\r\n", area1num, area2num); - continue; - } //end if - //if the two lines fully overlap - if ((x1 - 0.5 < x3 && x4 < x2 + 0.5) && - (x3 - 0.5 < x1 && x2 < x4 + 0.5)) - { - dist1 = y3 - y1; - dist2 = y4 - y2; - VectorCopy(v1, p1area1); - VectorCopy(v2, p2area1); - VectorCopy(v3, p1area2); - VectorCopy(v4, p2area2); - } //end if - else - { - //if the points are equal - if (x1 > x3 - 0.1 && x1 < x3 + 0.1) - { - dist1 = y3 - y1; - VectorCopy(v1, p1area1); - VectorCopy(v3, p1area2); - } //end if - else if (x1 < x3) - { - y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1); - dist1 = y3 - y; - VectorCopy(v3, p1area1); - p1area1[2] = y; - VectorCopy(v3, p1area2); - } //end if - else - { - y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3); - dist1 = y - y1; - VectorCopy(v1, p1area1); - VectorCopy(v1, p1area2); - p1area2[2] = y; - } //end if - //if the points are equal - if (x2 > x4 - 0.1 && x2 < x4 + 0.1) - { - dist2 = y4 - y2; - VectorCopy(v2, p2area1); - VectorCopy(v4, p2area2); - } //end if - else if (x2 < x4) - { - y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3); - dist2 = y - y2; - VectorCopy(v2, p2area1); - VectorCopy(v2, p2area2); - p2area2[2] = y; - } //end if - else - { - y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1); - dist2 = y4 - y; - VectorCopy(v4, p2area1); - p2area1[2] = y; - VectorCopy(v4, p2area2); - } //end else - } //end else - //if both distances are pretty much equal - //then we take the middle of the points - if (dist1 > dist2 - 1 && dist1 < dist2 + 1) - { - dist = dist1; - VectorAdd(p1area1, p2area1, start); - VectorScale(start, 0.5, start); - VectorAdd(p1area2, p2area2, end); - VectorScale(end, 0.5, end); - } //end if - else if (dist1 < dist2) - { - dist = dist1; - VectorCopy(p1area1, start); - VectorCopy(p1area2, end); - } //end else if - else - { - dist = dist2; - VectorCopy(p2area1, start); - VectorCopy(p2area2, end); - } //end else - //get the length of the overlapping part of the edges of the two areas - VectorSubtract(p2area2, p1area2, dir); - length = VectorLength(dir); - // - if (groundface1->faceflags & FACE_GROUND) - { - //if the vertical distance is smaller - if (dist < ground_bestdist || - //or the vertical distance is pretty much the same - //but the overlapping part of the edges is longer - (dist < ground_bestdist + 1 && length > ground_bestlength)) - { - ground_bestdist = dist; - ground_bestlength = length; - ground_foundreach = qtrue; - ground_bestarea2groundedgenum = edge1num; - ground_bestface1 = groundface1; - //best point towards area1 - VectorCopy(start, ground_beststart); - //normal is pointing into area2 - VectorCopy(normal, ground_bestnormal); - //best point towards area2 - VectorCopy(end, ground_bestend); - } //end if - } //end if - else - { - //if the vertical distance is smaller - if (dist < water_bestdist || - //or the vertical distance is pretty much the same - //but the overlapping part of the edges is longer - (dist < water_bestdist + 1 && length > water_bestlength)) - { - water_bestdist = dist; - water_bestlength = length; - water_foundreach = qtrue; - water_bestarea2groundedgenum = edge1num; - water_bestface1 = groundface1; - //best point towards area1 - VectorCopy(start, water_beststart); - //normal is pointing into area2 - VectorCopy(normal, water_bestnormal); - //best point towards area2 - VectorCopy(end, water_bestend); - } //end if - } //end else - } //end for - } //end for - } //end for - } //end for - // - // NOTE: swim reachabilities are already filtered out - // - // Steps - // - // --------- - // | step height -> TRAVEL_WALK - //--------| - // - // --------- - //~~~~~~~~| step height and low water -> TRAVEL_WALK - //--------| - // - //~~~~~~~~~~~~~~~~~~ - // --------- - // | step height and low water up to the step -> TRAVEL_WALK - //--------| - // - //check for a step reachability - if (ground_foundreach) - { - //if area2 is higher but lower than the maximum step height - //NOTE: ground_bestdist >= 0 also catches equal floor reachabilities - if (ground_bestdist >= 0 && ground_bestdist < aassettings.phys_maxstep) - { - //create walk reachability from area1 to area2 - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area2num; - lreach->facenum = 0; - lreach->edgenum = ground_bestarea2groundedgenum; - VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); - VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); - lreach->traveltype = TRAVEL_WALK; - lreach->traveltime = 0;//1; - //if going into a crouch area - if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num)) - { - lreach->traveltime += aassettings.rs_startcrouch; - } //end if - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - //NOTE: if there's nearby solid or a gap area after this area - /* - if (!AAS_NearbySolidOrGap(lreach->start, lreach->end)) - { - lreach->traveltime += 100; - } //end if - */ - //avoid rather small areas - //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100; - // - reach_step++; - return qtrue; - } //end if - } //end if - // - // Water Jumps - // - // --------- - // | - //~~~~~~~~| - // | - // | higher than step height and water up to waterjump height -> TRAVEL_WATERJUMP - //--------| - // - //~~~~~~~~~~~~~~~~~~ - // --------- - // | - // | - // | - // | higher than step height and low water up to the step -> TRAVEL_WATERJUMP - //--------| - // - //check for a waterjump reachability - if (water_foundreach) - { - //get a test point a little bit towards area1 - VectorMA(water_bestend, -INSIDEUNITS, water_bestnormal, testpoint); - //go down the maximum waterjump height - testpoint[2] -= aassettings.phys_maxwaterjump; - //if there IS water the sv_maxwaterjump height below the bestend point - if (aasworld.areasettings[AAS_PointAreaNum(testpoint)].areaflags & AREA_LIQUID) - { - //don't create rediculous water jump reachabilities from areas very far below - //the water surface - if (water_bestdist < aassettings.phys_maxwaterjump + 24) - { - //waterjumping from or towards a crouch only area is not possible in Quake2 - if ((aasworld.areasettings[area1num].presencetype & PRESENCE_NORMAL) && - (aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) - { - //create water jump reachability from area1 to area2 - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area2num; - lreach->facenum = 0; - lreach->edgenum = water_bestarea2groundedgenum; - VectorCopy(water_beststart, lreach->start); - VectorMA(water_bestend, INSIDEUNITS_WATERJUMP, water_bestnormal, lreach->end); - lreach->traveltype = TRAVEL_WATERJUMP; - lreach->traveltime = aassettings.rs_waterjump; - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - //we've got another waterjump reachability - reach_waterjump++; - return qtrue; - } //end if - } //end if - } //end if - } //end if - // - // Barrier Jumps - // - // --------- - // | - // | - // | - // | higher than step height lower than barrier height -> TRAVEL_BARRIERJUMP - //--------| - // - // --------- - // | - // | - // | - //~~~~~~~~| higher than step height lower than barrier height - //--------| and a thin layer of water in the area to jump from -> TRAVEL_BARRIERJUMP - // - //check for a barrier jump reachability - if (ground_foundreach) - { - //if area2 is higher but lower than the maximum barrier jump height - if (ground_bestdist > 0 && ground_bestdist < aassettings.phys_maxbarrier) - { - //if no water in area1 or a very thin layer of water on the ground - if (!water_foundreach || (ground_bestdist - water_bestdist < 16)) - { - //cannot perform a barrier jump towards or from a crouch area in Quake2 - if (!AAS_AreaCrouch(area1num) && !AAS_AreaCrouch(area2num)) - { - //create barrier jump reachability from area1 to area2 - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area2num; - lreach->facenum = 0; - lreach->edgenum = ground_bestarea2groundedgenum; - VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); - VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); - lreach->traveltype = TRAVEL_BARRIERJUMP; - lreach->traveltime = aassettings.rs_barrierjump;//AAS_BarrierJumpTravelTime(); - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - //we've got another barrierjump reachability - reach_barrier++; - return qtrue; - } //end if - } //end if - } //end if - } //end if - // - // Walk and Walk Off Ledge - // - //--------| - // | can walk or step back -> TRAVEL_WALK - // --------- - // - //--------| - // | - // | - // | - // | cannot walk/step back -> TRAVEL_WALKOFFLEDGE - // --------- - // - //--------| - // | - // |~~~~~~~~ - // | - // | cannot step back but can waterjump back -> TRAVEL_WALKOFFLEDGE - // --------- FIXME: create TRAVEL_WALK reach?? - // - //check for a walk or walk off ledge reachability - if (ground_foundreach) - { - if (ground_bestdist < 0) - { - if (ground_bestdist > -aassettings.phys_maxstep) - { - //create walk reachability from area1 to area2 - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area2num; - lreach->facenum = 0; - lreach->edgenum = ground_bestarea2groundedgenum; - VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); - VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); - lreach->traveltype = TRAVEL_WALK; - lreach->traveltime = 1; - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - //we've got another walk reachability - reach_walk++; - return qtrue; - } //end if - // if no maximum fall height set or less than the max - if (!aassettings.rs_maxfallheight || fabs(ground_bestdist) < aassettings.rs_maxfallheight) { - //trace a bounding box vertically to check for solids - VectorMA(ground_bestend, INSIDEUNITS, ground_bestnormal, ground_bestend); - VectorCopy(ground_bestend, start); - start[2] = ground_beststart[2]; - VectorCopy(ground_bestend, end); - end[2] += 4; - trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); - //if no solids were found - if (!trace.startsolid && trace.fraction >= 1.0) - { - //the trace end point must be in the goal area - trace.endpos[2] += 1; - if (AAS_PointAreaNum(trace.endpos) == area2num) - { - //if not going through a cluster portal - numareas = AAS_TraceAreas(start, end, areas, NULL, sizeof(areas) / sizeof(int)); - for (i = 0; i < numareas; i++) - if (AAS_AreaClusterPortal(areas[i])) - break; - if (i >= numareas) - { - //create a walk off ledge reachability from area1 to area2 - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area2num; - lreach->facenum = 0; - lreach->edgenum = ground_bestarea2groundedgenum; - VectorCopy(ground_beststart, lreach->start); - VectorCopy(ground_bestend, lreach->end); - lreach->traveltype = TRAVEL_WALKOFFLEDGE; - lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(ground_bestdist) * 50 / aassettings.phys_gravity; - //if falling from too high and not falling into water - if (!AAS_AreaSwim(area2num) && !AAS_AreaJumpPad(area2num)) - { - if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta5) - { - lreach->traveltime += aassettings.rs_falldamage5; - } //end if - if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta10) - { - lreach->traveltime += aassettings.rs_falldamage10; - } //end if - } //end if - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - // - reach_walkoffledge++; - //NOTE: don't create a weapon (rl, bfg) jump reachability here - //because it interferes with other reachabilities - //like the ladder reachability - return qtrue; - } //end if - } //end if - } //end if - } //end if - } //end else - } //end if - return qfalse; -} //end of the function AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge -//=========================================================================== -// returns the distance between the two vectors -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float VectorDistance(vec3_t v1, vec3_t v2) -{ - vec3_t dir; - - VectorSubtract(v2, v1, dir); - return VectorLength(dir); -} //end of the function VectorDistance -//=========================================================================== -// returns true if the first vector is between the last two vectors -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int VectorBetweenVectors(vec3_t v, vec3_t v1, vec3_t v2) -{ - vec3_t dir1, dir2; - - VectorSubtract(v, v1, dir1); - VectorSubtract(v, v2, dir2); - return (DotProduct(dir1, dir2) <= 0); -} //end of the function VectorBetweenVectors -//=========================================================================== -// returns the mid point between the two vectors -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void VectorMiddle(vec3_t v1, vec3_t v2, vec3_t middle) -{ - VectorAdd(v1, v2, middle); - VectorScale(middle, 0.5, middle); -} //end of the function VectorMiddle -//=========================================================================== -// calculate a range of points closest to each other on both edges -// -// Parameter: beststart1 start of the range of points on edge v1-v2 -// beststart2 end of the range of points on edge v1-v2 -// bestend1 start of the range of points on edge v3-v4 -// bestend2 end of the range of points on edge v3-v4 -// bestdist best distance so far -// Returns: - -// Changes Globals: - -//=========================================================================== -/* -float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4, - aas_plane_t *plane1, aas_plane_t *plane2, - vec3_t beststart, vec3_t bestend, float bestdist) -{ - vec3_t dir1, dir2, p1, p2, p3, p4; - float a1, a2, b1, b2, dist; - int founddist; - - //edge vectors - VectorSubtract(v2, v1, dir1); - VectorSubtract(v4, v3, dir2); - //get the horizontal directions - dir1[2] = 0; - dir2[2] = 0; - // - // p1 = point on an edge vector of area2 closest to v1 - // p2 = point on an edge vector of area2 closest to v2 - // p3 = point on an edge vector of area1 closest to v3 - // p4 = point on an edge vector of area1 closest to v4 - // - if (dir2[0]) - { - a2 = dir2[1] / dir2[0]; - b2 = v3[1] - a2 * v3[0]; - //point on the edge vector of area2 closest to v1 - p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; - p1[1] = a2 * p1[0] + b2; - //point on the edge vector of area2 closest to v2 - p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; - p2[1] = a2 * p2[0] + b2; - } //end if - else - { - //point on the edge vector of area2 closest to v1 - p1[0] = v3[0]; - p1[1] = v1[1]; - //point on the edge vector of area2 closest to v2 - p2[0] = v3[0]; - p2[1] = v2[1]; - } //end else - // - if (dir1[0]) - { - // - a1 = dir1[1] / dir1[0]; - b1 = v1[1] - a1 * v1[0]; - //point on the edge vector of area1 closest to v3 - p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; - p3[1] = a1 * p3[0] + b1; - //point on the edge vector of area1 closest to v4 - p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; - p4[1] = a1 * p4[0] + b1; - } //end if - else - { - //point on the edge vector of area1 closest to v3 - p3[0] = v1[0]; - p3[1] = v3[1]; - //point on the edge vector of area1 closest to v4 - p4[0] = v1[0]; - p4[1] = v4[1]; - } //end else - //start with zero z-coordinates - p1[2] = 0; - p2[2] = 0; - p3[2] = 0; - p4[2] = 0; - //calculate the z-coordinates from the ground planes - p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2]; - p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2]; - p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2]; - p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2]; - // - founddist = qfalse; - // - if (VectorBetweenVectors(p1, v3, v4)) - { - dist = VectorDistance(v1, p1); - if (dist > bestdist - 0.5 && dist < bestdist + 0.5) - { - VectorMiddle(beststart, v1, beststart); - VectorMiddle(bestend, p1, bestend); - } //end if - else if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v1, beststart); - VectorCopy(p1, bestend); - } //end if - founddist = qtrue; - } //end if - if (VectorBetweenVectors(p2, v3, v4)) - { - dist = VectorDistance(v2, p2); - if (dist > bestdist - 0.5 && dist < bestdist + 0.5) - { - VectorMiddle(beststart, v2, beststart); - VectorMiddle(bestend, p2, bestend); - } //end if - else if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v2, beststart); - VectorCopy(p2, bestend); - } //end if - founddist = qtrue; - } //end else if - if (VectorBetweenVectors(p3, v1, v2)) - { - dist = VectorDistance(v3, p3); - if (dist > bestdist - 0.5 && dist < bestdist + 0.5) - { - VectorMiddle(beststart, p3, beststart); - VectorMiddle(bestend, v3, bestend); - } //end if - else if (dist < bestdist) - { - bestdist = dist; - VectorCopy(p3, beststart); - VectorCopy(v3, bestend); - } //end if - founddist = qtrue; - } //end else if - if (VectorBetweenVectors(p4, v1, v2)) - { - dist = VectorDistance(v4, p4); - if (dist > bestdist - 0.5 && dist < bestdist + 0.5) - { - VectorMiddle(beststart, p4, beststart); - VectorMiddle(bestend, v4, bestend); - } //end if - else if (dist < bestdist) - { - bestdist = dist; - VectorCopy(p4, beststart); - VectorCopy(v4, bestend); - } //end if - founddist = qtrue; - } //end else if - //if no shortest distance was found the shortest distance - //is between one of the vertexes of edge1 and one of edge2 - if (!founddist) - { - dist = VectorDistance(v1, v3); - if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v1, beststart); - VectorCopy(v3, bestend); - } //end if - dist = VectorDistance(v1, v4); - if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v1, beststart); - VectorCopy(v4, bestend); - } //end if - dist = VectorDistance(v2, v3); - if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v2, beststart); - VectorCopy(v3, bestend); - } //end if - dist = VectorDistance(v2, v4); - if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v2, beststart); - VectorCopy(v4, bestend); - } //end if - } //end if - return bestdist; -} //end of the function AAS_ClosestEdgePoints*/ - -float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4, - aas_plane_t *plane1, aas_plane_t *plane2, - vec3_t beststart1, vec3_t bestend1, - vec3_t beststart2, vec3_t bestend2, float bestdist) -{ - vec3_t dir1, dir2, p1, p2, p3, p4; - float a1, a2, b1, b2, dist, dist1, dist2; - int founddist; - - //edge vectors - VectorSubtract(v2, v1, dir1); - VectorSubtract(v4, v3, dir2); - //get the horizontal directions - dir1[2] = 0; - dir2[2] = 0; - // - // p1 = point on an edge vector of area2 closest to v1 - // p2 = point on an edge vector of area2 closest to v2 - // p3 = point on an edge vector of area1 closest to v3 - // p4 = point on an edge vector of area1 closest to v4 - // - if (dir2[0]) - { - a2 = dir2[1] / dir2[0]; - b2 = v3[1] - a2 * v3[0]; - //point on the edge vector of area2 closest to v1 - p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; - p1[1] = a2 * p1[0] + b2; - //point on the edge vector of area2 closest to v2 - p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; - p2[1] = a2 * p2[0] + b2; - } //end if - else - { - //point on the edge vector of area2 closest to v1 - p1[0] = v3[0]; - p1[1] = v1[1]; - //point on the edge vector of area2 closest to v2 - p2[0] = v3[0]; - p2[1] = v2[1]; - } //end else - // - if (dir1[0]) - { - // - a1 = dir1[1] / dir1[0]; - b1 = v1[1] - a1 * v1[0]; - //point on the edge vector of area1 closest to v3 - p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; - p3[1] = a1 * p3[0] + b1; - //point on the edge vector of area1 closest to v4 - p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; - p4[1] = a1 * p4[0] + b1; - } //end if - else - { - //point on the edge vector of area1 closest to v3 - p3[0] = v1[0]; - p3[1] = v3[1]; - //point on the edge vector of area1 closest to v4 - p4[0] = v1[0]; - p4[1] = v4[1]; - } //end else - //start with zero z-coordinates - p1[2] = 0; - p2[2] = 0; - p3[2] = 0; - p4[2] = 0; - //calculate the z-coordinates from the ground planes - p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2]; - p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2]; - p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2]; - p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2]; - // - founddist = qfalse; - // - if (VectorBetweenVectors(p1, v3, v4)) - { - dist = VectorDistance(v1, p1); - if (dist > bestdist - 0.5 && dist < bestdist + 0.5) - { - dist1 = VectorDistance(beststart1, v1); - dist2 = VectorDistance(beststart2, v1); - if (dist1 > dist2) - { - if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart2); - } //end if - else - { - if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart1); - } //end else - dist1 = VectorDistance(bestend1, p1); - dist2 = VectorDistance(bestend2, p1); - if (dist1 > dist2) - { - if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend2); - } //end if - else - { - if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend1); - } //end else - } //end if - else if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v1, beststart1); - VectorCopy(v1, beststart2); - VectorCopy(p1, bestend1); - VectorCopy(p1, bestend2); - } //end if - founddist = qtrue; - } //end if - if (VectorBetweenVectors(p2, v3, v4)) - { - dist = VectorDistance(v2, p2); - if (dist > bestdist - 0.5 && dist < bestdist + 0.5) - { - dist1 = VectorDistance(beststart1, v2); - dist2 = VectorDistance(beststart2, v2); - if (dist1 > dist2) - { - if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart2); - } //end if - else - { - if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart1); - } //end else - dist1 = VectorDistance(bestend1, p2); - dist2 = VectorDistance(bestend2, p2); - if (dist1 > dist2) - { - if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend2); - } //end if - else - { - if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend1); - } //end else - } //end if - else if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v2, beststart1); - VectorCopy(v2, beststart2); - VectorCopy(p2, bestend1); - VectorCopy(p2, bestend2); - } //end if - founddist = qtrue; - } //end else if - if (VectorBetweenVectors(p3, v1, v2)) - { - dist = VectorDistance(v3, p3); - if (dist > bestdist - 0.5 && dist < bestdist + 0.5) - { - dist1 = VectorDistance(beststart1, p3); - dist2 = VectorDistance(beststart2, p3); - if (dist1 > dist2) - { - if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart2); - } //end if - else - { - if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart1); - } //end else - dist1 = VectorDistance(bestend1, v3); - dist2 = VectorDistance(bestend2, v3); - if (dist1 > dist2) - { - if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend2); - } //end if - else - { - if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend1); - } //end else - } //end if - else if (dist < bestdist) - { - bestdist = dist; - VectorCopy(p3, beststart1); - VectorCopy(p3, beststart2); - VectorCopy(v3, bestend1); - VectorCopy(v3, bestend2); - } //end if - founddist = qtrue; - } //end else if - if (VectorBetweenVectors(p4, v1, v2)) - { - dist = VectorDistance(v4, p4); - if (dist > bestdist - 0.5 && dist < bestdist + 0.5) - { - dist1 = VectorDistance(beststart1, p4); - dist2 = VectorDistance(beststart2, p4); - if (dist1 > dist2) - { - if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart2); - } //end if - else - { - if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart1); - } //end else - dist1 = VectorDistance(bestend1, v4); - dist2 = VectorDistance(bestend2, v4); - if (dist1 > dist2) - { - if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend2); - } //end if - else - { - if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend1); - } //end else - } //end if - else if (dist < bestdist) - { - bestdist = dist; - VectorCopy(p4, beststart1); - VectorCopy(p4, beststart2); - VectorCopy(v4, bestend1); - VectorCopy(v4, bestend2); - } //end if - founddist = qtrue; - } //end else if - //if no shortest distance was found the shortest distance - //is between one of the vertexes of edge1 and one of edge2 - if (!founddist) - { - dist = VectorDistance(v1, v3); - if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v1, beststart1); - VectorCopy(v1, beststart2); - VectorCopy(v3, bestend1); - VectorCopy(v3, bestend2); - } //end if - dist = VectorDistance(v1, v4); - if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v1, beststart1); - VectorCopy(v1, beststart2); - VectorCopy(v4, bestend1); - VectorCopy(v4, bestend2); - } //end if - dist = VectorDistance(v2, v3); - if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v2, beststart1); - VectorCopy(v2, beststart2); - VectorCopy(v3, bestend1); - VectorCopy(v3, bestend2); - } //end if - dist = VectorDistance(v2, v4); - if (dist < bestdist) - { - bestdist = dist; - VectorCopy(v2, beststart1); - VectorCopy(v2, beststart2); - VectorCopy(v4, bestend1); - VectorCopy(v4, bestend2); - } //end if - } //end if - return bestdist; -} //end of the function AAS_ClosestEdgePoints -//=========================================================================== -// creates possible jump reachabilities between the areas -// -// The two closest points on the ground of the areas are calculated -// One of the points will be on an edge of a ground face of area1 and -// one on an edge of a ground face of area2. -// If there is a range of closest points the point in the middle of this range -// is selected. -// Between these two points there must be one or more gaps. -// If the gaps exist a potential jump is predicted. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_Reachability_Jump(int area1num, int area2num) -{ - int i, j, k, l, face1num, face2num, edge1num, edge2num, traveltype; - int stopevent, areas[10], numareas; - float phys_jumpvel, maxjumpdistance, maxjumpheight, height, bestdist, speed; - vec_t *v1, *v2, *v3, *v4; - vec3_t beststart, beststart2, bestend, bestend2; - vec3_t teststart, testend, dir, velocity, cmdmove, up = {0, 0, 1}, sidewards; - aas_area_t *area1, *area2; - aas_face_t *face1, *face2; - aas_edge_t *edge1, *edge2; - aas_plane_t *plane1, *plane2, *plane; - aas_trace_t trace; - aas_clientmove_t move; - aas_lreachability_t *lreach; - - if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse; - //cannot jump from or to a crouch area - if (AAS_AreaCrouch(area1num) || AAS_AreaCrouch(area2num)) return qfalse; - // - area1 = &aasworld.areas[area1num]; - area2 = &aasworld.areas[area2num]; - // - phys_jumpvel = aassettings.phys_jumpvel; - //maximum distance a player can jump - maxjumpdistance = 2 * AAS_MaxJumpDistance(phys_jumpvel); - //maximum height a player can jump with the given initial z velocity - maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel); - - //if the areas are not near anough in the x-y direction - for (i = 0; i < 2; i++) - { - if (area1->mins[i] > area2->maxs[i] + maxjumpdistance) return qfalse; - if (area1->maxs[i] < area2->mins[i] - maxjumpdistance) return qfalse; - } //end for - //if area2 is way to high to jump up to - if (area2->mins[2] > area1->maxs[2] + maxjumpheight) return qfalse; - // - bestdist = 999999; - // - for (i = 0; i < area1->numfaces; i++) - { - face1num = aasworld.faceindex[area1->firstface + i]; - face1 = &aasworld.faces[abs(face1num)]; - //if not a ground face - if (!(face1->faceflags & FACE_GROUND)) continue; - // - for (j = 0; j < area2->numfaces; j++) - { - face2num = aasworld.faceindex[area2->firstface + j]; - face2 = &aasworld.faces[abs(face2num)]; - //if not a ground face - if (!(face2->faceflags & FACE_GROUND)) continue; - // - for (k = 0; k < face1->numedges; k++) - { - edge1num = abs(aasworld.edgeindex[face1->firstedge + k]); - edge1 = &aasworld.edges[edge1num]; - for (l = 0; l < face2->numedges; l++) - { - edge2num = abs(aasworld.edgeindex[face2->firstedge + l]); - edge2 = &aasworld.edges[edge2num]; - //calculate the minimum distance between the two edges - v1 = aasworld.vertexes[edge1->v[0]]; - v2 = aasworld.vertexes[edge1->v[1]]; - v3 = aasworld.vertexes[edge2->v[0]]; - v4 = aasworld.vertexes[edge2->v[1]]; - //get the ground planes - plane1 = &aasworld.planes[face1->planenum]; - plane2 = &aasworld.planes[face2->planenum]; - // - bestdist = AAS_ClosestEdgePoints(v1, v2, v3, v4, plane1, plane2, - beststart, bestend, - beststart2, bestend2, bestdist); - } //end for - } //end for - } //end for - } //end for - VectorMiddle(beststart, beststart2, beststart); - VectorMiddle(bestend, bestend2, bestend); - if (bestdist > 4 && bestdist < maxjumpdistance) - { -// Log_Write("shortest distance between %d and %d is %f\r\n", area1num, area2num, bestdist); - // if very close and almost no height difference then the bot can walk - if (bestdist <= 48 && fabs(beststart[2] - bestend[2]) < 8) - { - speed = 400; - traveltype = TRAVEL_WALKOFFLEDGE; - } //end if - else if (AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) - { - //FIXME: why multiply with 1.2??? - speed *= 1.2f; - traveltype = TRAVEL_WALKOFFLEDGE; - } //end else if - else - { - //get the horizontal speed for the jump, if it isn't possible to calculate this - //speed (the jump is not possible) then there's no jump reachability created - if (!AAS_HorizontalVelocityForJump(phys_jumpvel, beststart, bestend, &speed)) - return qfalse; - speed *= 1.05f; - traveltype = TRAVEL_JUMP; - // - //NOTE: test if the horizontal distance isn't too small - VectorSubtract(bestend, beststart, dir); - dir[2] = 0; - if (VectorLength(dir) < 10) - return qfalse; - } //end if - // - VectorSubtract(bestend, beststart, dir); - VectorNormalize(dir); - VectorMA(beststart, 1, dir, teststart); - // - VectorCopy(teststart, testend); - testend[2] -= 100; - trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1); - // - if (trace.startsolid) - return qfalse; - if (trace.fraction < 1) - { - plane = &aasworld.planes[trace.planenum]; - // if the bot can stand on the surface - if (DotProduct(plane->normal, up) >= 0.7) - { - // if no lava or slime below - if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME))) - { - if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier) - return qfalse; - } //end if - } //end if - } //end if - // - VectorMA(bestend, -1, dir, teststart); - // - VectorCopy(teststart, testend); - testend[2] -= 100; - trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1); - // - if (trace.startsolid) - return qfalse; - if (trace.fraction < 1) - { - plane = &aasworld.planes[trace.planenum]; - // if the bot can stand on the surface - if (DotProduct(plane->normal, up) >= 0.7) - { - // if no lava or slime below - if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME))) - { - if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier) - return qfalse; - } //end if - } //end if - } //end if - // - // get command movement - VectorClear(cmdmove); - if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) - cmdmove[2] = aassettings.phys_jumpvel; - else - cmdmove[2] = 0; - // - VectorSubtract(bestend, beststart, dir); - dir[2] = 0; - VectorNormalize(dir); - CrossProduct(dir, up, sidewards); - // - stopevent = SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE; - if (!AAS_AreaClusterPortal(area1num) && !AAS_AreaClusterPortal(area2num)) - stopevent |= SE_TOUCHCLUSTERPORTAL; - // - for (i = 0; i < 3; i++) - { - // - if (i == 1) - VectorAdd(testend, sidewards, testend); - else if (i == 2) - VectorSubtract(bestend, sidewards, testend); - else - VectorCopy(bestend, testend); - VectorSubtract(testend, beststart, dir); - dir[2] = 0; - VectorNormalize(dir); - VectorScale(dir, speed, velocity); - // - AAS_PredictClientMovement(&move, -1, beststart, PRESENCE_NORMAL, qtrue, - velocity, cmdmove, 3, 30, 0.1f, - stopevent, 0, qfalse); - // if prediction time wasn't enough to fully predict the movement - if (move.frames >= 30) - return qfalse; - // don't enter slime or lava and don't fall from too high - if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA)) - return qfalse; - // never jump or fall through a cluster portal - if (move.stopevent & SE_TOUCHCLUSTERPORTAL) - return qfalse; - //the end position should be in area2, also test a little bit back - //because the predicted jump could have rushed through the area - VectorMA(move.endpos, -64, dir, teststart); - teststart[2] += 1; - numareas = AAS_TraceAreas(move.endpos, teststart, areas, NULL, sizeof(areas) / sizeof(int)); - for (j = 0; j < numareas; j++) - { - if (areas[j] == area2num) - break; - } //end for - if (j < numareas) - break; - } - if (i >= 3) - return qfalse; - // -#ifdef REACH_DEBUG - //create the reachability - Log_Write("jump reachability between %d and %d\r\n", area1num, area2num); -#endif //REACH_DEBUG - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area2num; - lreach->facenum = 0; - lreach->edgenum = 0; - VectorCopy(beststart, lreach->start); - VectorCopy(bestend, lreach->end); - lreach->traveltype = traveltype; - - VectorSubtract(bestend, beststart, dir); - height = dir[2]; - dir[2] = 0; - if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE && height > VectorLength(dir)) - { - lreach->traveltime = aassettings.rs_startwalkoffledge + height * 50 / aassettings.phys_gravity; - } - else - { - lreach->traveltime = aassettings.rs_startjump + VectorDistance(bestend, beststart) * 240 / aassettings.phys_maxwalkvelocity; - } //end if - // - if (!AAS_AreaJumpPad(area2num)) - { - if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta5) - { - lreach->traveltime += aassettings.rs_falldamage5; - } //end if - else if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta10) - { - lreach->traveltime += aassettings.rs_falldamage10; - } //end if - } //end if - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - // - if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) - reach_jump++; - else - reach_walkoffledge++; - } //end if - return qfalse; -} //end of the function AAS_Reachability_Jump -//=========================================================================== -// create a possible ladder reachability from area1 to area2 -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_Reachability_Ladder(int area1num, int area2num) -{ - int i, j, k, l, edge1num, edge2num, sharededgenum, lowestedgenum; - int face1num, face2num, ladderface1num, ladderface2num; - int ladderface1vertical, ladderface2vertical, firstv; - float face1area, face2area, bestface1area, bestface2area; - float phys_jumpvel, maxjumpheight; - vec3_t area1point, area2point, v1, v2, up = {0, 0, 1}; - vec3_t mid, lowestpoint, start, end, sharededgevec, dir; - aas_area_t *area1, *area2; - aas_face_t *face1, *face2, *ladderface1, *ladderface2; - aas_plane_t *plane1, *plane2; - aas_edge_t *sharededge, *edge1; - aas_lreachability_t *lreach; - aas_trace_t trace; - - if (!AAS_AreaLadder(area1num) || !AAS_AreaLadder(area2num)) return qfalse; - // - phys_jumpvel = aassettings.phys_jumpvel; - //maximum height a player can jump with the given initial z velocity - maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel); - - area1 = &aasworld.areas[area1num]; - area2 = &aasworld.areas[area2num]; - // - ladderface1 = NULL; - ladderface2 = NULL; - ladderface1num = 0; //make compiler happy - ladderface2num = 0; //make compiler happy - bestface1area = -9999; - bestface2area = -9999; - sharededgenum = 0; //make compiler happy - lowestedgenum = 0; //make compiler happy - // - for (i = 0; i < area1->numfaces; i++) - { - face1num = aasworld.faceindex[area1->firstface + i]; - face1 = &aasworld.faces[abs(face1num)]; - //if not a ladder face - if (!(face1->faceflags & FACE_LADDER)) continue; - // - for (j = 0; j < area2->numfaces; j++) - { - face2num = aasworld.faceindex[area2->firstface + j]; - face2 = &aasworld.faces[abs(face2num)]; - //if not a ladder face - if (!(face2->faceflags & FACE_LADDER)) continue; - //check if the faces share an edge - for (k = 0; k < face1->numedges; k++) - { - edge1num = aasworld.edgeindex[face1->firstedge + k]; - for (l = 0; l < face2->numedges; l++) - { - edge2num = aasworld.edgeindex[face2->firstedge + l]; - if (abs(edge1num) == abs(edge2num)) - { - //get the face with the largest area - face1area = AAS_FaceArea(face1); - face2area = AAS_FaceArea(face2); - if (face1area > bestface1area && face2area > bestface2area) - { - bestface1area = face1area; - bestface2area = face2area; - ladderface1 = face1; - ladderface2 = face2; - ladderface1num = face1num; - ladderface2num = face2num; - sharededgenum = edge1num; - } //end if - break; - } //end if - } //end for - if (l != face2->numedges) break; - } //end for - } //end for - } //end for - // - if (ladderface1 && ladderface2) - { - //get the middle of the shared edge - sharededge = &aasworld.edges[abs(sharededgenum)]; - firstv = sharededgenum < 0; - // - VectorCopy(aasworld.vertexes[sharededge->v[firstv]], v1); - VectorCopy(aasworld.vertexes[sharededge->v[!firstv]], v2); - VectorAdd(v1, v2, area1point); - VectorScale(area1point, 0.5, area1point); - VectorCopy(area1point, area2point); - // - //if the face plane in area 1 is pretty much vertical - plane1 = &aasworld.planes[ladderface1->planenum ^ (ladderface1num < 0)]; - plane2 = &aasworld.planes[ladderface2->planenum ^ (ladderface2num < 0)]; - // - //get the points really into the areas - VectorSubtract(v2, v1, sharededgevec); - CrossProduct(plane1->normal, sharededgevec, dir); - VectorNormalize(dir); - //NOTE: 32 because that's larger than 16 (bot bbox x,y) - VectorMA(area1point, -32, dir, area1point); - VectorMA(area2point, 32, dir, area2point); - // - ladderface1vertical = abs(DotProduct(plane1->normal, up)) < 0.1; - ladderface2vertical = abs(DotProduct(plane2->normal, up)) < 0.1; - //there's only reachability between vertical ladder faces - if (!ladderface1vertical && !ladderface2vertical) return qfalse; - //if both vertical ladder faces - if (ladderface1vertical && ladderface2vertical - //and the ladder faces do not make a sharp corner - && DotProduct(plane1->normal, plane2->normal) > 0.7 - //and the shared edge is not too vertical - && abs(DotProduct(sharededgevec, up)) < 0.7) - { - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area2num; - lreach->facenum = ladderface1num; - lreach->edgenum = abs(sharededgenum); - VectorCopy(area1point, lreach->start); - //VectorCopy(area2point, lreach->end); - VectorMA(area2point, -3, plane1->normal, lreach->end); - lreach->traveltype = TRAVEL_LADDER; - lreach->traveltime = 10; - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - // - reach_ladder++; - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area1num; - lreach->facenum = ladderface2num; - lreach->edgenum = abs(sharededgenum); - VectorCopy(area2point, lreach->start); - //VectorCopy(area1point, lreach->end); - VectorMA(area1point, -3, plane1->normal, lreach->end); - lreach->traveltype = TRAVEL_LADDER; - lreach->traveltime = 10; - lreach->next = areareachability[area2num]; - areareachability[area2num] = lreach; - // - reach_ladder++; - // - return qtrue; - } //end if - //if the second ladder face is also a ground face - //create ladder end (just ladder) reachability and - //walk off a ladder (ledge) reachability - if (ladderface1vertical && (ladderface2->faceflags & FACE_GROUND)) - { - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area2num; - lreach->facenum = ladderface1num; - lreach->edgenum = abs(sharededgenum); - VectorCopy(area1point, lreach->start); - VectorCopy(area2point, lreach->end); - lreach->end[2] += 16; - VectorMA(lreach->end, -15, plane1->normal, lreach->end); - lreach->traveltype = TRAVEL_LADDER; - lreach->traveltime = 10; - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - // - reach_ladder++; - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area1num; - lreach->facenum = ladderface2num; - lreach->edgenum = abs(sharededgenum); - VectorCopy(area2point, lreach->start); - VectorCopy(area1point, lreach->end); - lreach->traveltype = TRAVEL_WALKOFFLEDGE; - lreach->traveltime = 10; - lreach->next = areareachability[area2num]; - areareachability[area2num] = lreach; - // - reach_walkoffledge++; - // - return qtrue; - } //end if - // - if (ladderface1vertical) - { - //find lowest edge of the ladder face - lowestpoint[2] = 99999; - for (i = 0; i < ladderface1->numedges; i++) - { - edge1num = abs(aasworld.edgeindex[ladderface1->firstedge + i]); - edge1 = &aasworld.edges[edge1num]; - // - VectorCopy(aasworld.vertexes[edge1->v[0]], v1); - VectorCopy(aasworld.vertexes[edge1->v[1]], v2); - // - VectorAdd(v1, v2, mid); - VectorScale(mid, 0.5, mid); - // - if (mid[2] < lowestpoint[2]) - { - VectorCopy(mid, lowestpoint); - lowestedgenum = edge1num; - } //end if - } //end for - // - plane1 = &aasworld.planes[ladderface1->planenum]; - //trace down in the middle of this edge - VectorMA(lowestpoint, 5, plane1->normal, start); - VectorCopy(start, end); - start[2] += 5; - end[2] -= 100; - //trace without entity collision - trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); - // - // -#ifdef REACH_DEBUG - if (trace.startsolid) - { - Log_Write("trace from area %d started in solid\r\n", area1num); - } //end if -#endif //REACH_DEBUG - // - trace.endpos[2] += 1; - area2num = AAS_PointAreaNum(trace.endpos); - // - area2 = &aasworld.areas[area2num]; - for (i = 0; i < area2->numfaces; i++) - { - face2num = aasworld.faceindex[area2->firstface + i]; - face2 = &aasworld.faces[abs(face2num)]; - // - if (face2->faceflags & FACE_LADDER) - { - plane2 = &aasworld.planes[face2->planenum]; - if (abs(DotProduct(plane2->normal, up)) < 0.1) break; - } //end if - } //end for - //if from another area without vertical ladder faces - if (i >= area2->numfaces && area2num != area1num && - //the reachabilities shouldn't exist already - !AAS_ReachabilityExists(area1num, area2num) && - !AAS_ReachabilityExists(area2num, area1num)) - { - //if the height is jumpable - if (start[2] - trace.endpos[2] < maxjumpheight) - { - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area2num; - lreach->facenum = ladderface1num; - lreach->edgenum = lowestedgenum; - VectorCopy(lowestpoint, lreach->start); - VectorCopy(trace.endpos, lreach->end); - lreach->traveltype = TRAVEL_LADDER; - lreach->traveltime = 10; - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - // - reach_ladder++; - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area1num; - lreach->facenum = ladderface1num; - lreach->edgenum = lowestedgenum; - VectorCopy(trace.endpos, lreach->start); - //get the end point a little bit into the ladder - VectorMA(lowestpoint, -5, plane1->normal, lreach->end); - //get the end point a little higher - lreach->end[2] += 10; - lreach->traveltype = TRAVEL_JUMP; - lreach->traveltime = 10; - lreach->next = areareachability[area2num]; - areareachability[area2num] = lreach; - // - reach_jump++; - // - return qtrue; -#ifdef REACH_DEBUG - Log_Write("jump up to ladder reach between %d and %d\r\n", area2num, area1num); -#endif //REACH_DEBUG - } //end if -#ifdef REACH_DEBUG - else Log_Write("jump too high between area %d and %d\r\n", area2num, area1num); -#endif //REACH_DEBUG - } //end if - /*//if slime or lava below the ladder - //try jump reachability from far towards the ladder - if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME - | AREACONTENTS_LAVA)) - { - for (i = 20; i <= 120; i += 20) - { - //trace down in the middle of this edge - VectorMA(lowestpoint, i, plane1->normal, start); - VectorCopy(start, end); - start[2] += 5; - end[2] -= 100; - //trace without entity collision - trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); - // - if (trace.startsolid) break; - trace.endpos[2] += 1; - area2num = AAS_PointAreaNum(trace.endpos); - if (area2num == area1num) continue; - // - if (start[2] - trace.endpos[2] > maxjumpheight) continue; - if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME - | AREACONTENTS_LAVA)) continue; - // - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area1num; - lreach->facenum = ladderface1num; - lreach->edgenum = lowestedgenum; - VectorCopy(trace.endpos, lreach->start); - VectorCopy(lowestpoint, lreach->end); - lreach->end[2] += 5; - lreach->traveltype = TRAVEL_JUMP; - lreach->traveltime = 10; - lreach->next = areareachability[area2num]; - areareachability[area2num] = lreach; - // - reach_jump++; - // - Log_Write("jump far to ladder reach between %d and %d\r\n", area2num, area1num); - // - break; - } //end for - } //end if*/ - } //end if - } //end if - return qfalse; -} //end of the function AAS_Reachability_Ladder -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_TravelFlagsForTeam(int ent) -{ - int notteam; - - if (!AAS_IntForBSPEpairKey(ent, "bot_notteam", ¬team)) - return 0; - if (notteam == 1) - return TRAVELFLAG_NOTTEAM1; - if (notteam == 2) - return TRAVELFLAG_NOTTEAM2; - return 0; -} //end of the function AAS_TravelFlagsForTeam -//=========================================================================== -// create possible teleporter reachabilities -// this is very game dependent.... :( -// -// classname = trigger_multiple or trigger_teleport -// target = "t1" -// -// classname = target_teleporter -// targetname = "t1" -// target = "t2" -// -// classname = misc_teleporter_dest -// targetname = "t2" -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_Reachability_Teleport(void) -{ - int area1num, area2num; - char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY]; - char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; - int ent, dest; - float angle; - vec3_t origin, destorigin, mins, maxs, end, angles; - vec3_t mid, velocity, cmdmove; - aas_lreachability_t *lreach; - aas_clientmove_t move; - aas_trace_t trace; - aas_link_t *areas, *link; - - for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) - { - if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; - if (!strcmp(classname, "trigger_multiple")) - { - AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); -//#ifdef REACH_DEBUG - botimport.Print(PRT_MESSAGE, "trigger_multiple model = \"%s\"\n", model); -//#endif - VectorClear(angles); - AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin); - // - if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY)) - { - botimport.Print(PRT_ERROR, "trigger_multiple at %1.0f %1.0f %1.0f without target\n", - origin[0], origin[1], origin[2]); - continue; - } //end if - for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest)) - { - if (!AAS_ValueForBSPEpairKey(dest, "classname", classname, MAX_EPAIRKEY)) continue; - if (!strcmp(classname, "target_teleporter")) - { - if (!AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) continue; - if (!strcmp(targetname, target)) - { - break; - } //end if - } //end if - } //end for - if (!dest) - { - continue; - } //end if - if (!AAS_ValueForBSPEpairKey(dest, "target", target, MAX_EPAIRKEY)) - { - botimport.Print(PRT_ERROR, "target_teleporter without target\n"); - continue; - } //end if - } //end else - else if (!strcmp(classname, "trigger_teleport")) - { - AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); -//#ifdef REACH_DEBUG - botimport.Print(PRT_MESSAGE, "trigger_teleport model = \"%s\"\n", model); -//#endif - VectorClear(angles); - AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin); - // - if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY)) - { - botimport.Print(PRT_ERROR, "trigger_teleport at %1.0f %1.0f %1.0f without target\n", - origin[0], origin[1], origin[2]); - continue; - } //end if - } //end if - else - { - continue; - } //end else - // - for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest)) - { - //classname should be misc_teleporter_dest - //but I've also seen target_position and actually any - //entity could be used... burp - if (AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) - { - if (!strcmp(targetname, target)) - { - break; - } //end if - } //end if - } //end for - if (!dest) - { - botimport.Print(PRT_ERROR, "teleporter without misc_teleporter_dest (%s)\n", target); - continue; - } //end if - if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin)) - { - botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target); - continue; - } //end if - // - area2num = AAS_PointAreaNum(destorigin); - //if not teleported into a teleporter or into a jumppad - if (!AAS_AreaTeleporter(area2num) && !AAS_AreaJumpPad(area2num)) - { - VectorCopy(destorigin, end); - end[2] -= 64; - trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1); - if (trace.startsolid) - { - botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target); - continue; - } //end if - area2num = AAS_PointAreaNum(trace.endpos); - // - /* - if (!AAS_AreaTeleporter(area2num) && - !AAS_AreaJumpPad(area2num) && - !AAS_AreaGrounded(area2num)) - { - VectorCopy(trace.endpos, destorigin); - } - else*/ - { - //predict where you'll end up - AAS_FloatForBSPEpairKey(dest, "angle", &angle); - if (angle) - { - VectorSet(angles, 0, angle, 0); - AngleVectors(angles, velocity, NULL, NULL); - VectorScale(velocity, 400, velocity); - } //end if - else - { - VectorClear(velocity); - } //end else - VectorClear(cmdmove); - AAS_PredictClientMovement(&move, -1, destorigin, PRESENCE_NORMAL, qfalse, - velocity, cmdmove, 0, 30, 0.1f, - SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| - SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, qfalse); //qtrue); - area2num = AAS_PointAreaNum(move.endpos); - if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA)) - { - botimport.Print(PRT_WARNING, "teleported into slime or lava at dest %s\n", target); - } //end if - VectorCopy(move.endpos, destorigin); - } //end else - } //end if - // - //botimport.Print(PRT_MESSAGE, "teleporter brush origin at %f %f %f\n", origin[0], origin[1], origin[2]); - //botimport.Print(PRT_MESSAGE, "teleporter brush mins = %f %f %f\n", mins[0], mins[1], mins[2]); - //botimport.Print(PRT_MESSAGE, "teleporter brush maxs = %f %f %f\n", maxs[0], maxs[1], maxs[2]); - VectorAdd(origin, mins, mins); - VectorAdd(origin, maxs, maxs); - // - VectorAdd(mins, maxs, mid); - VectorScale(mid, 0.5, mid); - //link an invalid (-1) entity - areas = AAS_LinkEntityClientBBox(mins, maxs, -1, PRESENCE_CROUCH); - if (!areas) botimport.Print(PRT_MESSAGE, "trigger_multiple not in any area\n"); - // - for (link = areas; link; link = link->next_area) - { - //if (!AAS_AreaGrounded(link->areanum)) continue; - if (!AAS_AreaTeleporter(link->areanum)) continue; - // - area1num = link->areanum; - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) break; - lreach->areanum = area2num; - lreach->facenum = 0; - lreach->edgenum = 0; - VectorCopy(mid, lreach->start); - VectorCopy(destorigin, lreach->end); - lreach->traveltype = TRAVEL_TELEPORT; - lreach->traveltype |= AAS_TravelFlagsForTeam(ent); - lreach->traveltime = aassettings.rs_teleport; - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - // - reach_teleport++; - } //end for - //unlink the invalid entity - AAS_UnlinkFromAreas(areas); - } //end for -} //end of the function AAS_Reachability_Teleport -//=========================================================================== -// create possible elevator (func_plat) reachabilities -// this is very game dependent.... :( -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_Reachability_Elevator(void) -{ - int area1num, area2num, modelnum, i, j, k, l, n, p; - float lip, height, speed; - char model[MAX_EPAIRKEY], classname[MAX_EPAIRKEY]; - int ent; - vec3_t mins, maxs, origin, angles = {0, 0, 0}; - vec3_t pos1, pos2, mids, platbottom, plattop; - vec3_t bottomorg, toporg, start, end, dir; - vec_t xvals[8], yvals[8], xvals_top[8], yvals_top[8]; - aas_lreachability_t *lreach; - aas_trace_t trace; - -#ifdef REACH_DEBUG - Log_Write("AAS_Reachability_Elevator\r\n"); -#endif //REACH_DEBUG - for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) - { - if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; - if (!strcmp(classname, "func_plat")) - { -#ifdef REACH_DEBUG - Log_Write("found func plat\r\n"); -#endif //REACH_DEBUG - if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) - { - botimport.Print(PRT_ERROR, "func_plat without model\n"); - continue; - } //end if - //get the model number, and skip the leading * - modelnum = atoi(model+1); - if (modelnum <= 0) - { - botimport.Print(PRT_ERROR, "func_plat with invalid model number\n"); - continue; - } //end if - //get the mins, maxs and origin of the model - //NOTE: the origin is usually (0,0,0) and the mins and maxs - // are the absolute mins and maxs - AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); - // - AAS_VectorForBSPEpairKey(ent, "origin", origin); - //pos1 is the top position, pos2 is the bottom - VectorCopy(origin, pos1); - VectorCopy(origin, pos2); - //get the lip of the plat - AAS_FloatForBSPEpairKey(ent, "lip", &lip); - if (!lip) lip = 8; - //get the movement height of the plat - AAS_FloatForBSPEpairKey(ent, "height", &height); - if (!height) height = (maxs[2] - mins[2]) - lip; - //get the speed of the plat - AAS_FloatForBSPEpairKey(ent, "speed", &speed); - if (!speed) speed = 200; - //get bottom position below pos1 - pos2[2] -= height; - // - //get a point just above the plat in the bottom position - VectorAdd(mins, maxs, mids); - VectorMA(pos2, 0.5, mids, platbottom); - platbottom[2] = maxs[2] - (pos1[2] - pos2[2]) + 2; - //get a point just above the plat in the top position - VectorAdd(mins, maxs, mids); - VectorMA(pos2, 0.5, mids, plattop); - plattop[2] = maxs[2] + 2; - // - /*if (!area1num) - { - Log_Write("no grounded area near plat bottom\r\n"); - continue; - } //end if*/ - //get the mins and maxs a little larger - for (i = 0; i < 3; i++) - { - mins[i] -= 1; - maxs[i] += 1; - } //end for - // - //botimport.Print(PRT_MESSAGE, "platbottom[2] = %1.1f plattop[2] = %1.1f\n", platbottom[2], plattop[2]); - // - VectorAdd(mins, maxs, mids); - VectorScale(mids, 0.5, mids); - // - xvals[0] = mins[0]; xvals[1] = mids[0]; xvals[2] = maxs[0]; xvals[3] = mids[0]; - yvals[0] = mids[1]; yvals[1] = maxs[1]; yvals[2] = mids[1]; yvals[3] = mins[1]; - // - xvals[4] = mins[0]; xvals[5] = maxs[0]; xvals[6] = maxs[0]; xvals[7] = mins[0]; - yvals[4] = maxs[1]; yvals[5] = maxs[1]; yvals[6] = mins[1]; yvals[7] = mins[1]; - //find adjacent areas around the bottom of the plat - for (i = 0; i < 9; i++) - { - if (i < 8) //check at the sides of the plat - { - bottomorg[0] = origin[0] + xvals[i]; - bottomorg[1] = origin[1] + yvals[i]; - bottomorg[2] = platbottom[2] + 16; - //get a grounded or swim area near the plat in the bottom position - area1num = AAS_PointAreaNum(bottomorg); - for (k = 0; k < 16; k++) - { - if (area1num) - { - if (AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) break; - } //end if - bottomorg[2] += 4; - area1num = AAS_PointAreaNum(bottomorg); - } //end if - //if in solid - if (k >= 16) - { - continue; - } //end if - } //end if - else //at the middle of the plat - { - VectorCopy(plattop, bottomorg); - bottomorg[2] += 24; - area1num = AAS_PointAreaNum(bottomorg); - if (!area1num) continue; - VectorCopy(platbottom, bottomorg); - bottomorg[2] += 24; - } //end else - //look at adjacent areas around the top of the plat - //make larger steps to outside the plat everytime - for (n = 0; n < 3; n++) - { - for (k = 0; k < 3; k++) - { - mins[k] -= 4; - maxs[k] += 4; - } //end for - xvals_top[0] = mins[0]; xvals_top[1] = mids[0]; xvals_top[2] = maxs[0]; xvals_top[3] = mids[0]; - yvals_top[0] = mids[1]; yvals_top[1] = maxs[1]; yvals_top[2] = mids[1]; yvals_top[3] = mins[1]; - // - xvals_top[4] = mins[0]; xvals_top[5] = maxs[0]; xvals_top[6] = maxs[0]; xvals_top[7] = mins[0]; - yvals_top[4] = maxs[1]; yvals_top[5] = maxs[1]; yvals_top[6] = mins[1]; yvals_top[7] = mins[1]; - // - for (j = 0; j < 8; j++) - { - toporg[0] = origin[0] + xvals_top[j]; - toporg[1] = origin[1] + yvals_top[j]; - toporg[2] = plattop[2] + 16; - //get a grounded or swim area near the plat in the top position - area2num = AAS_PointAreaNum(toporg); - for (l = 0; l < 16; l++) - { - if (area2num) - { - if (AAS_AreaGrounded(area2num) || AAS_AreaSwim(area2num)) - { - VectorCopy(plattop, start); - start[2] += 32; - VectorCopy(toporg, end); - end[2] += 1; - trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); - if (trace.fraction >= 1) break; - } //end if - } //end if - toporg[2] += 4; - area2num = AAS_PointAreaNum(toporg); - } //end if - //if in solid - if (l >= 16) continue; - //never create a reachability in the same area - if (area2num == area1num) continue; - //if the area isn't grounded - if (!AAS_AreaGrounded(area2num)) continue; - //if there already exists reachability between the areas - if (AAS_ReachabilityExists(area1num, area2num)) continue; - //if the reachability start is within the elevator bounding box - VectorSubtract(bottomorg, platbottom, dir); - VectorNormalize(dir); - dir[0] = bottomorg[0] + 24 * dir[0]; - dir[1] = bottomorg[1] + 24 * dir[1]; - dir[2] = bottomorg[2]; - // - for (p = 0; p < 3; p++) - if (dir[p] < origin[p] + mins[p] || dir[p] > origin[p] + maxs[p]) break; - if (p >= 3) continue; - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) continue; - lreach->areanum = area2num; - //the facenum is the model number - lreach->facenum = modelnum; - //the edgenum is the height - lreach->edgenum = (int) height; - // - VectorCopy(dir, lreach->start); - VectorCopy(toporg, lreach->end); - lreach->traveltype = TRAVEL_ELEVATOR; - lreach->traveltype |= AAS_TravelFlagsForTeam(ent); - lreach->traveltime = aassettings.rs_startelevator + height * 100 / speed; - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - //don't go any further to the outside - n = 9999; - // -#ifdef REACH_DEBUG - Log_Write("elevator reach from %d to %d\r\n", area1num, area2num); -#endif //REACH_DEBUG - // - reach_elevator++; - } //end for - } //end for - } //end for - } //end if - } //end for -} //end of the function AAS_Reachability_Elevator -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_lreachability_t *AAS_FindFaceReachabilities(vec3_t *facepoints, int numpoints, aas_plane_t *plane, int towardsface) -{ - int i, j, k, l; - int facenum, edgenum, bestfacenum; - float *v1, *v2, *v3, *v4; - float bestdist, speed, hordist, dist; - vec3_t beststart, beststart2, bestend, bestend2, tmp, hordir, testpoint; - aas_lreachability_t *lreach, *lreachabilities; - aas_area_t *area; - aas_face_t *face; - aas_edge_t *edge; - aas_plane_t *faceplane, *bestfaceplane; - - // - lreachabilities = NULL; - bestfacenum = 0; - bestfaceplane = NULL; - // - for (i = 1; i < aasworld.numareas; i++) - { - area = &aasworld.areas[i]; - // get the shortest distance between one of the func_bob start edges and - // one of the face edges of area1 - bestdist = 999999; - for (j = 0; j < area->numfaces; j++) - { - facenum = aasworld.faceindex[area->firstface + j]; - face = &aasworld.faces[abs(facenum)]; - //if not a ground face - if (!(face->faceflags & FACE_GROUND)) continue; - //get the ground planes - faceplane = &aasworld.planes[face->planenum]; - // - for (k = 0; k < face->numedges; k++) - { - edgenum = abs(aasworld.edgeindex[face->firstedge + k]); - edge = &aasworld.edges[edgenum]; - //calculate the minimum distance between the two edges - v1 = aasworld.vertexes[edge->v[0]]; - v2 = aasworld.vertexes[edge->v[1]]; - // - for (l = 0; l < numpoints; l++) - { - v3 = facepoints[l]; - v4 = facepoints[(l+1) % numpoints]; - dist = AAS_ClosestEdgePoints(v1, v2, v3, v4, faceplane, plane, - beststart, bestend, - beststart2, bestend2, bestdist); - if (dist < bestdist) - { - bestfacenum = facenum; - bestfaceplane = faceplane; - bestdist = dist; - } //end if - } //end for - } //end for - } //end for - // - if (bestdist > 192) continue; - // - VectorMiddle(beststart, beststart2, beststart); - VectorMiddle(bestend, bestend2, bestend); - // - if (!towardsface) - { - VectorCopy(beststart, tmp); - VectorCopy(bestend, beststart); - VectorCopy(tmp, bestend); - } //end if - // - VectorSubtract(bestend, beststart, hordir); - hordir[2] = 0; - hordist = VectorLength(hordir); - // - if (hordist > 2 * AAS_MaxJumpDistance(aassettings.phys_jumpvel)) continue; - //the end point should not be significantly higher than the start point - if (bestend[2] - 32 > beststart[2]) continue; - //don't fall down too far - if (bestend[2] < beststart[2] - 128) continue; - //the distance should not be too far - if (hordist > 32) - { - //check for walk off ledge - if (!AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) continue; - } //end if - // - beststart[2] += 1; - bestend[2] += 1; - // - if (towardsface) VectorCopy(bestend, testpoint); - else VectorCopy(beststart, testpoint); - testpoint[2] = 0; - testpoint[2] = (bestfaceplane->dist - DotProduct(bestfaceplane->normal, testpoint)) / bestfaceplane->normal[2]; - // - if (!AAS_PointInsideFace(bestfacenum, testpoint, 0.1f)) - { - //if the faces are not overlapping then only go down - if (bestend[2] - 16 > beststart[2]) continue; - } //end if - lreach = AAS_AllocReachability(); - if (!lreach) return lreachabilities; - lreach->areanum = i; - lreach->facenum = 0; - lreach->edgenum = 0; - VectorCopy(beststart, lreach->start); - VectorCopy(bestend, lreach->end); - lreach->traveltype = 0; - lreach->traveltime = 0; - lreach->next = lreachabilities; - lreachabilities = lreach; -#ifndef BSPC - if (towardsface) AAS_PermanentLine(lreach->start, lreach->end, 1); - else AAS_PermanentLine(lreach->start, lreach->end, 2); -#endif - } //end for - return lreachabilities; -} //end of the function AAS_FindFaceReachabilities -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_Reachability_FuncBobbing(void) -{ - int ent, spawnflags, modelnum, axis; - int i, numareas, areas[10]; - char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; - vec3_t origin, move_end, move_start, move_start_top, move_end_top; - vec3_t mins, maxs, angles = {0, 0, 0}; - vec3_t start_edgeverts[4], end_edgeverts[4], mid; - vec3_t org, start, end, dir, points[10]; - float height; - aas_plane_t start_plane, end_plane; - aas_lreachability_t *startreach, *endreach, *nextstartreach, *nextendreach, *lreach; - aas_lreachability_t *firststartreach, *firstendreach; - - for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) - { - if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; - if (strcmp(classname, "func_bobbing")) continue; - AAS_FloatForBSPEpairKey(ent, "height", &height); - if (!height) height = 32; - // - if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) - { - botimport.Print(PRT_ERROR, "func_bobbing without model\n"); - continue; - } //end if - //get the model number, and skip the leading * - modelnum = atoi(model+1); - if (modelnum <= 0) - { - botimport.Print(PRT_ERROR, "func_bobbing with invalid model number\n"); - continue; - } //end if - //if the entity has an origin set then use it - if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) - VectorSet(origin, 0, 0, 0); - // - AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); - // - VectorAdd(mins, origin, mins); - VectorAdd(maxs, origin, maxs); - // - VectorAdd(mins, maxs, mid); - VectorScale(mid, 0.5, mid); - VectorCopy(mid, origin); - // - VectorCopy(origin, move_end); - VectorCopy(origin, move_start); - // - AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); - // set the axis of bobbing - if (spawnflags & 1) axis = 0; - else if (spawnflags & 2) axis = 1; - else axis = 2; - // - move_start[axis] -= height; - move_end[axis] += height; - // - Log_Write("funcbob model %d, start = {%1.1f, %1.1f, %1.1f} end = {%1.1f, %1.1f, %1.1f}\n", - modelnum, move_start[0], move_start[1], move_start[2], move_end[0], move_end[1], move_end[2]); - // -#ifndef BSPC - /* - AAS_DrawPermanentCross(move_start, 4, 1); - AAS_DrawPermanentCross(move_end, 4, 2); - */ -#endif - // - for (i = 0; i < 4; i++) - { - VectorCopy(move_start, start_edgeverts[i]); - start_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z - start_edgeverts[i][2] += 24; //+ player origin to ground dist - } //end for - start_edgeverts[0][0] += maxs[0] - mid[0]; - start_edgeverts[0][1] += maxs[1] - mid[1]; - start_edgeverts[1][0] += maxs[0] - mid[0]; - start_edgeverts[1][1] += mins[1] - mid[1]; - start_edgeverts[2][0] += mins[0] - mid[0]; - start_edgeverts[2][1] += mins[1] - mid[1]; - start_edgeverts[3][0] += mins[0] - mid[0]; - start_edgeverts[3][1] += maxs[1] - mid[1]; - // - start_plane.dist = start_edgeverts[0][2]; - VectorSet(start_plane.normal, 0, 0, 1); - // - for (i = 0; i < 4; i++) - { - VectorCopy(move_end, end_edgeverts[i]); - end_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z - end_edgeverts[i][2] += 24; //+ player origin to ground dist - } //end for - end_edgeverts[0][0] += maxs[0] - mid[0]; - end_edgeverts[0][1] += maxs[1] - mid[1]; - end_edgeverts[1][0] += maxs[0] - mid[0]; - end_edgeverts[1][1] += mins[1] - mid[1]; - end_edgeverts[2][0] += mins[0] - mid[0]; - end_edgeverts[2][1] += mins[1] - mid[1]; - end_edgeverts[3][0] += mins[0] - mid[0]; - end_edgeverts[3][1] += maxs[1] - mid[1]; - // - end_plane.dist = end_edgeverts[0][2]; - VectorSet(end_plane.normal, 0, 0, 1); - // -#ifndef BSPC -#if 0 - for (i = 0; i < 4; i++) - { - AAS_PermanentLine(start_edgeverts[i], start_edgeverts[(i+1)%4], 1); - AAS_PermanentLine(end_edgeverts[i], end_edgeverts[(i+1)%4], 1); - } //end for -#endif -#endif - VectorCopy(move_start, move_start_top); - move_start_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z - VectorCopy(move_end, move_end_top); - move_end_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z - // - if (!AAS_PointAreaNum(move_start_top)) continue; - if (!AAS_PointAreaNum(move_end_top)) continue; - // - for (i = 0; i < 2; i++) - { - firststartreach = firstendreach = NULL; - // - if (i == 0) - { - firststartreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qtrue); - firstendreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qfalse); - } //end if - else - { - firststartreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qtrue); - firstendreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qfalse); - } //end else - // - //create reachabilities from start to end - for (startreach = firststartreach; startreach; startreach = nextstartreach) - { - nextstartreach = startreach->next; - // - //trace = AAS_TraceClientBBox(startreach->start, move_start_top, PRESENCE_NORMAL, -1); - //if (trace.fraction < 1) continue; - // - for (endreach = firstendreach; endreach; endreach = nextendreach) - { - nextendreach = endreach->next; - // - //trace = AAS_TraceClientBBox(endreach->end, move_end_top, PRESENCE_NORMAL, -1); - //if (trace.fraction < 1) continue; - // - Log_Write("funcbob reach from area %d to %d\n", startreach->areanum, endreach->areanum); - // - // - if (i == 0) VectorCopy(move_start_top, org); - else VectorCopy(move_end_top, org); - VectorSubtract(startreach->start, org, dir); - dir[2] = 0; - VectorNormalize(dir); - VectorCopy(startreach->start, start); - VectorMA(startreach->start, 1, dir, start); - start[2] += 1; - VectorMA(startreach->start, 16, dir, end); - end[2] += 1; - // - numareas = AAS_TraceAreas(start, end, areas, points, 10); - if (numareas <= 0) continue; - if (numareas > 1) VectorCopy(points[1], startreach->start); - else VectorCopy(end, startreach->start); - // - if (!AAS_PointAreaNum(startreach->start)) continue; - if (!AAS_PointAreaNum(endreach->end)) continue; - // - lreach = AAS_AllocReachability(); - lreach->areanum = endreach->areanum; - if (i == 0) lreach->edgenum = ((int)move_start[axis] << 16) | ((int) move_end[axis] & 0x0000ffff); - else lreach->edgenum = ((int)move_end[axis] << 16) | ((int) move_start[axis] & 0x0000ffff); - lreach->facenum = (spawnflags << 16) | modelnum; - VectorCopy(startreach->start, lreach->start); - VectorCopy(endreach->end, lreach->end); -#ifndef BSPC -// AAS_DrawArrow(lreach->start, lreach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW); -// AAS_PermanentLine(lreach->start, lreach->end, 1); -#endif - lreach->traveltype = TRAVEL_FUNCBOB; - lreach->traveltype |= AAS_TravelFlagsForTeam(ent); - lreach->traveltime = aassettings.rs_funcbob; - reach_funcbob++; - lreach->next = areareachability[startreach->areanum]; - areareachability[startreach->areanum] = lreach; - // - } //end for - } //end for - for (startreach = firststartreach; startreach; startreach = nextstartreach) - { - nextstartreach = startreach->next; - AAS_FreeReachability(startreach); - } //end for - for (endreach = firstendreach; endreach; endreach = nextendreach) - { - nextendreach = endreach->next; - AAS_FreeReachability(endreach); - } //end for - //only go up with func_bobbing entities that go up and down - if (!(spawnflags & 1) && !(spawnflags & 2)) break; - } //end for - } //end for -} //end of the function AAS_Reachability_FuncBobbing -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_Reachability_JumpPad(void) -{ - int face2num, i, ret, area2num, visualize, ent, bot_visualizejumppads; - //int modelnum, ent2; - //float dist, time, height, gravity, forward; - float speed, zvel, hordist; - aas_face_t *face2; - aas_area_t *area2; - aas_lreachability_t *lreach; - vec3_t areastart, facecenter, dir, cmdmove; - vec3_t velocity, absmins, absmaxs; - //vec3_t origin, ent2origin, angles, teststart; - aas_clientmove_t move; - //aas_trace_t trace; - aas_link_t *areas, *link; - //char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; - char classname[MAX_EPAIRKEY]; - -#ifdef BSPC - bot_visualizejumppads = 0; -#else - bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0"); -#endif - for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) - { - if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; - if (strcmp(classname, "trigger_push")) continue; - // - if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue; - /* - // - AAS_FloatForBSPEpairKey(ent, "speed", &speed); - if (!speed) speed = 1000; -// AAS_VectorForBSPEpairKey(ent, "angles", angles); -// AAS_SetMovedir(angles, velocity); -// VectorScale(velocity, speed, velocity); - VectorClear(angles); - //get the mins, maxs and origin of the model - AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); - if (model[0]) modelnum = atoi(model+1); - else modelnum = 0; - AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin); - VectorAdd(origin, absmins, absmins); - VectorAdd(origin, absmaxs, absmaxs); - // -#ifdef REACH_DEBUG - botimport.Print(PRT_MESSAGE, "absmins = %f %f %f\n", absmins[0], absmins[1], absmins[2]); - botimport.Print(PRT_MESSAGE, "absmaxs = %f %f %f\n", absmaxs[0], absmaxs[1], absmaxs[2]); -#endif - VectorAdd(absmins, absmaxs, origin); - VectorScale (origin, 0.5, origin); - - //get the start areas - VectorCopy(origin, teststart); - teststart[2] += 64; - trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1); - if (trace.startsolid) - { - botimport.Print(PRT_MESSAGE, "trigger_push start solid\n"); - VectorCopy(origin, areastart); - } //end if - else - { - VectorCopy(trace.endpos, areastart); - } //end else - areastart[2] += 0.125; - // - //AAS_DrawPermanentCross(origin, 4, 4); - //get the target entity - AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY); - for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2)) - { - if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue; - if (!strcmp(targetname, target)) break; - } //end for - if (!ent2) - { - botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target); - continue; - } //end if - AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin); - // - height = ent2origin[2] - origin[2]; - gravity = aassettings.sv_gravity; - time = sqrt( height / ( 0.5 * gravity ) ); - if (!time) - { - botimport.Print(PRT_MESSAGE, "trigger_push without time\n"); - continue; - } //end if - // set s.origin2 to the push velocity - VectorSubtract ( ent2origin, origin, velocity); - dist = VectorNormalize( velocity); - forward = dist / time; - //FIXME: why multiply by 1.1 - forward *= 1.1; - VectorScale(velocity, forward, velocity); - velocity[2] = time * gravity; - */ - //get the areas the jump pad brush is in - areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); - /* - for (link = areas; link; link = link->next_area) - { - if (link->areanum == 563) - { - ret = qfalse; - } - } - */ - for (link = areas; link; link = link->next_area) - { - if (AAS_AreaJumpPad(link->areanum)) break; - } //end for - if (!link) - { - botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n"); - AAS_UnlinkFromAreas(areas); - continue; - } //end if - // - botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]); - //if there is a horizontal velocity check for a reachability without air control - if (velocity[0] || velocity[1]) - { - VectorSet(cmdmove, 0, 0, 0); - //VectorCopy(velocity, cmdmove); - //cmdmove[2] = 0; - Com_Memset(&move, 0, sizeof(aas_clientmove_t)); - area2num = 0; - for (i = 0; i < 20; i++) - { - AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse, - velocity, cmdmove, 0, 30, 0.1f, - SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| - SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, bot_visualizejumppads); - area2num = move.endarea; - for (link = areas; link; link = link->next_area) - { - if (!AAS_AreaJumpPad(link->areanum)) continue; - if (link->areanum == area2num) break; - } //end if - if (!link) break; - VectorCopy(move.endpos, areastart); - VectorCopy(move.velocity, velocity); - } //end for - if (area2num && i < 20) - { - for (link = areas; link; link = link->next_area) - { - if (!AAS_AreaJumpPad(link->areanum)) continue; - if (AAS_ReachabilityExists(link->areanum, area2num)) continue; - //create a rocket or bfg jump reachability from area1 to area2 - lreach = AAS_AllocReachability(); - if (!lreach) - { - AAS_UnlinkFromAreas(areas); - return; - } //end if - lreach->areanum = area2num; - //NOTE: the facenum is the Z velocity - lreach->facenum = velocity[2]; - //NOTE: the edgenum is the horizontal velocity - lreach->edgenum = sqrt(velocity[0] * velocity[0] + velocity[1] * velocity[1]); - VectorCopy(areastart, lreach->start); - VectorCopy(move.endpos, lreach->end); - lreach->traveltype = TRAVEL_JUMPPAD; - lreach->traveltype |= AAS_TravelFlagsForTeam(ent); - lreach->traveltime = aassettings.rs_jumppad; - lreach->next = areareachability[link->areanum]; - areareachability[link->areanum] = lreach; - // - reach_jumppad++; - } //end for - } //end if - } //end if - // - if (fabs(velocity[0]) > 100 || fabs(velocity[1]) > 100) continue; - //check for areas we can reach with air control - for (area2num = 1; area2num < aasworld.numareas; area2num++) - { - visualize = qfalse; - /* - if (area2num == 3568) - { - for (link = areas; link; link = link->next_area) - { - if (link->areanum == 3380) - { - visualize = qtrue; - botimport.Print(PRT_MESSAGE, "bah\n"); - } //end if - } //end for - } //end if*/ - //never try to go back to one of the original jumppad areas - //and don't create reachabilities if they already exist - for (link = areas; link; link = link->next_area) - { - if (AAS_ReachabilityExists(link->areanum, area2num)) break; - if (AAS_AreaJumpPad(link->areanum)) - { - if (link->areanum == area2num) break; - } //end if - } //end if - if (link) continue; - // - area2 = &aasworld.areas[area2num]; - for (i = 0; i < area2->numfaces; i++) - { - face2num = aasworld.faceindex[area2->firstface + i]; - face2 = &aasworld.faces[abs(face2num)]; - //if it is not a ground face - if (!(face2->faceflags & FACE_GROUND)) continue; - //get the center of the face - AAS_FaceCenter(face2num, facecenter); - //only go higher up - if (facecenter[2] < areastart[2]) continue; - //get the jumppad jump z velocity - zvel = velocity[2]; - //get the horizontal speed for the jump, if it isn't possible to calculate this - //speed - ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed); - if (ret && speed < 150) - { - //direction towards the face center - VectorSubtract(facecenter, areastart, dir); - dir[2] = 0; - hordist = VectorNormalize(dir); - //if (hordist < 1.6 * facecenter[2] - areastart[2]) - { - //get command movement - VectorScale(dir, speed, cmdmove); - // - AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse, - velocity, cmdmove, 30, 30, 0.1f, - SE_ENTERWATER|SE_ENTERSLIME| - SE_ENTERLAVA|SE_HITGROUNDDAMAGE| - SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_HITGROUNDAREA, area2num, visualize); - //if prediction time wasn't enough to fully predict the movement - //don't enter slime or lava and don't fall from too high - if (move.frames < 30 && - !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) - && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER))) - { - //never go back to the same jumppad - for (link = areas; link; link = link->next_area) - { - if (link->areanum == move.endarea) break; - } - if (!link) - { - for (link = areas; link; link = link->next_area) - { - if (!AAS_AreaJumpPad(link->areanum)) continue; - if (AAS_ReachabilityExists(link->areanum, area2num)) continue; - //create a jumppad reachability from area1 to area2 - lreach = AAS_AllocReachability(); - if (!lreach) - { - AAS_UnlinkFromAreas(areas); - return; - } //end if - lreach->areanum = move.endarea; - //NOTE: the facenum is the Z velocity - lreach->facenum = velocity[2]; - //NOTE: the edgenum is the horizontal velocity - lreach->edgenum = sqrt(cmdmove[0] * cmdmove[0] + cmdmove[1] * cmdmove[1]); - VectorCopy(areastart, lreach->start); - VectorCopy(facecenter, lreach->end); - lreach->traveltype = TRAVEL_JUMPPAD; - lreach->traveltype |= AAS_TravelFlagsForTeam(ent); - lreach->traveltime = aassettings.rs_aircontrolledjumppad; - lreach->next = areareachability[link->areanum]; - areareachability[link->areanum] = lreach; - // - reach_jumppad++; - } //end for - } - } //end if - } //end if - } //end for - } //end for - } //end for - AAS_UnlinkFromAreas(areas); - } //end for -} //end of the function AAS_Reachability_JumpPad -//=========================================================================== -// never point at ground faces -// always a higher and pretty far area -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_Reachability_Grapple(int area1num, int area2num) -{ - int face2num, i, j, areanum, numareas, areas[20]; - float mingrappleangle, z, hordist; - bsp_trace_t bsptrace; - aas_trace_t trace; - aas_face_t *face2; - aas_area_t *area1, *area2; - aas_lreachability_t *lreach; - vec3_t areastart, facecenter, start, end, dir, down = {0, 0, -1}; - vec_t *v; - - //only grapple when on the ground or swimming - if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse; - //don't grapple from a crouch area - if (!(AAS_AreaPresenceType(area1num) & PRESENCE_NORMAL)) return qfalse; - //NOTE: disabled area swim it doesn't work right - if (AAS_AreaSwim(area1num)) return qfalse; - // - area1 = &aasworld.areas[area1num]; - area2 = &aasworld.areas[area2num]; - //don't grapple towards way lower areas - if (area2->maxs[2] < area1->mins[2]) return qfalse; - // - VectorCopy(aasworld.areas[area1num].center, start); - //if not a swim area - if (!AAS_AreaSwim(area1num)) - { - if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num, - start[0], start[1], start[2]); - VectorCopy(start, end); - end[2] -= 1000; - trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); - if (trace.startsolid) return qfalse; - VectorCopy(trace.endpos, areastart); - } //end if - else - { - if (!(AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return qfalse; - } //end else - // - //start is now the start point - // - for (i = 0; i < area2->numfaces; i++) - { - face2num = aasworld.faceindex[area2->firstface + i]; - face2 = &aasworld.faces[abs(face2num)]; - //if it is not a solid face - if (!(face2->faceflags & FACE_SOLID)) continue; - //direction towards the first vertex of the face - v = aasworld.vertexes[aasworld.edges[abs(aasworld.edgeindex[face2->firstedge])].v[0]]; - VectorSubtract(v, areastart, dir); - //if the face plane is facing away - if (DotProduct(aasworld.planes[face2->planenum].normal, dir) > 0) continue; - //get the center of the face - AAS_FaceCenter(face2num, facecenter); - //only go higher up with the grapple - if (facecenter[2] < areastart[2] + 64) continue; - //only use vertical faces or downward facing faces - if (DotProduct(aasworld.planes[face2->planenum].normal, down) < 0) continue; - //direction towards the face center - VectorSubtract(facecenter, areastart, dir); - // - z = dir[2]; - dir[2] = 0; - hordist = VectorLength(dir); - if (!hordist) continue; - //if too far - if (hordist > 2000) continue; - //check the minimal angle of the movement - mingrappleangle = 15; //15 degrees - if (z / hordist < tan(2 * M_PI * mingrappleangle / 360)) continue; - // - VectorCopy(facecenter, start); - VectorMA(facecenter, -500, aasworld.planes[face2->planenum].normal, end); - // - bsptrace = AAS_Trace(start, NULL, NULL, end, 0, CONTENTS_SOLID); - //the grapple won't stick to the sky and the grapple point should be near the AAS wall - if ((bsptrace.surface.flags & SURF_SKY) || (bsptrace.fraction * 500 > 32)) continue; - //trace a full bounding box from the area center on the ground to - //the center of the face - VectorSubtract(facecenter, areastart, dir); - VectorNormalize(dir); - VectorMA(areastart, 4, dir, start); - VectorCopy(bsptrace.endpos, end); - trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); - VectorSubtract(trace.endpos, facecenter, dir); - if (VectorLength(dir) > 24) continue; - // - VectorCopy(trace.endpos, start); - VectorCopy(trace.endpos, end); - end[2] -= AAS_FallDamageDistance(); - trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); - if (trace.fraction >= 1) continue; - //area to end in - areanum = AAS_PointAreaNum(trace.endpos); - //if not in lava or slime - if (aasworld.areasettings[areanum].contents & (AREACONTENTS_SLIME|AREACONTENTS_LAVA)) - { - continue; - } //end if - //do not go the the source area - if (areanum == area1num) continue; - //don't create reachabilities if they already exist - if (AAS_ReachabilityExists(area1num, areanum)) continue; - //only end in areas we can stand - if (!AAS_AreaGrounded(areanum)) continue; - //never go through cluster portals!! - numareas = AAS_TraceAreas(areastart, bsptrace.endpos, areas, NULL, 20); - if (numareas >= 20) continue; - for (j = 0; j < numareas; j++) - { - if (aasworld.areasettings[areas[j]].contents & AREACONTENTS_CLUSTERPORTAL) break; - } //end for - if (j < numareas) continue; - //create a new reachability link - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = areanum; - lreach->facenum = face2num; - lreach->edgenum = 0; - VectorCopy(areastart, lreach->start); - //VectorCopy(facecenter, lreach->end); - VectorCopy(bsptrace.endpos, lreach->end); - lreach->traveltype = TRAVEL_GRAPPLEHOOK; - VectorSubtract(lreach->end, lreach->start, dir); - lreach->traveltime = aassettings.rs_startgrapple + VectorLength(dir) * 0.25; - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - // - reach_grapple++; - } //end for - // - return qfalse; -} //end of the function AAS_Reachability_Grapple -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_SetWeaponJumpAreaFlags(void) -{ - int ent, i; - vec3_t mins = {-15, -15, -15}, maxs = {15, 15, 15}; - vec3_t origin; - int areanum, weaponjumpareas, spawnflags; - char classname[MAX_EPAIRKEY]; - - weaponjumpareas = 0; - for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) - { - if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; - if ( - !strcmp(classname, "item_armor_body") || - !strcmp(classname, "item_armor_combat") || - !strcmp(classname, "item_health_mega") || - !strcmp(classname, "weapon_grenadelauncher") || - !strcmp(classname, "weapon_rocketlauncher") || - !strcmp(classname, "weapon_lightning") || - !strcmp(classname, "weapon_plasmagun") || - !strcmp(classname, "weapon_railgun") || - !strcmp(classname, "weapon_bfg") || - !strcmp(classname, "item_quad") || - !strcmp(classname, "item_regen") || - !strcmp(classname, "item_invulnerability")) - { - if (AAS_VectorForBSPEpairKey(ent, "origin", origin)) - { - spawnflags = 0; - AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); - //if not a stationary item - if (!(spawnflags & 1)) - { - if (!AAS_DropToFloor(origin, mins, maxs)) - { - botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n", - classname, origin[0], origin[1], origin[2]); - } //end if - } //end if - //areanum = AAS_PointAreaNum(origin); - areanum = AAS_BestReachableArea(origin, mins, maxs, origin); - //the bot may rocket jump towards this area - aasworld.areasettings[areanum].areaflags |= AREA_WEAPONJUMP; - // - //if (!AAS_AreaGrounded(areanum)) - // botimport.Print(PRT_MESSAGE, "area not grounded\n"); - // - weaponjumpareas++; - } //end if - } //end if - } //end for - for (i = 1; i < aasworld.numareas; i++) - { - if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) - { - aasworld.areasettings[i].areaflags |= AREA_WEAPONJUMP; - weaponjumpareas++; - } //end if - } //end for - botimport.Print(PRT_MESSAGE, "%d weapon jump areas\n", weaponjumpareas); -} //end of the function AAS_SetWeaponJumpAreaFlags -//=========================================================================== -// create a possible weapon jump reachability from area1 to area2 -// -// check if there's a cool item in the second area -// check if area1 is lower than area2 -// check if the bot can rocketjump from area1 to area2 -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_Reachability_WeaponJump(int area1num, int area2num) -{ - int face2num, i, n, ret, visualize; - float speed, zvel, hordist; - aas_face_t *face2; - aas_area_t *area1, *area2; - aas_lreachability_t *lreach; - vec3_t areastart, facecenter, start, end, dir, cmdmove;// teststart; - vec3_t velocity; - aas_clientmove_t move; - aas_trace_t trace; - - visualize = qfalse; -// if (area1num == 4436 && area2num == 4318) -// { -// visualize = qtrue; -// } - if (!AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) return qfalse; - if (!AAS_AreaGrounded(area2num)) return qfalse; - //NOTE: only weapon jump towards areas with an interesting item in it?? - if (!(aasworld.areasettings[area2num].areaflags & AREA_WEAPONJUMP)) return qfalse; - // - area1 = &aasworld.areas[area1num]; - area2 = &aasworld.areas[area2num]; - //don't weapon jump towards way lower areas - if (area2->maxs[2] < area1->mins[2]) return qfalse; - // - VectorCopy(aasworld.areas[area1num].center, start); - //if not a swim area - if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num, - start[0], start[1], start[2]); - VectorCopy(start, end); - end[2] -= 1000; - trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); - if (trace.startsolid) return qfalse; - VectorCopy(trace.endpos, areastart); - // - //areastart is now the start point - // - for (i = 0; i < area2->numfaces; i++) - { - face2num = aasworld.faceindex[area2->firstface + i]; - face2 = &aasworld.faces[abs(face2num)]; - //if it is not a solid face - if (!(face2->faceflags & FACE_GROUND)) continue; - //get the center of the face - AAS_FaceCenter(face2num, facecenter); - //only go higher up with weapon jumps - if (facecenter[2] < areastart[2] + 64) continue; - //NOTE: set to 2 to allow bfg jump reachabilities - for (n = 0; n < 1/*2*/; n++) - { - //get the rocket jump z velocity - if (n) zvel = AAS_BFGJumpZVelocity(areastart); - else zvel = AAS_RocketJumpZVelocity(areastart); - //get the horizontal speed for the jump, if it isn't possible to calculate this - //speed (the jump is not possible) then there's no jump reachability created - ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed); - if (ret && speed < 300) - { - //direction towards the face center - VectorSubtract(facecenter, areastart, dir); - dir[2] = 0; - hordist = VectorNormalize(dir); - //if (hordist < 1.6 * (facecenter[2] - areastart[2])) - { - //get command movement - VectorScale(dir, speed, cmdmove); - VectorSet(velocity, 0, 0, zvel); - /* - //get command movement - VectorScale(dir, speed, velocity); - velocity[2] = zvel; - VectorSet(cmdmove, 0, 0, 0); - */ - // - AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qtrue, - velocity, cmdmove, 30, 30, 0.1f, - SE_ENTERWATER|SE_ENTERSLIME| - SE_ENTERLAVA|SE_HITGROUNDDAMAGE| - SE_TOUCHJUMPPAD|SE_HITGROUND|SE_HITGROUNDAREA, area2num, visualize); - //if prediction time wasn't enough to fully predict the movement - //don't enter slime or lava and don't fall from too high - if (move.frames < 30 && - !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) - && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD))) - { - //create a rocket or bfg jump reachability from area1 to area2 - lreach = AAS_AllocReachability(); - if (!lreach) return qfalse; - lreach->areanum = area2num; - lreach->facenum = 0; - lreach->edgenum = 0; - VectorCopy(areastart, lreach->start); - VectorCopy(facecenter, lreach->end); - if (n) - { - lreach->traveltype = TRAVEL_BFGJUMP; - lreach->traveltime = aassettings.rs_bfgjump; - } //end if - else - { - lreach->traveltype = TRAVEL_ROCKETJUMP; - lreach->traveltime = aassettings.rs_rocketjump; - } //end else - lreach->next = areareachability[area1num]; - areareachability[area1num] = lreach; - // - reach_rocketjump++; - return qtrue; - } //end if - } //end if - } //end if - } //end for - } //end for - // - return qfalse; -} //end of the function AAS_Reachability_WeaponJump -//=========================================================================== -// calculates additional walk off ledge reachabilities for the given area -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_Reachability_WalkOffLedge(int areanum) -{ - int i, j, k, l, m, n, p, areas[10], numareas; - int face1num, face2num, face3num, edge1num, edge2num, edge3num; - int otherareanum, gap, reachareanum, side; - aas_area_t *area, *area2; - aas_face_t *face1, *face2, *face3; - aas_edge_t *edge; - aas_plane_t *plane; - vec_t *v1, *v2; - vec3_t sharededgevec, mid, dir, testend; - aas_lreachability_t *lreach; - aas_trace_t trace; - - if (!AAS_AreaGrounded(areanum) || AAS_AreaSwim(areanum)) return; - // - area = &aasworld.areas[areanum]; - // - for (i = 0; i < area->numfaces; i++) - { - face1num = aasworld.faceindex[area->firstface + i]; - face1 = &aasworld.faces[abs(face1num)]; - //face 1 must be a ground face - if (!(face1->faceflags & FACE_GROUND)) continue; - //go through all the edges of this ground face - for (k = 0; k < face1->numedges; k++) - { - edge1num = aasworld.edgeindex[face1->firstedge + k]; - //find another not ground face using this same edge - for (j = 0; j < area->numfaces; j++) - { - face2num = aasworld.faceindex[area->firstface + j]; - face2 = &aasworld.faces[abs(face2num)]; - //face 2 may not be a ground face - if (face2->faceflags & FACE_GROUND) continue; - //compare all the edges - for (l = 0; l < face2->numedges; l++) - { - edge2num = aasworld.edgeindex[face2->firstedge + l]; - if (abs(edge1num) == abs(edge2num)) - { - //get the area at the other side of the face - if (face2->frontarea == areanum) otherareanum = face2->backarea; - else otherareanum = face2->frontarea; - // - area2 = &aasworld.areas[otherareanum]; - //if the other area is grounded! - if (aasworld.areasettings[otherareanum].areaflags & AREA_GROUNDED) - { - //check for a possible gap - gap = qfalse; - for (n = 0; n < area2->numfaces; n++) - { - face3num = aasworld.faceindex[area2->firstface + n]; - //may not be the shared face of the two areas - if (abs(face3num) == abs(face2num)) continue; - // - face3 = &aasworld.faces[abs(face3num)]; - //find an edge shared by all three faces - for (m = 0; m < face3->numedges; m++) - { - edge3num = aasworld.edgeindex[face3->firstedge + m]; - //but the edge should be shared by all three faces - if (abs(edge3num) == abs(edge1num)) - { - if (!(face3->faceflags & FACE_SOLID)) - { - gap = qtrue; - break; - } //end if - // - if (face3->faceflags & FACE_GROUND) - { - gap = qfalse; - break; - } //end if - //FIXME: there are more situations to be handled - gap = qtrue; - break; - } //end if - } //end for - if (m < face3->numedges) break; - } //end for - if (!gap) break; - } //end if - //check for a walk off ledge reachability - edge = &aasworld.edges[abs(edge1num)]; - side = edge1num < 0; - // - v1 = aasworld.vertexes[edge->v[side]]; - v2 = aasworld.vertexes[edge->v[!side]]; - // - plane = &aasworld.planes[face1->planenum]; - //get the points really into the areas - VectorSubtract(v2, v1, sharededgevec); - CrossProduct(plane->normal, sharededgevec, dir); - VectorNormalize(dir); - // - VectorAdd(v1, v2, mid); - VectorScale(mid, 0.5, mid); - VectorMA(mid, 8, dir, mid); - // - VectorCopy(mid, testend); - testend[2] -= 1000; - trace = AAS_TraceClientBBox(mid, testend, PRESENCE_CROUCH, -1); - // - if (trace.startsolid) - { - //Log_Write("area %d: trace.startsolid\r\n", areanum); - break; - } //end if - reachareanum = AAS_PointAreaNum(trace.endpos); - if (reachareanum == areanum) - { - //Log_Write("area %d: same area\r\n", areanum); - break; - } //end if - if (AAS_ReachabilityExists(areanum, reachareanum)) - { - //Log_Write("area %d: reachability already exists\r\n", areanum); - break; - } //end if - if (!AAS_AreaGrounded(reachareanum) && !AAS_AreaSwim(reachareanum)) - { - //Log_Write("area %d, reach area %d: not grounded and not swim\r\n", areanum, reachareanum); - break; - } //end if - // - if (aasworld.areasettings[reachareanum].contents & (AREACONTENTS_SLIME - | AREACONTENTS_LAVA)) - { - //Log_Write("area %d, reach area %d: lava or slime\r\n", areanum, reachareanum); - break; - } //end if - //if not going through a cluster portal - numareas = AAS_TraceAreas(mid, testend, areas, NULL, sizeof(areas) / sizeof(int)); - for (p = 0; p < numareas; p++) - if (AAS_AreaClusterPortal(areas[p])) - break; - if (p < numareas) - break; - // if a maximum fall height is set and the bot would fall down further - if (aassettings.rs_maxfallheight && fabs(mid[2] - trace.endpos[2]) > aassettings.rs_maxfallheight) - break; - // - lreach = AAS_AllocReachability(); - if (!lreach) break; - lreach->areanum = reachareanum; - lreach->facenum = 0; - lreach->edgenum = edge1num; - VectorCopy(mid, lreach->start); - VectorCopy(trace.endpos, lreach->end); - lreach->traveltype = TRAVEL_WALKOFFLEDGE; - lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(mid[2] - trace.endpos[2]) * 50 / aassettings.phys_gravity; - if (!AAS_AreaSwim(reachareanum) && !AAS_AreaJumpPad(reachareanum)) - { - if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta5) - { - lreach->traveltime += aassettings.rs_falldamage5; - } //end if - else if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta10) - { - lreach->traveltime += aassettings.rs_falldamage10; - } //end if - } //end if - lreach->next = areareachability[areanum]; - areareachability[areanum] = lreach; - //we've got another walk off ledge reachability - reach_walkoffledge++; - } //end if - } //end for - } //end for - } //end for - } //end for -} //end of the function AAS_Reachability_WalkOffLedge -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_StoreReachability(void) -{ - int i; - aas_areasettings_t *areasettings; - aas_lreachability_t *lreach; - aas_reachability_t *reach; - - if (aasworld.reachability) FreeMemory(aasworld.reachability); - aasworld.reachability = (aas_reachability_t *) GetClearedMemory((numlreachabilities + 10) * sizeof(aas_reachability_t)); - aasworld.reachabilitysize = 1; - for (i = 0; i < aasworld.numareas; i++) - { - areasettings = &aasworld.areasettings[i]; - areasettings->firstreachablearea = aasworld.reachabilitysize; - areasettings->numreachableareas = 0; - for (lreach = areareachability[i]; lreach; lreach = lreach->next) - { - reach = &aasworld.reachability[areasettings->firstreachablearea + - areasettings->numreachableareas]; - reach->areanum = lreach->areanum; - reach->facenum = lreach->facenum; - reach->edgenum = lreach->edgenum; - VectorCopy(lreach->start, reach->start); - VectorCopy(lreach->end, reach->end); - reach->traveltype = lreach->traveltype; - reach->traveltime = lreach->traveltime; - // - areasettings->numreachableareas++; - } //end for - aasworld.reachabilitysize += areasettings->numreachableareas; - } //end for -} //end of the function AAS_StoreReachability -//=========================================================================== -// -// TRAVEL_WALK 100% equal floor height + steps -// TRAVEL_CROUCH 100% -// TRAVEL_BARRIERJUMP 100% -// TRAVEL_JUMP 80% -// TRAVEL_LADDER 100% + fall down from ladder + jump up to ladder -// TRAVEL_WALKOFFLEDGE 90% walk off very steep walls? -// TRAVEL_SWIM 100% -// TRAVEL_WATERJUMP 100% -// TRAVEL_TELEPORT 100% -// TRAVEL_ELEVATOR 100% -// TRAVEL_GRAPPLEHOOK 100% -// TRAVEL_DOUBLEJUMP 0% -// TRAVEL_RAMPJUMP 0% -// TRAVEL_STRAFEJUMP 0% -// TRAVEL_ROCKETJUMP 100% (currently limited towards areas with items) -// TRAVEL_BFGJUMP 0% (currently disabled) -// TRAVEL_JUMPPAD 100% -// TRAVEL_FUNCBOB 100% -// -// Parameter: - -// Returns: true if NOT finished -// Changes Globals: - -//=========================================================================== -int AAS_ContinueInitReachability(float time) -{ - int i, j, todo, start_time; - static float framereachability, reachability_delay; - static int lastpercentage; - - if (!aasworld.loaded) return qfalse; - //if reachability is calculated for all areas - if (aasworld.numreachabilityareas >= aasworld.numareas + 2) return qfalse; - //if starting with area 1 (area 0 is a dummy) - if (aasworld.numreachabilityareas == 1) - { - botimport.Print(PRT_MESSAGE, "calculating reachability...\n"); - lastpercentage = 0; - framereachability = 2000; - reachability_delay = 1000; - } //end if - //number of areas to calculate reachability for this cycle - todo = aasworld.numreachabilityareas + (int) framereachability; - start_time = Sys_MilliSeconds(); - //loop over the areas - for (i = aasworld.numreachabilityareas; i < aasworld.numareas && i < todo; i++) - { - aasworld.numreachabilityareas++; - //only create jumppad reachabilities from jumppad areas - if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) - { - continue; - } //end if - //loop over the areas - for (j = 1; j < aasworld.numareas; j++) - { - if (i == j) continue; - //never create reachabilities from teleporter or jumppad areas to regular areas - if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)) - { - if (!(aasworld.areasettings[j].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))) - { - continue; - } //end if - } //end if - //if there already is a reachability link from area i to j - if (AAS_ReachabilityExists(i, j)) continue; - //check for a swim reachability - if (AAS_Reachability_Swim(i, j)) continue; - //check for a simple walk on equal floor height reachability - if (AAS_Reachability_EqualFloorHeight(i, j)) continue; - //check for step, barrier, waterjump and walk off ledge reachabilities - if (AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(i, j)) continue; - //check for ladder reachabilities - if (AAS_Reachability_Ladder(i, j)) continue; - //check for a jump reachability - if (AAS_Reachability_Jump(i, j)) continue; - } //end for - //never create these reachabilities from teleporter or jumppad areas - if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)) - { - continue; - } //end if - //loop over the areas - for (j = 1; j < aasworld.numareas; j++) - { - if (i == j) continue; - // - if (AAS_ReachabilityExists(i, j)) continue; - //check for a grapple hook reachability - if (calcgrapplereach) AAS_Reachability_Grapple(i, j); - //check for a weapon jump reachability - AAS_Reachability_WeaponJump(i, j); - } //end for - //if the calculation took more time than the max reachability delay - if (Sys_MilliSeconds() - start_time > (int) reachability_delay) break; - // - if (aasworld.numreachabilityareas * 1000 / aasworld.numareas > lastpercentage) break; - } //end for - // - if (aasworld.numreachabilityareas == aasworld.numareas) - { - botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) 100.0); - botimport.Print(PRT_MESSAGE, "\nplease wait while storing reachability...\n"); - aasworld.numreachabilityareas++; - } //end if - //if this is the last step in the reachability calculations - else if (aasworld.numreachabilityareas == aasworld.numareas + 1) - { - //create additional walk off ledge reachabilities for every area - for (i = 1; i < aasworld.numareas; i++) - { - //only create jumppad reachabilities from jumppad areas - if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) - { - continue; - } //end if - AAS_Reachability_WalkOffLedge(i); - } //end for - //create jump pad reachabilities - AAS_Reachability_JumpPad(); - //create teleporter reachabilities - AAS_Reachability_Teleport(); - //create elevator (func_plat) reachabilities - AAS_Reachability_Elevator(); - //create func_bobbing reachabilities - AAS_Reachability_FuncBobbing(); - // -#ifdef DEBUG - botimport.Print(PRT_MESSAGE, "%6d reach swim\n", reach_swim); - botimport.Print(PRT_MESSAGE, "%6d reach equal floor\n", reach_equalfloor); - botimport.Print(PRT_MESSAGE, "%6d reach step\n", reach_step); - botimport.Print(PRT_MESSAGE, "%6d reach barrier\n", reach_barrier); - botimport.Print(PRT_MESSAGE, "%6d reach waterjump\n", reach_waterjump); - botimport.Print(PRT_MESSAGE, "%6d reach walkoffledge\n", reach_walkoffledge); - botimport.Print(PRT_MESSAGE, "%6d reach jump\n", reach_jump); - botimport.Print(PRT_MESSAGE, "%6d reach ladder\n", reach_ladder); - botimport.Print(PRT_MESSAGE, "%6d reach walk\n", reach_walk); - botimport.Print(PRT_MESSAGE, "%6d reach teleport\n", reach_teleport); - botimport.Print(PRT_MESSAGE, "%6d reach funcbob\n", reach_funcbob); - botimport.Print(PRT_MESSAGE, "%6d reach elevator\n", reach_elevator); - botimport.Print(PRT_MESSAGE, "%6d reach grapple\n", reach_grapple); - botimport.Print(PRT_MESSAGE, "%6d reach rocketjump\n", reach_rocketjump); - botimport.Print(PRT_MESSAGE, "%6d reach jumppad\n", reach_jumppad); -#endif - //*/ - //store all the reachabilities - AAS_StoreReachability(); - //free the reachability link heap - AAS_ShutDownReachabilityHeap(); - // - FreeMemory(areareachability); - // - aasworld.numreachabilityareas++; - // - botimport.Print(PRT_MESSAGE, "calculating clusters...\n"); - } //end if - else - { - lastpercentage = aasworld.numreachabilityareas * 1000 / aasworld.numareas; - botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) lastpercentage / 10); - } //end else - //not yet finished - return qtrue; -} //end of the function AAS_ContinueInitReachability -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitReachability(void) -{ - if (!aasworld.loaded) return; - - if (aasworld.reachabilitysize) - { -#ifndef BSPC - if (!((int)LibVarGetValue("forcereachability"))) - { - aasworld.numreachabilityareas = aasworld.numareas + 2; - return; - } //end if -#else - aasworld.numreachabilityareas = aasworld.numareas + 2; - return; -#endif //BSPC - } //end if -#ifndef BSPC - calcgrapplereach = LibVarGetValue("grapplereach"); -#endif - aasworld.savefile = qtrue; - //start with area 1 because area zero is a dummy - aasworld.numreachabilityareas = 1; - ////aasworld.numreachabilityareas = aasworld.numareas + 1; //only calculate entity reachabilities - //setup the heap with reachability links - AAS_SetupReachabilityHeap(); - //allocate area reachability link array - areareachability = (aas_lreachability_t **) GetClearedMemory( - aasworld.numareas * sizeof(aas_lreachability_t *)); - // - AAS_SetWeaponJumpAreaFlags(); -} //end of the function AAS_InitReachable diff --git a/tools/quake3/bspc/deps/botlib/be_aas_reach.h b/tools/quake3/bspc/deps/botlib/be_aas_reach.h deleted file mode 100644 index 0ab8740b..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_reach.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_reach.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_reach.h $ - * - *****************************************************************************/ - -#ifdef AASINTERN -//initialize calculating the reachabilities -void AAS_InitReachability(void); -//continue calculating the reachabilities -int AAS_ContinueInitReachability(float time); -// -int AAS_BestReachableLinkArea(aas_link_t *areas); -#endif //AASINTERN - -//returns true if the are has reachabilities to other areas -int AAS_AreaReachability(int areanum); -//returns the best reachable area and goal origin for a bounding box at the given origin -int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin); -//returns the best jumppad area from which the bbox at origin is reachable -int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs); -//returns the next reachability using the given model -int AAS_NextModelReachability(int num, int modelnum); -//returns the total area of the ground faces of the given area -float AAS_AreaGroundFaceArea(int areanum); -//returns true if the area is crouch only -int AAS_AreaCrouch(int areanum); -//returns true if a player can swim in this area -int AAS_AreaSwim(int areanum); -//returns true if the area is filled with a liquid -int AAS_AreaLiquid(int areanum); -//returns true if the area contains lava -int AAS_AreaLava(int areanum); -//returns true if the area contains slime -int AAS_AreaSlime(int areanum); -//returns true if the area has one or more ground faces -int AAS_AreaGrounded(int areanum); -//returns true if the area has one or more ladder faces -int AAS_AreaLadder(int areanum); -//returns true if the area is a jump pad -int AAS_AreaJumpPad(int areanum); -//returns true if the area is donotenter -int AAS_AreaDoNotEnter(int areanum); diff --git a/tools/quake3/bspc/deps/botlib/be_aas_route.c b/tools/quake3/bspc/deps/botlib/be_aas_route.c deleted file mode 100644 index 2795c11e..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_route.c +++ /dev/null @@ -1,2209 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_route.c - * - * desc: AAS - * - * $Archive: /MissionPack/code/botlib/be_aas_route.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_utils.h" -#include "l_memory.h" -#include "l_log.h" -#include "l_crc.h" -#include "l_libvar.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_aas_def.h" - -#define ROUTING_DEBUG - -//travel time in hundreths of a second = distance * 100 / speed -#define DISTANCEFACTOR_CROUCH 1.3f //crouch speed = 100 -#define DISTANCEFACTOR_SWIM 1 //should be 0.66, swim speed = 150 -#define DISTANCEFACTOR_WALK 0.33f //walk speed = 300 - -//cache refresh time -#define CACHE_REFRESHTIME 15.0f //15 seconds refresh time - -//maximum number of routing updates each frame -#define MAX_FRAMEROUTINGUPDATES 10 - - -/* - - area routing cache: - stores the distances within one cluster to a specific goal area - this goal area is in this same cluster and could be a cluster portal - for every cluster there's a list with routing cache for every area - in that cluster (including the portals of that cluster) - area cache stores aasworld.clusters[?].numreachabilityareas travel times - - portal routing cache: - stores the distances of all portals to a specific goal area - this goal area could be in any cluster and could also be a cluster portal - for every area (aasworld.numareas) the portal cache stores - aasworld.numportals travel times - -*/ - -#ifdef ROUTING_DEBUG -int numareacacheupdates; -int numportalcacheupdates; -#endif //ROUTING_DEBUG - -int routingcachesize; -int max_routingcachesize; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef ROUTING_DEBUG -void AAS_RoutingInfo(void) -{ - botimport.Print(PRT_MESSAGE, "%d area cache updates\n", numareacacheupdates); - botimport.Print(PRT_MESSAGE, "%d portal cache updates\n", numportalcacheupdates); - botimport.Print(PRT_MESSAGE, "%d bytes routing cache\n", routingcachesize); -} //end of the function AAS_RoutingInfo -#endif //ROUTING_DEBUG -//=========================================================================== -// returns the number of the area in the cluster -// assumes the given area is in the given cluster or a portal of the cluster -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -__inline int AAS_ClusterAreaNum(int cluster, int areanum) -{ - int side, areacluster; - - areacluster = aasworld.areasettings[areanum].cluster; - if (areacluster > 0) return aasworld.areasettings[areanum].clusterareanum; - else - { -/*#ifdef ROUTING_DEBUG - if (aasworld.portals[-areacluster].frontcluster != cluster && - aasworld.portals[-areacluster].backcluster != cluster) - { - botimport.Print(PRT_ERROR, "portal %d: does not belong to cluster %d\n" - , -areacluster, cluster); - } //end if -#endif //ROUTING_DEBUG*/ - side = aasworld.portals[-areacluster].frontcluster != cluster; - return aasworld.portals[-areacluster].clusterareanum[side]; - } //end else -} //end of the function AAS_ClusterAreaNum -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitTravelFlagFromType(void) -{ - int i; - - for (i = 0; i < MAX_TRAVELTYPES; i++) - { - aasworld.travelflagfortype[i] = TFL_INVALID; - } //end for - aasworld.travelflagfortype[TRAVEL_INVALID] = TFL_INVALID; - aasworld.travelflagfortype[TRAVEL_WALK] = TFL_WALK; - aasworld.travelflagfortype[TRAVEL_CROUCH] = TFL_CROUCH; - aasworld.travelflagfortype[TRAVEL_BARRIERJUMP] = TFL_BARRIERJUMP; - aasworld.travelflagfortype[TRAVEL_JUMP] = TFL_JUMP; - aasworld.travelflagfortype[TRAVEL_LADDER] = TFL_LADDER; - aasworld.travelflagfortype[TRAVEL_WALKOFFLEDGE] = TFL_WALKOFFLEDGE; - aasworld.travelflagfortype[TRAVEL_SWIM] = TFL_SWIM; - aasworld.travelflagfortype[TRAVEL_WATERJUMP] = TFL_WATERJUMP; - aasworld.travelflagfortype[TRAVEL_TELEPORT] = TFL_TELEPORT; - aasworld.travelflagfortype[TRAVEL_ELEVATOR] = TFL_ELEVATOR; - aasworld.travelflagfortype[TRAVEL_ROCKETJUMP] = TFL_ROCKETJUMP; - aasworld.travelflagfortype[TRAVEL_BFGJUMP] = TFL_BFGJUMP; - aasworld.travelflagfortype[TRAVEL_GRAPPLEHOOK] = TFL_GRAPPLEHOOK; - aasworld.travelflagfortype[TRAVEL_DOUBLEJUMP] = TFL_DOUBLEJUMP; - aasworld.travelflagfortype[TRAVEL_RAMPJUMP] = TFL_RAMPJUMP; - aasworld.travelflagfortype[TRAVEL_STRAFEJUMP] = TFL_STRAFEJUMP; - aasworld.travelflagfortype[TRAVEL_JUMPPAD] = TFL_JUMPPAD; - aasworld.travelflagfortype[TRAVEL_FUNCBOB] = TFL_FUNCBOB; -} //end of the function AAS_InitTravelFlagFromType -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -__inline int AAS_TravelFlagForType_inline(int traveltype) -{ - int tfl; - - tfl = 0; - if (tfl & TRAVELFLAG_NOTTEAM1) - tfl |= TFL_NOTTEAM1; - if (tfl & TRAVELFLAG_NOTTEAM2) - tfl |= TFL_NOTTEAM2; - traveltype &= TRAVELTYPE_MASK; - if (traveltype < 0 || traveltype >= MAX_TRAVELTYPES) - return TFL_INVALID; - tfl |= aasworld.travelflagfortype[traveltype]; - return tfl; -} //end of the function AAS_TravelFlagForType_inline -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_TravelFlagForType(int traveltype) -{ - return AAS_TravelFlagForType_inline(traveltype); -} //end of the function AAS_TravelFlagForType_inline -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_UnlinkCache(aas_routingcache_t *cache) -{ - if (cache->time_next) cache->time_next->time_prev = cache->time_prev; - else aasworld.newestcache = cache->time_prev; - if (cache->time_prev) cache->time_prev->time_next = cache->time_next; - else aasworld.oldestcache = cache->time_next; - cache->time_next = NULL; - cache->time_prev = NULL; -} //end of the function AAS_UnlinkCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_LinkCache(aas_routingcache_t *cache) -{ - if (aasworld.newestcache) - { - aasworld.newestcache->time_next = cache; - cache->time_prev = aasworld.newestcache; - } //end if - else - { - aasworld.oldestcache = cache; - cache->time_prev = NULL; - } //end else - cache->time_next = NULL; - aasworld.newestcache = cache; -} //end of the function AAS_LinkCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeRoutingCache(aas_routingcache_t *cache) -{ - AAS_UnlinkCache(cache); - routingcachesize -= cache->size; - FreeMemory(cache); -} //end of the function AAS_FreeRoutingCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_RemoveRoutingCacheInCluster( int clusternum ) -{ - int i; - aas_routingcache_t *cache, *nextcache; - aas_cluster_t *cluster; - - if (!aasworld.clusterareacache) - return; - cluster = &aasworld.clusters[clusternum]; - for (i = 0; i < cluster->numareas; i++) - { - for (cache = aasworld.clusterareacache[clusternum][i]; cache; cache = nextcache) - { - nextcache = cache->next; - AAS_FreeRoutingCache(cache); - } //end for - aasworld.clusterareacache[clusternum][i] = NULL; - } //end for -} //end of the function AAS_RemoveRoutingCacheInCluster -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_RemoveRoutingCacheUsingArea( int areanum ) -{ - int i, clusternum; - aas_routingcache_t *cache, *nextcache; - - clusternum = aasworld.areasettings[areanum].cluster; - if (clusternum > 0) - { - //remove all the cache in the cluster the area is in - AAS_RemoveRoutingCacheInCluster( clusternum ); - } //end if - else - { - // if this is a portal remove all cache in both the front and back cluster - AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].frontcluster ); - AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].backcluster ); - } //end else - // remove all portal cache - for (i = 0; i < aasworld.numareas; i++) - { - //refresh portal cache - for (cache = aasworld.portalcache[i]; cache; cache = nextcache) - { - nextcache = cache->next; - AAS_FreeRoutingCache(cache); - } //end for - aasworld.portalcache[i] = NULL; - } //end for -} //end of the function AAS_RemoveRoutingCacheUsingArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_EnableRoutingArea(int areanum, int enable) -{ - int flags; - - if (areanum <= 0 || areanum >= aasworld.numareas) - { - if (bot_developer) - { - botimport.Print(PRT_ERROR, "AAS_EnableRoutingArea: areanum %d out of range\n", areanum); - } //end if - return 0; - } //end if - flags = aasworld.areasettings[areanum].areaflags & AREA_DISABLED; - if (enable < 0) - return !flags; - - if (enable) - aasworld.areasettings[areanum].areaflags &= ~AREA_DISABLED; - else - aasworld.areasettings[areanum].areaflags |= AREA_DISABLED; - // if the status of the area changed - if ( (flags & AREA_DISABLED) != (aasworld.areasettings[areanum].areaflags & AREA_DISABLED) ) - { - //remove all routing cache involving this area - AAS_RemoveRoutingCacheUsingArea( areanum ); - } //end if - return !flags; -} //end of the function AAS_EnableRoutingArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -__inline float AAS_RoutingTime(void) -{ - return AAS_Time(); -} //end of the function AAS_RoutingTime -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_GetAreaContentsTravelFlags(int areanum) -{ - int contents, tfl; - - contents = aasworld.areasettings[areanum].contents; - tfl = 0; - if (contents & AREACONTENTS_WATER) - tfl |= TFL_WATER; - else if (contents & AREACONTENTS_SLIME) - tfl |= TFL_SLIME; - else if (contents & AREACONTENTS_LAVA) - tfl |= TFL_LAVA; - else - tfl |= TFL_AIR; - if (contents & AREACONTENTS_DONOTENTER) - tfl |= TFL_DONOTENTER; - if (contents & AREACONTENTS_NOTTEAM1) - tfl |= TFL_NOTTEAM1; - if (contents & AREACONTENTS_NOTTEAM2) - tfl |= TFL_NOTTEAM2; - if (aasworld.areasettings[areanum].areaflags & AREA_BRIDGE) - tfl |= TFL_BRIDGE; - return tfl; -} //end of the function AAS_GetAreaContentsTravelFlags -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -__inline int AAS_AreaContentsTravelFlags_inline(int areanum) -{ - return aasworld.areacontentstravelflags[areanum]; -} //end of the function AAS_AreaContentsTravelFlags -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaContentsTravelFlags(int areanum) -{ - return aasworld.areacontentstravelflags[areanum]; -} //end of the function AAS_AreaContentsTravelFlags -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitAreaContentsTravelFlags(void) -{ - int i; - - if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags); - aasworld.areacontentstravelflags = (int *) GetClearedMemory(aasworld.numareas * sizeof(int)); - // - for (i = 0; i < aasworld.numareas; i++) { - aasworld.areacontentstravelflags[i] = AAS_GetAreaContentsTravelFlags(i); - } -} //end of the function AAS_InitAreaContentsTravelFlags -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CreateReversedReachability(void) -{ - int i, n; - aas_reversedlink_t *revlink; - aas_reachability_t *reach; - aas_areasettings_t *settings; - char *ptr; -#ifdef DEBUG - int starttime; - - starttime = Sys_MilliSeconds(); -#endif - //free reversed links that have already been created - if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability); - //allocate memory for the reversed reachability links - ptr = (char *) GetClearedMemory(aasworld.numareas * sizeof(aas_reversedreachability_t) + - aasworld.reachabilitysize * sizeof(aas_reversedlink_t)); - // - aasworld.reversedreachability = (aas_reversedreachability_t *) ptr; - //pointer to the memory for the reversed links - ptr += aasworld.numareas * sizeof(aas_reversedreachability_t); - //check all reachabilities of all areas - for (i = 1; i < aasworld.numareas; i++) - { - //settings of the area - settings = &aasworld.areasettings[i]; - // - if (settings->numreachableareas >= 128) - botimport.Print(PRT_WARNING, "area %d has more than 128 reachabilities\n", i); - //create reversed links for the reachabilities - for (n = 0; n < settings->numreachableareas && n < 128; n++) - { - //reachability link - reach = &aasworld.reachability[settings->firstreachablearea + n]; - // - revlink = (aas_reversedlink_t *) ptr; - ptr += sizeof(aas_reversedlink_t); - // - revlink->areanum = i; - revlink->linknum = settings->firstreachablearea + n; - revlink->next = aasworld.reversedreachability[reach->areanum].first; - aasworld.reversedreachability[reach->areanum].first = revlink; - aasworld.reversedreachability[reach->areanum].numlinks++; - } //end for - } //end for -#ifdef DEBUG - botimport.Print(PRT_MESSAGE, "reversed reachability %d msec\n", Sys_MilliSeconds() - starttime); -#endif -} //end of the function AAS_CreateReversedReachability -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end) -{ - int intdist; - float dist; - vec3_t dir; - - VectorSubtract(start, end, dir); - dist = VectorLength(dir); - //if crouch only area - if (AAS_AreaCrouch(areanum)) dist *= DISTANCEFACTOR_CROUCH; - //if swim area - else if (AAS_AreaSwim(areanum)) dist *= DISTANCEFACTOR_SWIM; - //normal walk area - else dist *= DISTANCEFACTOR_WALK; - // - intdist = (int) dist; - //make sure the distance isn't zero - if (intdist <= 0) intdist = 1; - return intdist; -} //end of the function AAS_AreaTravelTime -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CalculateAreaTravelTimes(void) -{ - int i, l, n, size; - char *ptr; - vec3_t end; - aas_reversedreachability_t *revreach; - aas_reversedlink_t *revlink; - aas_reachability_t *reach; - aas_areasettings_t *settings; - int starttime; - - starttime = Sys_MilliSeconds(); - //if there are still area travel times, free the memory - if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes); - //get the total size of all the area travel times - size = aasworld.numareas * sizeof(unsigned short **); - for (i = 0; i < aasworld.numareas; i++) - { - revreach = &aasworld.reversedreachability[i]; - //settings of the area - settings = &aasworld.areasettings[i]; - // - size += settings->numreachableareas * sizeof(unsigned short *); - // - size += settings->numreachableareas * revreach->numlinks * sizeof(unsigned short); - } //end for - //allocate memory for the area travel times - ptr = (char *) GetClearedMemory(size); - aasworld.areatraveltimes = (unsigned short ***) ptr; - ptr += aasworld.numareas * sizeof(unsigned short **); - //calcluate the travel times for all the areas - for (i = 0; i < aasworld.numareas; i++) - { - //reversed reachabilities of this area - revreach = &aasworld.reversedreachability[i]; - //settings of the area - settings = &aasworld.areasettings[i]; - // - aasworld.areatraveltimes[i] = (unsigned short **) ptr; - ptr += settings->numreachableareas * sizeof(unsigned short *); - // - for (l = 0; l < settings->numreachableareas; l++) - { - aasworld.areatraveltimes[i][l] = (unsigned short *) ptr; - ptr += revreach->numlinks * sizeof(unsigned short); - //reachability link - reach = &aasworld.reachability[settings->firstreachablearea + l]; - // - for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++) - { - VectorCopy(aasworld.reachability[revlink->linknum].end, end); - // - aasworld.areatraveltimes[i][l][n] = AAS_AreaTravelTime(i, end, reach->start); - } //end for - } //end for - } //end for -#ifdef DEBUG - botimport.Print(PRT_MESSAGE, "area travel times %d msec\n", Sys_MilliSeconds() - starttime); -#endif -} //end of the function AAS_CalculateAreaTravelTimes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_PortalMaxTravelTime(int portalnum) -{ - int l, n, t, maxt; - aas_portal_t *portal; - aas_reversedreachability_t *revreach; - aas_reversedlink_t *revlink; - aas_areasettings_t *settings; - - portal = &aasworld.portals[portalnum]; - //reversed reachabilities of this portal area - revreach = &aasworld.reversedreachability[portal->areanum]; - //settings of the portal area - settings = &aasworld.areasettings[portal->areanum]; - // - maxt = 0; - for (l = 0; l < settings->numreachableareas; l++) - { - for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++) - { - t = aasworld.areatraveltimes[portal->areanum][l][n]; - if (t > maxt) - { - maxt = t; - } //end if - } //end for - } //end for - return maxt; -} //end of the function AAS_PortalMaxTravelTime -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitPortalMaxTravelTimes(void) -{ - int i; - - if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes); - - aasworld.portalmaxtraveltimes = (int *) GetClearedMemory(aasworld.numportals * sizeof(int)); - - for (i = 0; i < aasworld.numportals; i++) - { - aasworld.portalmaxtraveltimes[i] = AAS_PortalMaxTravelTime(i); - //botimport.Print(PRT_MESSAGE, "portal %d max tt = %d\n", i, aasworld.portalmaxtraveltimes[i]); - } //end for -} //end of the function AAS_InitPortalMaxTravelTimes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -/* -int AAS_FreeOldestCache(void) -{ - int i, j, bestcluster, bestarea, freed; - float besttime; - aas_routingcache_t *cache, *bestcache; - - freed = qfalse; - besttime = 999999999; - bestcache = NULL; - bestcluster = 0; - bestarea = 0; - //refresh cluster cache - for (i = 0; i < aasworld.numclusters; i++) - { - for (j = 0; j < aasworld.clusters[i].numareas; j++) - { - for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) - { - //never remove cache leading towards a portal - if (aasworld.areasettings[cache->areanum].cluster < 0) continue; - //if this cache is older than the cache we found so far - if (cache->time < besttime) - { - bestcache = cache; - bestcluster = i; - bestarea = j; - besttime = cache->time; - } //end if - } //end for - } //end for - } //end for - if (bestcache) - { - cache = bestcache; - if (cache->prev) cache->prev->next = cache->next; - else aasworld.clusterareacache[bestcluster][bestarea] = cache->next; - if (cache->next) cache->next->prev = cache->prev; - AAS_FreeRoutingCache(cache); - freed = qtrue; - } //end if - besttime = 999999999; - bestcache = NULL; - bestarea = 0; - for (i = 0; i < aasworld.numareas; i++) - { - //refresh portal cache - for (cache = aasworld.portalcache[i]; cache; cache = cache->next) - { - if (cache->time < besttime) - { - bestcache = cache; - bestarea = i; - besttime = cache->time; - } //end if - } //end for - } //end for - if (bestcache) - { - cache = bestcache; - if (cache->prev) cache->prev->next = cache->next; - else aasworld.portalcache[bestarea] = cache->next; - if (cache->next) cache->next->prev = cache->prev; - AAS_FreeRoutingCache(cache); - freed = qtrue; - } //end if - return freed; -} //end of the function AAS_FreeOldestCache -*/ -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_FreeOldestCache(void) -{ - int clusterareanum; - aas_routingcache_t *cache; - - for (cache = aasworld.oldestcache; cache; cache = cache->time_next) { - // never free area cache leading towards a portal - if (cache->type == CACHETYPE_AREA && aasworld.areasettings[cache->areanum].cluster < 0) { - continue; - } - break; - } - if (cache) { - // unlink the cache - if (cache->type == CACHETYPE_AREA) { - //number of the area in the cluster - clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum); - // unlink from cluster area cache - if (cache->prev) cache->prev->next = cache->next; - else aasworld.clusterareacache[cache->cluster][clusterareanum] = cache->next; - if (cache->next) cache->next->prev = cache->prev; - } - else { - // unlink from portal cache - if (cache->prev) cache->prev->next = cache->next; - else aasworld.portalcache[cache->areanum] = cache->next; - if (cache->next) cache->next->prev = cache->prev; - } - AAS_FreeRoutingCache(cache); - return qtrue; - } - return qfalse; -} //end of the function AAS_FreeOldestCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_routingcache_t *AAS_AllocRoutingCache(int numtraveltimes) -{ - aas_routingcache_t *cache; - int size; - - // - size = sizeof(aas_routingcache_t) - + numtraveltimes * sizeof(unsigned short int) - + numtraveltimes * sizeof(unsigned char); - // - routingcachesize += size; - // - cache = (aas_routingcache_t *) GetClearedMemory(size); - cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - + numtraveltimes * sizeof(unsigned short int); - cache->size = size; - return cache; -} //end of the function AAS_AllocRoutingCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeAllClusterAreaCache(void) -{ - int i, j; - aas_routingcache_t *cache, *nextcache; - aas_cluster_t *cluster; - - //free all cluster cache if existing - if (!aasworld.clusterareacache) return; - //free caches - for (i = 0; i < aasworld.numclusters; i++) - { - cluster = &aasworld.clusters[i]; - for (j = 0; j < cluster->numareas; j++) - { - for (cache = aasworld.clusterareacache[i][j]; cache; cache = nextcache) - { - nextcache = cache->next; - AAS_FreeRoutingCache(cache); - } //end for - aasworld.clusterareacache[i][j] = NULL; - } //end for - } //end for - //free the cluster cache array - FreeMemory(aasworld.clusterareacache); - aasworld.clusterareacache = NULL; -} //end of the function AAS_FreeAllClusterAreaCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitClusterAreaCache(void) -{ - int i, size; - char *ptr; - - // - for (size = 0, i = 0; i < aasworld.numclusters; i++) - { - size += aasworld.clusters[i].numareas; - } //end for - //two dimensional array with pointers for every cluster to routing cache - //for every area in that cluster - ptr = (char *) GetClearedMemory( - aasworld.numclusters * sizeof(aas_routingcache_t **) + - size * sizeof(aas_routingcache_t *)); - aasworld.clusterareacache = (aas_routingcache_t ***) ptr; - ptr += aasworld.numclusters * sizeof(aas_routingcache_t **); - for (i = 0; i < aasworld.numclusters; i++) - { - aasworld.clusterareacache[i] = (aas_routingcache_t **) ptr; - ptr += aasworld.clusters[i].numareas * sizeof(aas_routingcache_t *); - } //end for -} //end of the function AAS_InitClusterAreaCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeAllPortalCache(void) -{ - int i; - aas_routingcache_t *cache, *nextcache; - - //free all portal cache if existing - if (!aasworld.portalcache) return; - //free portal caches - for (i = 0; i < aasworld.numareas; i++) - { - for (cache = aasworld.portalcache[i]; cache; cache = nextcache) - { - nextcache = cache->next; - AAS_FreeRoutingCache(cache); - } //end for - aasworld.portalcache[i] = NULL; - } //end for - FreeMemory(aasworld.portalcache); - aasworld.portalcache = NULL; -} //end of the function AAS_FreeAllPortalCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitPortalCache(void) -{ - // - aasworld.portalcache = (aas_routingcache_t **) GetClearedMemory( - aasworld.numareas * sizeof(aas_routingcache_t *)); -} //end of the function AAS_InitPortalCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitRoutingUpdate(void) -{ - int i, maxreachabilityareas; - - //free routing update fields if already existing - if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate); - // - maxreachabilityareas = 0; - for (i = 0; i < aasworld.numclusters; i++) - { - if (aasworld.clusters[i].numreachabilityareas > maxreachabilityareas) - { - maxreachabilityareas = aasworld.clusters[i].numreachabilityareas; - } //end if - } //end for - //allocate memory for the routing update fields - aasworld.areaupdate = (aas_routingupdate_t *) GetClearedMemory( - maxreachabilityareas * sizeof(aas_routingupdate_t)); - // - if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate); - //allocate memory for the portal update fields - aasworld.portalupdate = (aas_routingupdate_t *) GetClearedMemory( - (aasworld.numportals+1) * sizeof(aas_routingupdate_t)); -} //end of the function AAS_InitRoutingUpdate -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_CreateAllRoutingCache(void) -{ - int i, j, t; - - aasworld.initialized = qtrue; - botimport.Print(PRT_MESSAGE, "AAS_CreateAllRoutingCache\n"); - for (i = 1; i < aasworld.numareas; i++) - { - if (!AAS_AreaReachability(i)) continue; - for (j = 1; j < aasworld.numareas; j++) - { - if (i == j) continue; - if (!AAS_AreaReachability(j)) continue; - t = AAS_AreaTravelTimeToGoalArea(i, aasworld.areas[i].center, j, TFL_DEFAULT); - //Log_Write("traveltime from %d to %d is %d", i, j, t); - } //end for - } //end for - aasworld.initialized = qfalse; -} //end of the function AAS_CreateAllRoutingCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== - -//the route cache header -//this header is followed by numportalcache + numareacache aas_routingcache_t -//structures that store routing cache -typedef struct routecacheheader_s -{ - int ident; - int version; - int numareas; - int numclusters; - int areacrc; - int clustercrc; - int numportalcache; - int numareacache; -} routecacheheader_t; - -#define RCID (('C'<<24)+('R'<<16)+('E'<<8)+'M') -#define RCVERSION 2 - -//void AAS_DecompressVis(byte *in, int numareas, byte *decompressed); -//int AAS_CompressVis(byte *vis, int numareas, byte *dest); - -void AAS_WriteRouteCache(void) -{ - int i, j, numportalcache, numareacache, totalsize; - aas_routingcache_t *cache; - aas_cluster_t *cluster; - fileHandle_t fp; - char filename[MAX_QPATH]; - routecacheheader_t routecacheheader; - - numportalcache = 0; - for (i = 0; i < aasworld.numareas; i++) - { - for (cache = aasworld.portalcache[i]; cache; cache = cache->next) - { - numportalcache++; - } //end for - } //end for - numareacache = 0; - for (i = 0; i < aasworld.numclusters; i++) - { - cluster = &aasworld.clusters[i]; - for (j = 0; j < cluster->numareas; j++) - { - for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) - { - numareacache++; - } //end for - } //end for - } //end for - // open the file for writing - Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname); - botimport.FS_FOpenFile( filename, &fp, FS_WRITE ); - if (!fp) - { - AAS_Error("Unable to open file: %s\n", filename); - return; - } //end if - //create the header - routecacheheader.ident = RCID; - routecacheheader.version = RCVERSION; - routecacheheader.numareas = aasworld.numareas; - routecacheheader.numclusters = aasworld.numclusters; - routecacheheader.areacrc = CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas ); - routecacheheader.clustercrc = CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters ); - routecacheheader.numportalcache = numportalcache; - routecacheheader.numareacache = numareacache; - //write the header - botimport.FS_Write(&routecacheheader, sizeof(routecacheheader_t), fp); - // - totalsize = 0; - //write all the cache - for (i = 0; i < aasworld.numareas; i++) - { - for (cache = aasworld.portalcache[i]; cache; cache = cache->next) - { - botimport.FS_Write(cache, cache->size, fp); - totalsize += cache->size; - } //end for - } //end for - for (i = 0; i < aasworld.numclusters; i++) - { - cluster = &aasworld.clusters[i]; - for (j = 0; j < cluster->numareas; j++) - { - for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) - { - botimport.FS_Write(cache, cache->size, fp); - totalsize += cache->size; - } //end for - } //end for - } //end for - // write the visareas - /* - for (i = 0; i < aasworld.numareas; i++) - { - if (!aasworld.areavisibility[i]) { - size = 0; - botimport.FS_Write(&size, sizeof(int), fp); - continue; - } - AAS_DecompressVis( aasworld.areavisibility[i], aasworld.numareas, aasworld.decompressedvis ); - size = AAS_CompressVis( aasworld.decompressedvis, aasworld.numareas, aasworld.decompressedvis ); - botimport.FS_Write(&size, sizeof(int), fp); - botimport.FS_Write(aasworld.decompressedvis, size, fp); - } - */ - // - botimport.FS_FCloseFile(fp); - botimport.Print(PRT_MESSAGE, "\nroute cache written to %s\n", filename); - botimport.Print(PRT_MESSAGE, "written %d bytes of routing cache\n", totalsize); -} //end of the function AAS_WriteRouteCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_routingcache_t *AAS_ReadCache(fileHandle_t fp) -{ - int size; - aas_routingcache_t *cache; - - botimport.FS_Read(&size, sizeof(size), fp); - cache = (aas_routingcache_t *) GetMemory(size); - cache->size = size; - botimport.FS_Read((unsigned char *)cache + sizeof(size), size - sizeof(size), fp); - cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - sizeof(unsigned short) + - (size - sizeof(aas_routingcache_t) + sizeof(unsigned short)) / 3 * 2; - return cache; -} //end of the function AAS_ReadCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_ReadRouteCache(void) -{ - int i, clusterareanum;//, size; - fileHandle_t fp; - char filename[MAX_QPATH]; - routecacheheader_t routecacheheader; - aas_routingcache_t *cache; - - Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname); - botimport.FS_FOpenFile( filename, &fp, FS_READ ); - if (!fp) - { - return qfalse; - } //end if - botimport.FS_Read(&routecacheheader, sizeof(routecacheheader_t), fp ); - if (routecacheheader.ident != RCID) - { - AAS_Error("%s is not a route cache dump\n"); - return qfalse; - } //end if - if (routecacheheader.version != RCVERSION) - { - AAS_Error("route cache dump has wrong version %d, should be %d", routecacheheader.version, RCVERSION); - return qfalse; - } //end if - if (routecacheheader.numareas != aasworld.numareas) - { - //AAS_Error("route cache dump has wrong number of areas\n"); - return qfalse; - } //end if - if (routecacheheader.numclusters != aasworld.numclusters) - { - //AAS_Error("route cache dump has wrong number of clusters\n"); - return qfalse; - } //end if - if (routecacheheader.areacrc != - CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas )) - { - //AAS_Error("route cache dump area CRC incorrect\n"); - return qfalse; - } //end if - if (routecacheheader.clustercrc != - CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters )) - { - //AAS_Error("route cache dump cluster CRC incorrect\n"); - return qfalse; - } //end if - //read all the portal cache - for (i = 0; i < routecacheheader.numportalcache; i++) - { - cache = AAS_ReadCache(fp); - cache->next = aasworld.portalcache[cache->areanum]; - cache->prev = NULL; - if (aasworld.portalcache[cache->areanum]) - aasworld.portalcache[cache->areanum]->prev = cache; - aasworld.portalcache[cache->areanum] = cache; - } //end for - //read all the cluster area cache - for (i = 0; i < routecacheheader.numareacache; i++) - { - cache = AAS_ReadCache(fp); - clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum); - cache->next = aasworld.clusterareacache[cache->cluster][clusterareanum]; - cache->prev = NULL; - if (aasworld.clusterareacache[cache->cluster][clusterareanum]) - aasworld.clusterareacache[cache->cluster][clusterareanum]->prev = cache; - aasworld.clusterareacache[cache->cluster][clusterareanum] = cache; - } //end for - // read the visareas - /* - aasworld.areavisibility = (byte **) GetClearedMemory(aasworld.numareas * sizeof(byte *)); - aasworld.decompressedvis = (byte *) GetClearedMemory(aasworld.numareas * sizeof(byte)); - for (i = 0; i < aasworld.numareas; i++) - { - botimport.FS_Read(&size, sizeof(size), fp ); - if (size) { - aasworld.areavisibility[i] = (byte *) GetMemory(size); - botimport.FS_Read(aasworld.areavisibility[i], size, fp ); - } - } - */ - // - botimport.FS_FCloseFile(fp); - return qtrue; -} //end of the function AAS_ReadRouteCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#define MAX_REACHABILITYPASSAREAS 32 - -void AAS_InitReachabilityAreas(void) -{ - int i, j, numareas, areas[MAX_REACHABILITYPASSAREAS]; - int numreachareas; - aas_reachability_t *reach; - vec3_t start, end; - - if (aasworld.reachabilityareas) - FreeMemory(aasworld.reachabilityareas); - if (aasworld.reachabilityareaindex) - FreeMemory(aasworld.reachabilityareaindex); - - aasworld.reachabilityareas = (aas_reachabilityareas_t *) - GetClearedMemory(aasworld.reachabilitysize * sizeof(aas_reachabilityareas_t)); - aasworld.reachabilityareaindex = (int *) - GetClearedMemory(aasworld.reachabilitysize * MAX_REACHABILITYPASSAREAS * sizeof(int)); - numreachareas = 0; - for (i = 0; i < aasworld.reachabilitysize; i++) - { - reach = &aasworld.reachability[i]; - numareas = 0; - switch(reach->traveltype & TRAVELTYPE_MASK) - { - //trace areas from start to end - case TRAVEL_BARRIERJUMP: - case TRAVEL_WATERJUMP: - VectorCopy(reach->start, end); - end[2] = reach->end[2]; - numareas = AAS_TraceAreas(reach->start, end, areas, NULL, MAX_REACHABILITYPASSAREAS); - break; - case TRAVEL_WALKOFFLEDGE: - VectorCopy(reach->end, start); - start[2] = reach->start[2]; - numareas = AAS_TraceAreas(start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS); - break; - case TRAVEL_GRAPPLEHOOK: - numareas = AAS_TraceAreas(reach->start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS); - break; - - //trace arch - case TRAVEL_JUMP: break; - case TRAVEL_ROCKETJUMP: break; - case TRAVEL_BFGJUMP: break; - case TRAVEL_JUMPPAD: break; - - //trace from reach->start to entity center, along entity movement - //and from entity center to reach->end - case TRAVEL_ELEVATOR: break; - case TRAVEL_FUNCBOB: break; - - //no areas in between - case TRAVEL_WALK: break; - case TRAVEL_CROUCH: break; - case TRAVEL_LADDER: break; - case TRAVEL_SWIM: break; - case TRAVEL_TELEPORT: break; - default: break; - } //end switch - aasworld.reachabilityareas[i].firstarea = numreachareas; - aasworld.reachabilityareas[i].numareas = numareas; - for (j = 0; j < numareas; j++) - { - aasworld.reachabilityareaindex[numreachareas++] = areas[j]; - } //end for - } //end for -} //end of the function AAS_InitReachabilityAreas -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitRouting(void) -{ - AAS_InitTravelFlagFromType(); - // - AAS_InitAreaContentsTravelFlags(); - //initialize the routing update fields - AAS_InitRoutingUpdate(); - //create reversed reachability links used by the routing update algorithm - AAS_CreateReversedReachability(); - //initialize the cluster cache - AAS_InitClusterAreaCache(); - //initialize portal cache - AAS_InitPortalCache(); - //initialize the area travel times - AAS_CalculateAreaTravelTimes(); - //calculate the maximum travel times through portals - AAS_InitPortalMaxTravelTimes(); - //get the areas reachabilities go through - AAS_InitReachabilityAreas(); - // -#ifdef ROUTING_DEBUG - numareacacheupdates = 0; - numportalcacheupdates = 0; -#endif //ROUTING_DEBUG - // - routingcachesize = 0; - max_routingcachesize = 1024 * (int) LibVarValue("max_routingcache", "4096"); - // read any routing cache if available - AAS_ReadRouteCache(); -} //end of the function AAS_InitRouting -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeRoutingCaches(void) -{ - // free all the existing cluster area cache - AAS_FreeAllClusterAreaCache(); - // free all the existing portal cache - AAS_FreeAllPortalCache(); - // free cached travel times within areas - if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes); - aasworld.areatraveltimes = NULL; - // free cached maximum travel time through cluster portals - if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes); - aasworld.portalmaxtraveltimes = NULL; - // free reversed reachability links - if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability); - aasworld.reversedreachability = NULL; - // free routing algorithm memory - if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate); - aasworld.areaupdate = NULL; - if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate); - aasworld.portalupdate = NULL; - // free lists with areas the reachabilities go through - if (aasworld.reachabilityareas) FreeMemory(aasworld.reachabilityareas); - aasworld.reachabilityareas = NULL; - // free the reachability area index - if (aasworld.reachabilityareaindex) FreeMemory(aasworld.reachabilityareaindex); - aasworld.reachabilityareaindex = NULL; - // free area contents travel flags look up table - if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags); - aasworld.areacontentstravelflags = NULL; -} //end of the function AAS_FreeRoutingCaches -//=========================================================================== -// update the given routing cache -// -// Parameter: areacache : routing cache to update -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_UpdateAreaRoutingCache(aas_routingcache_t *areacache) -{ - int i, nextareanum, cluster, badtravelflags, clusterareanum, linknum; - int numreachabilityareas; - unsigned short int t, startareatraveltimes[128]; //NOTE: not more than 128 reachabilities per area allowed - aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; - aas_reachability_t *reach; - aas_reversedreachability_t *revreach; - aas_reversedlink_t *revlink; - -#ifdef ROUTING_DEBUG - numareacacheupdates++; -#endif //ROUTING_DEBUG - //number of reachability areas within this cluster - numreachabilityareas = aasworld.clusters[areacache->cluster].numreachabilityareas; - // - aasworld.frameroutingupdates++; - //clear the routing update fields -// Com_Memset(aasworld.areaupdate, 0, aasworld.numareas * sizeof(aas_routingupdate_t)); - // - badtravelflags = ~areacache->travelflags; - // - clusterareanum = AAS_ClusterAreaNum(areacache->cluster, areacache->areanum); - if (clusterareanum >= numreachabilityareas) return; - // - Com_Memset(startareatraveltimes, 0, sizeof(startareatraveltimes)); - // - curupdate = &aasworld.areaupdate[clusterareanum]; - curupdate->areanum = areacache->areanum; - //VectorCopy(areacache->origin, curupdate->start); - curupdate->areatraveltimes = startareatraveltimes; - curupdate->tmptraveltime = areacache->starttraveltime; - // - areacache->traveltimes[clusterareanum] = areacache->starttraveltime; - //put the area to start with in the current read list - curupdate->next = NULL; - curupdate->prev = NULL; - updateliststart = curupdate; - updatelistend = curupdate; - //while there are updates in the current list - while (updateliststart) - { - curupdate = updateliststart; - // - if (curupdate->next) curupdate->next->prev = NULL; - else updatelistend = NULL; - updateliststart = curupdate->next; - // - curupdate->inlist = qfalse; - //check all reversed reachability links - revreach = &aasworld.reversedreachability[curupdate->areanum]; - // - for (i = 0, revlink = revreach->first; revlink; revlink = revlink->next, i++) - { - linknum = revlink->linknum; - reach = &aasworld.reachability[linknum]; - //if there is used an undesired travel type - if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue; - //if not allowed to enter the next area - if (aasworld.areasettings[reach->areanum].areaflags & AREA_DISABLED) continue; - //if the next area has a not allowed travel flag - if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue; - //number of the area the reversed reachability leads to - nextareanum = revlink->areanum; - //get the cluster number of the area - cluster = aasworld.areasettings[nextareanum].cluster; - //don't leave the cluster - if (cluster > 0 && cluster != areacache->cluster) continue; - //get the number of the area in the cluster - clusterareanum = AAS_ClusterAreaNum(areacache->cluster, nextareanum); - if (clusterareanum >= numreachabilityareas) continue; - //time already travelled plus the traveltime through - //the current area plus the travel time from the reachability - t = curupdate->tmptraveltime + - //AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->end) + - curupdate->areatraveltimes[i] + - reach->traveltime; - // - if (!areacache->traveltimes[clusterareanum] || - areacache->traveltimes[clusterareanum] > t) - { - areacache->traveltimes[clusterareanum] = t; - areacache->reachabilities[clusterareanum] = linknum - aasworld.areasettings[nextareanum].firstreachablearea; - nextupdate = &aasworld.areaupdate[clusterareanum]; - nextupdate->areanum = nextareanum; - nextupdate->tmptraveltime = t; - //VectorCopy(reach->start, nextupdate->start); - nextupdate->areatraveltimes = aasworld.areatraveltimes[nextareanum][linknum - - aasworld.areasettings[nextareanum].firstreachablearea]; - if (!nextupdate->inlist) - { - // we add the update to the end of the list - // we could also use a B+ tree to have a real sorted list - // on travel time which makes for faster routing updates - nextupdate->next = NULL; - nextupdate->prev = updatelistend; - if (updatelistend) updatelistend->next = nextupdate; - else updateliststart = nextupdate; - updatelistend = nextupdate; - nextupdate->inlist = qtrue; - } //end if - } //end if - } //end for - } //end while -} //end of the function AAS_UpdateAreaRoutingCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_routingcache_t *AAS_GetAreaRoutingCache(int clusternum, int areanum, int travelflags) -{ - int clusterareanum; - aas_routingcache_t *cache, *clustercache; - - //number of the area in the cluster - clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); - //pointer to the cache for the area in the cluster - clustercache = aasworld.clusterareacache[clusternum][clusterareanum]; - //find the cache without undesired travel flags - for (cache = clustercache; cache; cache = cache->next) - { - //if there aren't used any undesired travel types for the cache - if (cache->travelflags == travelflags) break; - } //end for - //if there was no cache - if (!cache) - { - cache = AAS_AllocRoutingCache(aasworld.clusters[clusternum].numreachabilityareas); - cache->cluster = clusternum; - cache->areanum = areanum; - VectorCopy(aasworld.areas[areanum].center, cache->origin); - cache->starttraveltime = 1; - cache->travelflags = travelflags; - cache->prev = NULL; - cache->next = clustercache; - if (clustercache) clustercache->prev = cache; - aasworld.clusterareacache[clusternum][clusterareanum] = cache; - AAS_UpdateAreaRoutingCache(cache); - } //end if - else - { - AAS_UnlinkCache(cache); - } //end else - //the cache has been accessed - cache->time = AAS_RoutingTime(); - cache->type = CACHETYPE_AREA; - AAS_LinkCache(cache); - return cache; -} //end of the function AAS_GetAreaRoutingCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_UpdatePortalRoutingCache(aas_routingcache_t *portalcache) -{ - int i, portalnum, clusterareanum, clusternum; - unsigned short int t; - aas_portal_t *portal; - aas_cluster_t *cluster; - aas_routingcache_t *cache; - aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; - -#ifdef ROUTING_DEBUG - numportalcacheupdates++; -#endif //ROUTING_DEBUG - //clear the routing update fields -// Com_Memset(aasworld.portalupdate, 0, (aasworld.numportals+1) * sizeof(aas_routingupdate_t)); - // - curupdate = &aasworld.portalupdate[aasworld.numportals]; - curupdate->cluster = portalcache->cluster; - curupdate->areanum = portalcache->areanum; - curupdate->tmptraveltime = portalcache->starttraveltime; - //if the start area is a cluster portal, store the travel time for that portal - clusternum = aasworld.areasettings[portalcache->areanum].cluster; - if (clusternum < 0) - { - portalcache->traveltimes[-clusternum] = portalcache->starttraveltime; - } //end if - //put the area to start with in the current read list - curupdate->next = NULL; - curupdate->prev = NULL; - updateliststart = curupdate; - updatelistend = curupdate; - //while there are updates in the current list - while (updateliststart) - { - curupdate = updateliststart; - //remove the current update from the list - if (curupdate->next) curupdate->next->prev = NULL; - else updatelistend = NULL; - updateliststart = curupdate->next; - //current update is removed from the list - curupdate->inlist = qfalse; - // - cluster = &aasworld.clusters[curupdate->cluster]; - // - cache = AAS_GetAreaRoutingCache(curupdate->cluster, - curupdate->areanum, portalcache->travelflags); - //take all portals of the cluster - for (i = 0; i < cluster->numportals; i++) - { - portalnum = aasworld.portalindex[cluster->firstportal + i]; - portal = &aasworld.portals[portalnum]; - //if this is the portal of the current update continue - if (portal->areanum == curupdate->areanum) continue; - // - clusterareanum = AAS_ClusterAreaNum(curupdate->cluster, portal->areanum); - if (clusterareanum >= cluster->numreachabilityareas) continue; - // - t = cache->traveltimes[clusterareanum]; - if (!t) continue; - t += curupdate->tmptraveltime; - // - if (!portalcache->traveltimes[portalnum] || - portalcache->traveltimes[portalnum] > t) - { - portalcache->traveltimes[portalnum] = t; - nextupdate = &aasworld.portalupdate[portalnum]; - if (portal->frontcluster == curupdate->cluster) - { - nextupdate->cluster = portal->backcluster; - } //end if - else - { - nextupdate->cluster = portal->frontcluster; - } //end else - nextupdate->areanum = portal->areanum; - //add travel time through the actual portal area for the next update - nextupdate->tmptraveltime = t + aasworld.portalmaxtraveltimes[portalnum]; - if (!nextupdate->inlist) - { - // we add the update to the end of the list - // we could also use a B+ tree to have a real sorted list - // on travel time which makes for faster routing updates - nextupdate->next = NULL; - nextupdate->prev = updatelistend; - if (updatelistend) updatelistend->next = nextupdate; - else updateliststart = nextupdate; - updatelistend = nextupdate; - nextupdate->inlist = qtrue; - } //end if - } //end if - } //end for - } //end while -} //end of the function AAS_UpdatePortalRoutingCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_routingcache_t *AAS_GetPortalRoutingCache(int clusternum, int areanum, int travelflags) -{ - aas_routingcache_t *cache; - - //find the cached portal routing if existing - for (cache = aasworld.portalcache[areanum]; cache; cache = cache->next) - { - if (cache->travelflags == travelflags) break; - } //end for - //if the portal routing isn't cached - if (!cache) - { - cache = AAS_AllocRoutingCache(aasworld.numportals); - cache->cluster = clusternum; - cache->areanum = areanum; - VectorCopy(aasworld.areas[areanum].center, cache->origin); - cache->starttraveltime = 1; - cache->travelflags = travelflags; - //add the cache to the cache list - cache->prev = NULL; - cache->next = aasworld.portalcache[areanum]; - if (aasworld.portalcache[areanum]) aasworld.portalcache[areanum]->prev = cache; - aasworld.portalcache[areanum] = cache; - //update the cache - AAS_UpdatePortalRoutingCache(cache); - } //end if - else - { - AAS_UnlinkCache(cache); - } //end else - //the cache has been accessed - cache->time = AAS_RoutingTime(); - cache->type = CACHETYPE_PORTAL; - AAS_LinkCache(cache); - return cache; -} //end of the function AAS_GetPortalRoutingCache -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, int *reachnum) -{ - int clusternum, goalclusternum, portalnum, i, clusterareanum, bestreachnum; - unsigned short int t, besttime; - aas_portal_t *portal; - aas_cluster_t *cluster; - aas_routingcache_t *areacache, *portalcache; - aas_reachability_t *reach; - - if (!aasworld.initialized) return qfalse; - - if (areanum == goalareanum) - { - *traveltime = 1; - *reachnum = 0; - return qtrue; - } - // - if (areanum <= 0 || areanum >= aasworld.numareas) - { - if (bot_developer) - { - botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: areanum %d out of range\n", areanum); - } //end if - return qfalse; - } //end if - if (goalareanum <= 0 || goalareanum >= aasworld.numareas) - { - if (bot_developer) - { - botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: goalareanum %d out of range\n", goalareanum); - } //end if - return qfalse; - } //end if - // make sure the routing cache doesn't grow to large - while(AvailableMemory() < 1 * 1024 * 1024) { - if (!AAS_FreeOldestCache()) break; - } - // - if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goalareanum)) - { - travelflags |= TFL_DONOTENTER; - } //end if - //NOTE: the number of routing updates is limited per frame - /* - if (aasworld.frameroutingupdates > MAX_FRAMEROUTINGUPDATES) - { -#ifdef DEBUG - //Log_Write("WARNING: AAS_AreaTravelTimeToGoalArea: frame routing updates overflowed"); -#endif - return 0; - } //end if - */ - // - clusternum = aasworld.areasettings[areanum].cluster; - goalclusternum = aasworld.areasettings[goalareanum].cluster; - //check if the area is a portal of the goal area cluster - if (clusternum < 0 && goalclusternum > 0) - { - portal = &aasworld.portals[-clusternum]; - if (portal->frontcluster == goalclusternum || - portal->backcluster == goalclusternum) - { - clusternum = goalclusternum; - } //end if - } //end if - //check if the goalarea is a portal of the area cluster - else if (clusternum > 0 && goalclusternum < 0) - { - portal = &aasworld.portals[-goalclusternum]; - if (portal->frontcluster == clusternum || - portal->backcluster == clusternum) - { - goalclusternum = clusternum; - } //end if - } //end if - //if both areas are in the same cluster - //NOTE: there might be a shorter route via another cluster!!! but we don't care - if (clusternum > 0 && goalclusternum > 0 && clusternum == goalclusternum) - { - // - areacache = AAS_GetAreaRoutingCache(clusternum, goalareanum, travelflags); - //the number of the area in the cluster - clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); - //the cluster the area is in - cluster = &aasworld.clusters[clusternum]; - //if the area is NOT a reachability area - if (clusterareanum >= cluster->numreachabilityareas) return 0; - //if it is possible to travel to the goal area through this cluster - if (areacache->traveltimes[clusterareanum] != 0) - { - *reachnum = aasworld.areasettings[areanum].firstreachablearea + - areacache->reachabilities[clusterareanum]; - if (!origin) { - *traveltime = areacache->traveltimes[clusterareanum]; - return qtrue; - } - reach = &aasworld.reachability[*reachnum]; - *traveltime = areacache->traveltimes[clusterareanum] + - AAS_AreaTravelTime(areanum, origin, reach->start); - // - return qtrue; - } //end if - } //end if - // - clusternum = aasworld.areasettings[areanum].cluster; - goalclusternum = aasworld.areasettings[goalareanum].cluster; - //if the goal area is a portal - if (goalclusternum < 0) - { - //just assume the goal area is part of the front cluster - portal = &aasworld.portals[-goalclusternum]; - goalclusternum = portal->frontcluster; - } //end if - //get the portal routing cache - portalcache = AAS_GetPortalRoutingCache(goalclusternum, goalareanum, travelflags); - //if the area is a cluster portal, read directly from the portal cache - if (clusternum < 0) - { - *traveltime = portalcache->traveltimes[-clusternum]; - *reachnum = aasworld.areasettings[areanum].firstreachablearea + - portalcache->reachabilities[-clusternum]; - return qtrue; - } //end if - // - besttime = 0; - bestreachnum = -1; - //the cluster the area is in - cluster = &aasworld.clusters[clusternum]; - //find the portal of the area cluster leading towards the goal area - for (i = 0; i < cluster->numportals; i++) - { - portalnum = aasworld.portalindex[cluster->firstportal + i]; - //if the goal area isn't reachable from the portal - if (!portalcache->traveltimes[portalnum]) continue; - // - portal = &aasworld.portals[portalnum]; - //get the cache of the portal area - areacache = AAS_GetAreaRoutingCache(clusternum, portal->areanum, travelflags); - //current area inside the current cluster - clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); - //if the area is NOT a reachability area - if (clusterareanum >= cluster->numreachabilityareas) continue; - //if the portal is NOT reachable from this area - if (!areacache->traveltimes[clusterareanum]) continue; - //total travel time is the travel time the portal area is from - //the goal area plus the travel time towards the portal area - t = portalcache->traveltimes[portalnum] + areacache->traveltimes[clusterareanum]; - //FIXME: add the exact travel time through the actual portal area - //NOTE: for now we just add the largest travel time through the portal area - // because we can't directly calculate the exact travel time - // to be more specific we don't know which reachability was used to travel - // into the portal area - t += aasworld.portalmaxtraveltimes[portalnum]; - // - if (origin) - { - *reachnum = aasworld.areasettings[areanum].firstreachablearea + - areacache->reachabilities[clusterareanum]; - reach = aasworld.reachability + *reachnum; - t += AAS_AreaTravelTime(areanum, origin, reach->start); - } //end if - //if the time is better than the one already found - if (!besttime || t < besttime) - { - bestreachnum = *reachnum; - besttime = t; - } //end if - } //end for - if (bestreachnum < 0) { - return qfalse; - } - *reachnum = bestreachnum; - *traveltime = besttime; - return qtrue; -} //end of the function AAS_AreaRouteToGoalArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags) -{ - int traveltime, reachnum; - - if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum)) - { - return traveltime; - } - return 0; -} //end of the function AAS_AreaTravelTimeToGoalArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaReachabilityToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags) -{ - int traveltime, reachnum; - - if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum)) - { - return reachnum; - } - return 0; -} //end of the function AAS_AreaReachabilityToGoalArea -//=========================================================================== -// predict the route and stop on one of the stop events -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin, - int goalareanum, int travelflags, int maxareas, int maxtime, - int stopevent, int stopcontents, int stoptfl, int stopareanum) -{ - int curareanum, reachnum, i, j, testareanum; - vec3_t curorigin; - aas_reachability_t *reach; - aas_reachabilityareas_t *reachareas; - - //init output - route->stopevent = RSE_NONE; - route->endarea = goalareanum; - route->endcontents = 0; - route->endtravelflags = 0; - VectorCopy(origin, route->endpos); - route->time = 0; - - curareanum = areanum; - VectorCopy(origin, curorigin); - - for (i = 0; curareanum != goalareanum && (!maxareas || i < maxareas) && i < aasworld.numareas; i++) - { - reachnum = AAS_AreaReachabilityToGoalArea(curareanum, curorigin, goalareanum, travelflags); - if (!reachnum) - { - route->stopevent = RSE_NOROUTE; - return qfalse; - } //end if - reach = &aasworld.reachability[reachnum]; - // - if (stopevent & RSE_USETRAVELTYPE) - { - if (AAS_TravelFlagForType_inline(reach->traveltype) & stoptfl) - { - route->stopevent = RSE_USETRAVELTYPE; - route->endarea = curareanum; - route->endcontents = aasworld.areasettings[curareanum].contents; - route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype); - VectorCopy(reach->start, route->endpos); - return qtrue; - } //end if - if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & stoptfl) - { - route->stopevent = RSE_USETRAVELTYPE; - route->endarea = reach->areanum; - route->endcontents = aasworld.areasettings[reach->areanum].contents; - route->endtravelflags = AAS_AreaContentsTravelFlags_inline(reach->areanum); - VectorCopy(reach->end, route->endpos); - route->time += AAS_AreaTravelTime(areanum, origin, reach->start); - route->time += reach->traveltime; - return qtrue; - } //end if - } //end if - reachareas = &aasworld.reachabilityareas[reachnum]; - for (j = 0; j < reachareas->numareas + 1; j++) - { - if (j >= reachareas->numareas) - testareanum = reach->areanum; - else - testareanum = aasworld.reachabilityareaindex[reachareas->firstarea + j]; - if (stopevent & RSE_ENTERCONTENTS) - { - if (aasworld.areasettings[testareanum].contents & stopcontents) - { - route->stopevent = RSE_ENTERCONTENTS; - route->endarea = testareanum; - route->endcontents = aasworld.areasettings[testareanum].contents; - VectorCopy(reach->end, route->endpos); - route->time += AAS_AreaTravelTime(areanum, origin, reach->start); - route->time += reach->traveltime; - return qtrue; - } //end if - } //end if - if (stopevent & RSE_ENTERAREA) - { - if (testareanum == stopareanum) - { - route->stopevent = RSE_ENTERAREA; - route->endarea = testareanum; - route->endcontents = aasworld.areasettings[testareanum].contents; - VectorCopy(reach->start, route->endpos); - return qtrue; - } //end if - } //end if - } //end for - - route->time += AAS_AreaTravelTime(areanum, origin, reach->start); - route->time += reach->traveltime; - route->endarea = reach->areanum; - route->endcontents = aasworld.areasettings[reach->areanum].contents; - route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype); - VectorCopy(reach->end, route->endpos); - // - curareanum = reach->areanum; - VectorCopy(reach->end, curorigin); - // - if (maxtime && route->time > maxtime) - break; - } //end while - if (curareanum != goalareanum) - return qfalse; - return qtrue; -} //end of the function AAS_PredictRoute -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_BridgeWalkable(int areanum) -{ - return qfalse; -} //end of the function AAS_BridgeWalkable -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach) -{ - if (!aasworld.initialized) - { - Com_Memset(reach, 0, sizeof(aas_reachability_t)); - return; - } //end if - if (num < 0 || num >= aasworld.reachabilitysize) - { - Com_Memset(reach, 0, sizeof(aas_reachability_t)); - return; - } //end if - Com_Memcpy(reach, &aasworld.reachability[num], sizeof(aas_reachability_t));; -} //end of the function AAS_ReachabilityFromNum -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_NextAreaReachability(int areanum, int reachnum) -{ - aas_areasettings_t *settings; - - if (!aasworld.initialized) return 0; - - if (areanum <= 0 || areanum >= aasworld.numareas) - { - botimport.Print(PRT_ERROR, "AAS_NextAreaReachability: areanum %d out of range\n", areanum); - return 0; - } //end if - - settings = &aasworld.areasettings[areanum]; - if (!reachnum) - { - return settings->firstreachablearea; - } //end if - if (reachnum < settings->firstreachablearea) - { - botimport.Print(PRT_FATAL, "AAS_NextAreaReachability: reachnum < settings->firstreachableara"); - return 0; - } //end if - reachnum++; - if (reachnum >= settings->firstreachablearea + settings->numreachableareas) - { - return 0; - } //end if - return reachnum; -} //end of the function AAS_NextAreaReachability -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_NextModelReachability(int num, int modelnum) -{ - int i; - - if (num <= 0) num = 1; - else if (num >= aasworld.reachabilitysize) return 0; - else num++; - // - for (i = num; i < aasworld.reachabilitysize; i++) - { - if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) - { - if (aasworld.reachability[i].facenum == modelnum) return i; - } //end if - else if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) - { - if ((aasworld.reachability[i].facenum & 0x0000FFFF) == modelnum) return i; - } //end if - } //end for - return 0; -} //end of the function AAS_NextModelReachability -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin) -{ - int i, n, t; - vec3_t start, end; - aas_trace_t trace; - - //if the area has no reachabilities - if (!AAS_AreaReachability(areanum)) return qfalse; - // - n = aasworld.numareas * random(); - for (i = 0; i < aasworld.numareas; i++) - { - if (n <= 0) n = 1; - if (n >= aasworld.numareas) n = 1; - if (AAS_AreaReachability(n)) - { - t = AAS_AreaTravelTimeToGoalArea(areanum, aasworld.areas[areanum].center, n, travelflags); - //if the goal is reachable - if (t > 0) - { - if (AAS_AreaSwim(n)) - { - *goalareanum = n; - VectorCopy(aasworld.areas[n].center, goalorigin); - //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum); - return qtrue; - } //end if - VectorCopy(aasworld.areas[n].center, start); - if (!AAS_PointAreaNum(start)) - Log_Write("area %d center %f %f %f in solid?", n, start[0], start[1], start[2]); - VectorCopy(start, end); - end[2] -= 300; - trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); - if (!trace.startsolid && trace.fraction < 1 && AAS_PointAreaNum(trace.endpos) == n) - { - if (AAS_AreaGroundFaceArea(n) > 300) - { - *goalareanum = n; - VectorCopy(trace.endpos, goalorigin); - //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum); - return qtrue; - } //end if - } //end if - } //end if - } //end if - n++; - } //end for - return qfalse; -} //end of the function AAS_RandomGoalArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaVisible(int srcarea, int destarea) -{ - return qfalse; -} //end of the function AAS_AreaVisible -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float DistancePointToLine(vec3_t v1, vec3_t v2, vec3_t point) -{ - vec3_t vec, p2; - - AAS_ProjectPointOntoVector(point, v1, v2, p2); - VectorSubtract(point, p2, vec); - return VectorLength(vec); -} //end of the function DistancePointToLine -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags) -{ - int i, j, nextareanum, badtravelflags, numreach, bestarea; - unsigned short int t, besttraveltime; - static unsigned short int *hidetraveltimes; - aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; - aas_reachability_t *reach; - float dist1, dist2; - vec3_t v1, v2, p; - qboolean startVisible; - - // - if (!hidetraveltimes) - { - hidetraveltimes = (unsigned short int *) GetClearedMemory(aasworld.numareas * sizeof(unsigned short int)); - } //end if - else - { - Com_Memset(hidetraveltimes, 0, aasworld.numareas * sizeof(unsigned short int)); - } //end else - besttraveltime = 0; - bestarea = 0; - //assume visible - startVisible = qtrue; - // - badtravelflags = ~travelflags; - // - curupdate = &aasworld.areaupdate[areanum]; - curupdate->areanum = areanum; - VectorCopy(origin, curupdate->start); - curupdate->areatraveltimes = aasworld.areatraveltimes[areanum][0]; - curupdate->tmptraveltime = 0; - //put the area to start with in the current read list - curupdate->next = NULL; - curupdate->prev = NULL; - updateliststart = curupdate; - updatelistend = curupdate; - //while there are updates in the list - while (updateliststart) - { - curupdate = updateliststart; - // - if (curupdate->next) curupdate->next->prev = NULL; - else updatelistend = NULL; - updateliststart = curupdate->next; - // - curupdate->inlist = qfalse; - //check all reversed reachability links - numreach = aasworld.areasettings[curupdate->areanum].numreachableareas; - reach = &aasworld.reachability[aasworld.areasettings[curupdate->areanum].firstreachablearea]; - // - for (i = 0; i < numreach; i++, reach++) - { - //if an undesired travel type is used - if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue; - // - if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue; - //number of the area the reachability leads to - nextareanum = reach->areanum; - // if this moves us into the enemies area, skip it - if (nextareanum == enemyareanum) continue; - //time already travelled plus the traveltime through - //the current area plus the travel time from the reachability - t = curupdate->tmptraveltime + - AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) + - reach->traveltime; - - //avoid going near the enemy - AAS_ProjectPointOntoVector(enemyorigin, curupdate->start, reach->end, p); - for (j = 0; j < 3; j++) - if ((p[j] > curupdate->start[j] && p[j] > reach->end[j]) || - (p[j] < curupdate->start[j] && p[j] < reach->end[j])) - break; - if (j < 3) - { - VectorSubtract(enemyorigin, reach->end, v2); - } //end if - else - { - VectorSubtract(enemyorigin, p, v2); - } //end else - dist2 = VectorLength(v2); - //never go through the enemy - if (dist2 < 40) continue; - // - VectorSubtract(enemyorigin, curupdate->start, v1); - dist1 = VectorLength(v1); - // - if (dist2 < dist1) - { - t += (dist1 - dist2) * 10; - } - // if we weren't visible when starting, make sure we don't move into their view - if (!startVisible && AAS_AreaVisible(enemyareanum, nextareanum)) { - continue; - } - // - if (besttraveltime && t >= besttraveltime) continue; - // - if (!hidetraveltimes[nextareanum] || - hidetraveltimes[nextareanum] > t) - { - //if the nextarea is not visible from the enemy area - if (!AAS_AreaVisible(enemyareanum, nextareanum)) - { - besttraveltime = t; - bestarea = nextareanum; - } //end if - hidetraveltimes[nextareanum] = t; - nextupdate = &aasworld.areaupdate[nextareanum]; - nextupdate->areanum = nextareanum; - nextupdate->tmptraveltime = t; - //remember where we entered this area - VectorCopy(reach->end, nextupdate->start); - //if this update is not in the list yet - if (!nextupdate->inlist) - { - //add the new update to the end of the list - nextupdate->next = NULL; - nextupdate->prev = updatelistend; - if (updatelistend) updatelistend->next = nextupdate; - else updateliststart = nextupdate; - updatelistend = nextupdate; - nextupdate->inlist = qtrue; - } //end if - } //end if - } //end for - } //end while - return bestarea; -} //end of the function AAS_NearestHideArea diff --git a/tools/quake3/bspc/deps/botlib/be_aas_route.h b/tools/quake3/bspc/deps/botlib/be_aas_route.h deleted file mode 100644 index 8805c66f..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_route.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_route.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_route.h $ - * - *****************************************************************************/ - -#ifdef AASINTERN -//initialize the AAS routing -void AAS_InitRouting(void); -//free the AAS routing caches -void AAS_FreeRoutingCaches(void); -//returns the travel time from start to end in the given area -unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end); -// -void AAS_CreateAllRoutingCache(void); -void AAS_WriteRouteCache(void); -// -void AAS_RoutingInfo(void); -#endif //AASINTERN - -//returns the travel flag for the given travel type -int AAS_TravelFlagForType(int traveltype); -//return the travel flag(s) for traveling through this area -int AAS_AreaContentsTravelFlags(int areanum); -//returns the index of the next reachability for the given area -int AAS_NextAreaReachability(int areanum, int reachnum); -//returns the reachability with the given index -void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach); -//returns a random goal area and goal origin -int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin); -//enable or disable an area for routing -int AAS_EnableRoutingArea(int areanum, int enable); -//returns the travel time within the given area from start to end -unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end); -//returns the travel time from the area to the goal area using the given travel flags -int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags); -//predict a route up to a stop event -int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin, - int goalareanum, int travelflags, int maxareas, int maxtime, - int stopevent, int stopcontents, int stoptfl, int stopareanum); - - diff --git a/tools/quake3/bspc/deps/botlib/be_aas_routealt.c b/tools/quake3/bspc/deps/botlib/be_aas_routealt.c deleted file mode 100644 index 1bdc5892..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_routealt.c +++ /dev/null @@ -1,240 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_routealt.c - * - * desc: AAS - * - * $Archive: /MissionPack/code/botlib/be_aas_routealt.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_utils.h" -#include "l_memory.h" -#include "l_log.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_aas_def.h" - -#define ENABLE_ALTROUTING -//#define ALTROUTE_DEBUG - -typedef struct midrangearea_s -{ - int valid; - unsigned short starttime; - unsigned short goaltime; -} midrangearea_t; - -midrangearea_t *midrangeareas; -int *clusterareas; -int numclusterareas; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_AltRoutingFloodCluster_r(int areanum) -{ - int i, otherareanum; - aas_area_t *area; - aas_face_t *face; - - //add the current area to the areas of the current cluster - clusterareas[numclusterareas] = areanum; - numclusterareas++; - //remove the area from the mid range areas - midrangeareas[areanum].valid = qfalse; - //flood to other areas through the faces of this area - area = &aasworld.areas[areanum]; - for (i = 0; i < area->numfaces; i++) - { - face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; - //get the area at the other side of the face - if (face->frontarea == areanum) otherareanum = face->backarea; - else otherareanum = face->frontarea; - //if there is an area at the other side of this face - if (!otherareanum) continue; - //if the other area is not a midrange area - if (!midrangeareas[otherareanum].valid) continue; - // - AAS_AltRoutingFloodCluster_r(otherareanum); - } //end for -} //end of the function AAS_AltRoutingFloodCluster_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, - aas_altroutegoal_t *altroutegoals, int maxaltroutegoals, - int type) -{ -#ifndef ENABLE_ALTROUTING - return 0; -#else - int i, j, bestareanum; - int numaltroutegoals, nummidrangeareas; - int starttime, goaltime, goaltraveltime; - float dist, bestdist; - vec3_t mid, dir; -#ifdef ALTROUTE_DEBUG - int startmillisecs; - - startmillisecs = Sys_MilliSeconds(); -#endif - - if (!startareanum || !goalareanum) - return 0; - //travel time towards the goal area - goaltraveltime = AAS_AreaTravelTimeToGoalArea(startareanum, start, goalareanum, travelflags); - //clear the midrange areas - Com_Memset(midrangeareas, 0, aasworld.numareas * sizeof(midrangearea_t)); - numaltroutegoals = 0; - // - nummidrangeareas = 0; - // - for (i = 1; i < aasworld.numareas; i++) - { - // - if (!(type & ALTROUTEGOAL_ALL)) - { - if (!(type & ALTROUTEGOAL_CLUSTERPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL))) - { - if (!(type & ALTROUTEGOAL_VIEWPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL))) - { - continue; - } //end if - } //end if - } //end if - //if the area has no reachabilities - if (!AAS_AreaReachability(i)) continue; - //tavel time from the area to the start area - starttime = AAS_AreaTravelTimeToGoalArea(startareanum, start, i, travelflags); - if (!starttime) continue; - //if the travel time from the start to the area is greater than the shortest goal travel time - if (starttime > (float) 1.1 * goaltraveltime) continue; - //travel time from the area to the goal area - goaltime = AAS_AreaTravelTimeToGoalArea(i, NULL, goalareanum, travelflags); - if (!goaltime) continue; - //if the travel time from the area to the goal is greater than the shortest goal travel time - if (goaltime > (float) 0.8 * goaltraveltime) continue; - //this is a mid range area - midrangeareas[i].valid = qtrue; - midrangeareas[i].starttime = starttime; - midrangeareas[i].goaltime = goaltime; - Log_Write("%d midrange area %d", nummidrangeareas, i); - nummidrangeareas++; - } //end for - // - for (i = 1; i < aasworld.numareas; i++) - { - if (!midrangeareas[i].valid) continue; - //get the areas in one cluster - numclusterareas = 0; - AAS_AltRoutingFloodCluster_r(i); - //now we've got a cluster with areas through which an alternative route could go - //get the 'center' of the cluster - VectorClear(mid); - for (j = 0; j < numclusterareas; j++) - { - VectorAdd(mid, aasworld.areas[clusterareas[j]].center, mid); - } //end for - VectorScale(mid, 1.0 / numclusterareas, mid); - //get the area closest to the center of the cluster - bestdist = 999999; - bestareanum = 0; - for (j = 0; j < numclusterareas; j++) - { - VectorSubtract(mid, aasworld.areas[clusterareas[j]].center, dir); - dist = VectorLength(dir); - if (dist < bestdist) - { - bestdist = dist; - bestareanum = clusterareas[j]; - } //end if - } //end for - //now we've got an area for an alternative route - //FIXME: add alternative goal origin - VectorCopy(aasworld.areas[bestareanum].center, altroutegoals[numaltroutegoals].origin); - altroutegoals[numaltroutegoals].areanum = bestareanum; - altroutegoals[numaltroutegoals].starttraveltime = midrangeareas[bestareanum].starttime; - altroutegoals[numaltroutegoals].goaltraveltime = midrangeareas[bestareanum].goaltime; - altroutegoals[numaltroutegoals].extratraveltime = - (midrangeareas[bestareanum].starttime + midrangeareas[bestareanum].goaltime) - - goaltraveltime; - numaltroutegoals++; - // -#ifdef ALTROUTE_DEBUG - AAS_ShowAreaPolygons(bestareanum, 1, qtrue); -#endif - //don't return more than the maximum alternative route goals - if (numaltroutegoals >= maxaltroutegoals) break; - } //end for -#ifdef ALTROUTE_DEBUG - botimport.Print(PRT_MESSAGE, "alternative route goals in %d msec\n", Sys_MilliSeconds() - startmillisecs); -#endif - return numaltroutegoals; -#endif -} //end of the function AAS_AlternativeRouteGoals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitAlternativeRouting(void) -{ -#ifdef ENABLE_ALTROUTING - if (midrangeareas) FreeMemory(midrangeareas); - midrangeareas = (midrangearea_t *) GetMemory(aasworld.numareas * sizeof(midrangearea_t)); - if (clusterareas) FreeMemory(clusterareas); - clusterareas = (int *) GetMemory(aasworld.numareas * sizeof(int)); -#endif -} //end of the function AAS_InitAlternativeRouting -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ShutdownAlternativeRouting(void) -{ -#ifdef ENABLE_ALTROUTING - if (midrangeareas) FreeMemory(midrangeareas); - midrangeareas = NULL; - if (clusterareas) FreeMemory(clusterareas); - clusterareas = NULL; - numclusterareas = 0; -#endif -} //end of the function AAS_ShutdownAlternativeRouting diff --git a/tools/quake3/bspc/deps/botlib/be_aas_routealt.h b/tools/quake3/bspc/deps/botlib/be_aas_routealt.h deleted file mode 100644 index 160966a9..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_routealt.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_routealt.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_routealt.h $ - * - *****************************************************************************/ - -#ifdef AASINTERN -void AAS_InitAlternativeRouting(void); -void AAS_ShutdownAlternativeRouting(void); -#endif //AASINTERN - - -int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, - aas_altroutegoal_t *altroutegoals, int maxaltroutegoals, - int type); diff --git a/tools/quake3/bspc/deps/botlib/be_aas_sample.c b/tools/quake3/bspc/deps/botlib/be_aas_sample.c deleted file mode 100644 index 2e207b2f..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_sample.c +++ /dev/null @@ -1,1394 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_sample.c - * - * desc: AAS environment sampling - * - * $Archive: /MissionPack/code/botlib/be_aas_sample.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#ifndef BSPC -#include "l_libvar.h" -#endif -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_interface.h" -#include "be_aas_funcs.h" -#include "be_aas_def.h" - -extern botlib_import_t botimport; - -//#define AAS_SAMPLE_DEBUG - -#define BBOX_NORMAL_EPSILON 0.001 - -#define ON_EPSILON 0 //0.0005 - -#define TRACEPLANE_EPSILON 0.125 - -typedef struct aas_tracestack_s -{ - vec3_t start; //start point of the piece of line to trace - vec3_t end; //end point of the piece of line to trace - int planenum; //last plane used as splitter - int nodenum; //node found after splitting with planenum -} aas_tracestack_t; - -int numaaslinks; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs) -{ - int index; - //bounding box size for each presence type - vec3_t boxmins[3] = {{0, 0, 0}, {-15, -15, -24}, {-15, -15, -24}}; - vec3_t boxmaxs[3] = {{0, 0, 0}, { 15, 15, 32}, { 15, 15, 8}}; - - if (presencetype == PRESENCE_NORMAL) index = 1; - else if (presencetype == PRESENCE_CROUCH) index = 2; - else - { - botimport.Print(PRT_FATAL, "AAS_PresenceTypeBoundingBox: unknown presence type\n"); - index = 2; - } //end if - VectorCopy(boxmins[index], mins); - VectorCopy(boxmaxs[index], maxs); -} //end of the function AAS_PresenceTypeBoundingBox -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitAASLinkHeap(void) -{ - int i, max_aaslinks; - - max_aaslinks = aasworld.linkheapsize; - //if there's no link heap present - if (!aasworld.linkheap) - { -#ifdef BSPC - max_aaslinks = 6144; -#else - max_aaslinks = (int) LibVarValue("max_aaslinks", "6144"); -#endif - if (max_aaslinks < 0) max_aaslinks = 0; - aasworld.linkheapsize = max_aaslinks; - aasworld.linkheap = (aas_link_t *) GetHunkMemory(max_aaslinks * sizeof(aas_link_t)); - } //end if - //link the links on the heap - aasworld.linkheap[0].prev_ent = NULL; - aasworld.linkheap[0].next_ent = &aasworld.linkheap[1]; - for (i = 1; i < max_aaslinks-1; i++) - { - aasworld.linkheap[i].prev_ent = &aasworld.linkheap[i - 1]; - aasworld.linkheap[i].next_ent = &aasworld.linkheap[i + 1]; - } //end for - aasworld.linkheap[max_aaslinks-1].prev_ent = &aasworld.linkheap[max_aaslinks-2]; - aasworld.linkheap[max_aaslinks-1].next_ent = NULL; - //pointer to the first free link - aasworld.freelinks = &aasworld.linkheap[0]; - // - numaaslinks = max_aaslinks; -} //end of the function AAS_InitAASLinkHeap -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeAASLinkHeap(void) -{ - if (aasworld.linkheap) FreeMemory(aasworld.linkheap); - aasworld.linkheap = NULL; - aasworld.linkheapsize = 0; -} //end of the function AAS_FreeAASLinkHeap -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_link_t *AAS_AllocAASLink(void) -{ - aas_link_t *link; - - link = aasworld.freelinks; - if (!link) - { -#ifndef BSPC - if (bot_developer) -#endif - { - botimport.Print(PRT_FATAL, "empty aas link heap\n"); - } //end if - return NULL; - } //end if - if (aasworld.freelinks) aasworld.freelinks = aasworld.freelinks->next_ent; - if (aasworld.freelinks) aasworld.freelinks->prev_ent = NULL; - numaaslinks--; - return link; -} //end of the function AAS_AllocAASLink -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_DeAllocAASLink(aas_link_t *link) -{ - if (aasworld.freelinks) aasworld.freelinks->prev_ent = link; - link->prev_ent = NULL; - link->next_ent = aasworld.freelinks; - link->prev_area = NULL; - link->next_area = NULL; - aasworld.freelinks = link; - numaaslinks++; -} //end of the function AAS_DeAllocAASLink -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_InitAASLinkedEntities(void) -{ - if (!aasworld.loaded) return; - if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities); - aasworld.arealinkedentities = (aas_link_t **) GetClearedHunkMemory( - aasworld.numareas * sizeof(aas_link_t *)); -} //end of the function AAS_InitAASLinkedEntities -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FreeAASLinkedEntities(void) -{ - if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities); - aasworld.arealinkedentities = NULL; -} //end of the function AAS_InitAASLinkedEntities -//=========================================================================== -// returns the AAS area the point is in -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_PointAreaNum(vec3_t point) -{ - int nodenum; - vec_t dist; - aas_node_t *node; - aas_plane_t *plane; - - if (!aasworld.loaded) - { - botimport.Print(PRT_ERROR, "AAS_PointAreaNum: aas not loaded\n"); - return 0; - } //end if - - //start with node 1 because node zero is a dummy used for solid leafs - nodenum = 1; - while (nodenum > 0) - { -// botimport.Print(PRT_MESSAGE, "[%d]", nodenum); -#ifdef AAS_SAMPLE_DEBUG - if (nodenum >= aasworld.numnodes) - { - botimport.Print(PRT_ERROR, "nodenum = %d >= aasworld.numnodes = %d\n", nodenum, aasworld.numnodes); - return 0; - } //end if -#endif //AAS_SAMPLE_DEBUG - node = &aasworld.nodes[nodenum]; -#ifdef AAS_SAMPLE_DEBUG - if (node->planenum < 0 || node->planenum >= aasworld.numplanes) - { - botimport.Print(PRT_ERROR, "node->planenum = %d >= aasworld.numplanes = %d\n", node->planenum, aasworld.numplanes); - return 0; - } //end if -#endif //AAS_SAMPLE_DEBUG - plane = &aasworld.planes[node->planenum]; - dist = DotProduct(point, plane->normal) - plane->dist; - if (dist > 0) nodenum = node->children[0]; - else nodenum = node->children[1]; - } //end while - if (!nodenum) - { -#ifdef AAS_SAMPLE_DEBUG - botimport.Print(PRT_MESSAGE, "in solid\n"); -#endif //AAS_SAMPLE_DEBUG - return 0; - } //end if - return -nodenum; -} //end of the function AAS_PointAreaNum -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_PointReachabilityAreaIndex( vec3_t origin ) -{ - int areanum, cluster, i, index; - - if (!aasworld.initialized) - return 0; - - if ( !origin ) - { - index = 0; - for (i = 0; i < aasworld.numclusters; i++) - { - index += aasworld.clusters[i].numreachabilityareas; - } //end for - return index; - } //end if - - areanum = AAS_PointAreaNum( origin ); - if ( !areanum || !AAS_AreaReachability(areanum) ) - return 0; - cluster = aasworld.areasettings[areanum].cluster; - areanum = aasworld.areasettings[areanum].clusterareanum; - if (cluster < 0) - { - cluster = aasworld.portals[-cluster].frontcluster; - areanum = aasworld.portals[-cluster].clusterareanum[0]; - } //end if - - index = 0; - for (i = 0; i < cluster; i++) - { - index += aasworld.clusters[i].numreachabilityareas; - } //end for - index += areanum; - return index; -} //end of the function AAS_PointReachabilityAreaIndex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaCluster(int areanum) -{ - if (areanum <= 0 || areanum >= aasworld.numareas) - { - botimport.Print(PRT_ERROR, "AAS_AreaCluster: invalid area number\n"); - return 0; - } //end if - return aasworld.areasettings[areanum].cluster; -} //end of the function AAS_AreaCluster -//=========================================================================== -// returns the presence types of the given area -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaPresenceType(int areanum) -{ - if (!aasworld.loaded) return 0; - if (areanum <= 0 || areanum >= aasworld.numareas) - { - botimport.Print(PRT_ERROR, "AAS_AreaPresenceType: invalid area number\n"); - return 0; - } //end if - return aasworld.areasettings[areanum].presencetype; -} //end of the function AAS_AreaPresenceType -//=========================================================================== -// returns the presence type at the given point -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_PointPresenceType(vec3_t point) -{ - int areanum; - - if (!aasworld.loaded) return 0; - - areanum = AAS_PointAreaNum(point); - if (!areanum) return PRESENCE_NONE; - return aasworld.areasettings[areanum].presencetype; -} //end of the function AAS_PointPresenceType -//=========================================================================== -// calculates the minimum distance between the origin of the box and the -// given plane when both will collide on the given side of the plane -// -// normal = normal vector of plane to calculate distance from -// mins = minimums of box relative to origin -// maxs = maximums of box relative to origin -// side = side of the plane we want to calculate the distance from -// 0 normal vector side -// 1 not normal vector side -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -vec_t AAS_BoxOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs, int side) -{ - vec3_t v1, v2; - int i; - - //swap maxs and mins when on the other side of the plane - if (side) - { - //get a point of the box that would be one of the first - //to collide with the plane - for (i = 0; i < 3; i++) - { - if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = maxs[i]; - else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = mins[i]; - else v1[i] = 0; - } //end for - } //end if - else - { - //get a point of the box that would be one of the first - //to collide with the plane - for (i = 0; i < 3; i++) - { - if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = mins[i]; - else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = maxs[i]; - else v1[i] = 0; - } //end for - } //end else - // - VectorCopy(normal, v2); - VectorInverse(v2); -// VectorNegate(normal, v2); - return DotProduct(v1, v2); -} //end of the function AAS_BoxOriginDistanceFromPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_AreaEntityCollision(int areanum, vec3_t start, vec3_t end, - int presencetype, int passent, aas_trace_t *trace) -{ - int collision; - vec3_t boxmins, boxmaxs; - aas_link_t *link; - bsp_trace_t bsptrace; - - AAS_PresenceTypeBoundingBox(presencetype, boxmins, boxmaxs); - - Com_Memset(&bsptrace, 0, sizeof(bsp_trace_t)); //make compiler happy - //assume no collision - bsptrace.fraction = 1; - collision = qfalse; - for (link = aasworld.arealinkedentities[areanum]; link; link = link->next_ent) - { - //ignore the pass entity - if (link->entnum == passent) continue; - // - if (AAS_EntityCollision(link->entnum, start, boxmins, boxmaxs, end, - CONTENTS_SOLID|CONTENTS_PLAYERCLIP, &bsptrace)) - { - collision = qtrue; - } //end if - } //end for - if (collision) - { - trace->startsolid = bsptrace.startsolid; - trace->ent = bsptrace.ent; - VectorCopy(bsptrace.endpos, trace->endpos); - trace->area = 0; - trace->planenum = 0; - return qtrue; - } //end if - return qfalse; -} //end of the function AAS_AreaEntityCollision -//=========================================================================== -// recursive subdivision of the line by the BSP tree. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, - int passent) -{ - int side, nodenum, tmpplanenum; - float front, back, frac; - vec3_t cur_start, cur_end, cur_mid, v1, v2; - aas_tracestack_t tracestack[127]; - aas_tracestack_t *tstack_p; - aas_node_t *aasnode; - aas_plane_t *plane; - aas_trace_t trace; - - //clear the trace structure - Com_Memset(&trace, 0, sizeof(aas_trace_t)); - - if (!aasworld.loaded) return trace; - - tstack_p = tracestack; - //we start with the whole line on the stack - VectorCopy(start, tstack_p->start); - VectorCopy(end, tstack_p->end); - tstack_p->planenum = 0; - //start with node 1 because node zero is a dummy for a solid leaf - tstack_p->nodenum = 1; //starting at the root of the tree - tstack_p++; - - while (1) - { - //pop up the stack - tstack_p--; - //if the trace stack is empty (ended up with a piece of the - //line to be traced in an area) - if (tstack_p < tracestack) - { - tstack_p++; - //nothing was hit - trace.startsolid = qfalse; - trace.fraction = 1.0; - //endpos is the end of the line - VectorCopy(end, trace.endpos); - //nothing hit - trace.ent = 0; - trace.area = 0; - trace.planenum = 0; - return trace; - } //end if - //number of the current node to test the line against - nodenum = tstack_p->nodenum; - //if it is an area - if (nodenum < 0) - { -#ifdef AAS_SAMPLE_DEBUG - if (-nodenum > aasworld.numareasettings) - { - botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: -nodenum out of range\n"); - return trace; - } //end if -#endif //AAS_SAMPLE_DEBUG - //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start)); - //if can't enter the area because it hasn't got the right presence type - if (!(aasworld.areasettings[-nodenum].presencetype & presencetype)) - { - //if the start point is still the initial start point - //NOTE: no need for epsilons because the points will be - //exactly the same when they're both the start point - if (tstack_p->start[0] == start[0] && - tstack_p->start[1] == start[1] && - tstack_p->start[2] == start[2]) - { - trace.startsolid = qtrue; - trace.fraction = 0.0; - VectorClear(v1); - } //end if - else - { - trace.startsolid = qfalse; - VectorSubtract(end, start, v1); - VectorSubtract(tstack_p->start, start, v2); - trace.fraction = VectorLength(v2) / VectorNormalize(v1); - VectorMA(tstack_p->start, -0.125, v1, tstack_p->start); - } //end else - VectorCopy(tstack_p->start, trace.endpos); - trace.ent = 0; - trace.area = -nodenum; -// VectorSubtract(end, start, v1); - trace.planenum = tstack_p->planenum; - //always take the plane with normal facing towards the trace start - plane = &aasworld.planes[trace.planenum]; - if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1; - return trace; - } //end if - else - { - if (passent >= 0) - { - if (AAS_AreaEntityCollision(-nodenum, tstack_p->start, - tstack_p->end, presencetype, passent, - &trace)) - { - if (!trace.startsolid) - { - VectorSubtract(end, start, v1); - VectorSubtract(trace.endpos, start, v2); - trace.fraction = VectorLength(v2) / VectorLength(v1); - } //end if - return trace; - } //end if - } //end if - } //end else - trace.lastarea = -nodenum; - continue; - } //end if - //if it is a solid leaf - if (!nodenum) - { - //if the start point is still the initial start point - //NOTE: no need for epsilons because the points will be - //exactly the same when they're both the start point - if (tstack_p->start[0] == start[0] && - tstack_p->start[1] == start[1] && - tstack_p->start[2] == start[2]) - { - trace.startsolid = qtrue; - trace.fraction = 0.0; - VectorClear(v1); - } //end if - else - { - trace.startsolid = qfalse; - VectorSubtract(end, start, v1); - VectorSubtract(tstack_p->start, start, v2); - trace.fraction = VectorLength(v2) / VectorNormalize(v1); - VectorMA(tstack_p->start, -0.125, v1, tstack_p->start); - } //end else - VectorCopy(tstack_p->start, trace.endpos); - trace.ent = 0; - trace.area = 0; //hit solid leaf -// VectorSubtract(end, start, v1); - trace.planenum = tstack_p->planenum; - //always take the plane with normal facing towards the trace start - plane = &aasworld.planes[trace.planenum]; - if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1; - return trace; - } //end if -#ifdef AAS_SAMPLE_DEBUG - if (nodenum > aasworld.numnodes) - { - botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: nodenum out of range\n"); - return trace; - } //end if -#endif //AAS_SAMPLE_DEBUG - //the node to test against - aasnode = &aasworld.nodes[nodenum]; - //start point of current line to test against node - VectorCopy(tstack_p->start, cur_start); - //end point of the current line to test against node - VectorCopy(tstack_p->end, cur_end); - //the current node plane - plane = &aasworld.planes[aasnode->planenum]; - - switch(plane->type) - {/*FIXME: wtf doesn't this work? obviously the axial node planes aren't always facing positive!!! - //check for axial planes - case PLANE_X: - { - front = cur_start[0] - plane->dist; - back = cur_end[0] - plane->dist; - break; - } //end case - case PLANE_Y: - { - front = cur_start[1] - plane->dist; - back = cur_end[1] - plane->dist; - break; - } //end case - case PLANE_Z: - { - front = cur_start[2] - plane->dist; - back = cur_end[2] - plane->dist; - break; - } //end case*/ - default: //gee it's not an axial plane - { - front = DotProduct(cur_start, plane->normal) - plane->dist; - back = DotProduct(cur_end, plane->normal) - plane->dist; - break; - } //end default - } //end switch - // bk010221 - old location of FPE hack and divide by zero expression - //if the whole to be traced line is totally at the front of this node - //only go down the tree with the front child - if ((front >= -ON_EPSILON && back >= -ON_EPSILON)) - { - //keep the current start and end point on the stack - //and go down the tree with the front child - tstack_p->nodenum = aasnode->children[0]; - tstack_p++; - if (tstack_p >= &tracestack[127]) - { - botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); - return trace; - } //end if - } //end if - //if the whole to be traced line is totally at the back of this node - //only go down the tree with the back child - else if ((front < ON_EPSILON && back < ON_EPSILON)) - { - //keep the current start and end point on the stack - //and go down the tree with the back child - tstack_p->nodenum = aasnode->children[1]; - tstack_p++; - if (tstack_p >= &tracestack[127]) - { - botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); - return trace; - } //end if - } //end if - //go down the tree both at the front and back of the node - else - { - tmpplanenum = tstack_p->planenum; - // bk010221 - new location of divide by zero (see above) - if ( front == back ) front -= 0.001f; // bk0101022 - hack/FPE - //calculate the hitpoint with the node (split point of the line) - //put the crosspoint TRACEPLANE_EPSILON pixels on the near side - if (front < 0) frac = (front + TRACEPLANE_EPSILON)/(front-back); - else frac = (front - TRACEPLANE_EPSILON)/(front-back); // bk010221 - // - if (frac < 0) - frac = 0.001f; //0 - else if (frac > 1) - frac = 0.999f; //1 - //frac = front / (front-back); - // - cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac; - cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac; - cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac; - -// AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED); - //side the front part of the line is on - side = front < 0; - //first put the end part of the line on the stack (back side) - VectorCopy(cur_mid, tstack_p->start); - //not necesary to store because still on stack - //VectorCopy(cur_end, tstack_p->end); - tstack_p->planenum = aasnode->planenum; - tstack_p->nodenum = aasnode->children[!side]; - tstack_p++; - if (tstack_p >= &tracestack[127]) - { - botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); - return trace; - } //end if - //now put the part near the start of the line on the stack so we will - //continue with thats part first. This way we'll find the first - //hit of the bbox - VectorCopy(cur_start, tstack_p->start); - VectorCopy(cur_mid, tstack_p->end); - tstack_p->planenum = tmpplanenum; - tstack_p->nodenum = aasnode->children[side]; - tstack_p++; - if (tstack_p >= &tracestack[127]) - { - botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); - return trace; - } //end if - } //end else - } //end while -// return trace; -} //end of the function AAS_TraceClientBBox -//=========================================================================== -// recursive subdivision of the line by the BSP tree. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas) -{ - int side, nodenum, tmpplanenum; - int numareas; - float front, back, frac; - vec3_t cur_start, cur_end, cur_mid; - aas_tracestack_t tracestack[127]; - aas_tracestack_t *tstack_p; - aas_node_t *aasnode; - aas_plane_t *plane; - - numareas = 0; - areas[0] = 0; - if (!aasworld.loaded) return numareas; - - tstack_p = tracestack; - //we start with the whole line on the stack - VectorCopy(start, tstack_p->start); - VectorCopy(end, tstack_p->end); - tstack_p->planenum = 0; - //start with node 1 because node zero is a dummy for a solid leaf - tstack_p->nodenum = 1; //starting at the root of the tree - tstack_p++; - - while (1) - { - //pop up the stack - tstack_p--; - //if the trace stack is empty (ended up with a piece of the - //line to be traced in an area) - if (tstack_p < tracestack) - { - return numareas; - } //end if - //number of the current node to test the line against - nodenum = tstack_p->nodenum; - //if it is an area - if (nodenum < 0) - { -#ifdef AAS_SAMPLE_DEBUG - if (-nodenum > aasworld.numareasettings) - { - botimport.Print(PRT_ERROR, "AAS_TraceAreas: -nodenum = %d out of range\n", -nodenum); - return numareas; - } //end if -#endif //AAS_SAMPLE_DEBUG - //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start)); - areas[numareas] = -nodenum; - if (points) VectorCopy(tstack_p->start, points[numareas]); - numareas++; - if (numareas >= maxareas) return numareas; - continue; - } //end if - //if it is a solid leaf - if (!nodenum) - { - continue; - } //end if -#ifdef AAS_SAMPLE_DEBUG - if (nodenum > aasworld.numnodes) - { - botimport.Print(PRT_ERROR, "AAS_TraceAreas: nodenum out of range\n"); - return numareas; - } //end if -#endif //AAS_SAMPLE_DEBUG - //the node to test against - aasnode = &aasworld.nodes[nodenum]; - //start point of current line to test against node - VectorCopy(tstack_p->start, cur_start); - //end point of the current line to test against node - VectorCopy(tstack_p->end, cur_end); - //the current node plane - plane = &aasworld.planes[aasnode->planenum]; - - switch(plane->type) - {/*FIXME: wtf doesn't this work? obviously the node planes aren't always facing positive!!! - //check for axial planes - case PLANE_X: - { - front = cur_start[0] - plane->dist; - back = cur_end[0] - plane->dist; - break; - } //end case - case PLANE_Y: - { - front = cur_start[1] - plane->dist; - back = cur_end[1] - plane->dist; - break; - } //end case - case PLANE_Z: - { - front = cur_start[2] - plane->dist; - back = cur_end[2] - plane->dist; - break; - } //end case*/ - default: //gee it's not an axial plane - { - front = DotProduct(cur_start, plane->normal) - plane->dist; - back = DotProduct(cur_end, plane->normal) - plane->dist; - break; - } //end default - } //end switch - - //if the whole to be traced line is totally at the front of this node - //only go down the tree with the front child - if (front > 0 && back > 0) - { - //keep the current start and end point on the stack - //and go down the tree with the front child - tstack_p->nodenum = aasnode->children[0]; - tstack_p++; - if (tstack_p >= &tracestack[127]) - { - botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); - return numareas; - } //end if - } //end if - //if the whole to be traced line is totally at the back of this node - //only go down the tree with the back child - else if (front <= 0 && back <= 0) - { - //keep the current start and end point on the stack - //and go down the tree with the back child - tstack_p->nodenum = aasnode->children[1]; - tstack_p++; - if (tstack_p >= &tracestack[127]) - { - botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); - return numareas; - } //end if - } //end if - //go down the tree both at the front and back of the node - else - { - tmpplanenum = tstack_p->planenum; - //calculate the hitpoint with the node (split point of the line) - //put the crosspoint TRACEPLANE_EPSILON pixels on the near side - if (front < 0) frac = (front)/(front-back); - else frac = (front)/(front-back); - if (frac < 0) frac = 0; - else if (frac > 1) frac = 1; - //frac = front / (front-back); - // - cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac; - cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac; - cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac; - -// AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED); - //side the front part of the line is on - side = front < 0; - //first put the end part of the line on the stack (back side) - VectorCopy(cur_mid, tstack_p->start); - //not necesary to store because still on stack - //VectorCopy(cur_end, tstack_p->end); - tstack_p->planenum = aasnode->planenum; - tstack_p->nodenum = aasnode->children[!side]; - tstack_p++; - if (tstack_p >= &tracestack[127]) - { - botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); - return numareas; - } //end if - //now put the part near the start of the line on the stack so we will - //continue with thats part first. This way we'll find the first - //hit of the bbox - VectorCopy(cur_start, tstack_p->start); - VectorCopy(cur_mid, tstack_p->end); - tstack_p->planenum = tmpplanenum; - tstack_p->nodenum = aasnode->children[side]; - tstack_p++; - if (tstack_p >= &tracestack[127]) - { - botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); - return numareas; - } //end if - } //end else - } //end while -// return numareas; -} //end of the function AAS_TraceAreas -//=========================================================================== -// a simple cross product -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -// void AAS_OrthogonalToVectors(vec3_t v1, vec3_t v2, vec3_t res) -#define AAS_OrthogonalToVectors(v1, v2, res) \ - (res)[0] = ((v1)[1] * (v2)[2]) - ((v1)[2] * (v2)[1]);\ - (res)[1] = ((v1)[2] * (v2)[0]) - ((v1)[0] * (v2)[2]);\ - (res)[2] = ((v1)[0] * (v2)[1]) - ((v1)[1] * (v2)[0]); -//=========================================================================== -// tests if the given point is within the face boundaries -// -// Parameter: face : face to test if the point is in it -// pnormal : normal of the plane to use for the face -// point : point to test if inside face boundaries -// Returns: qtrue if the point is within the face boundaries -// Changes Globals: - -//=========================================================================== -qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon) -{ - int i, firstvertex, edgenum; - vec3_t v0; - vec3_t edgevec, pointvec, sepnormal; - aas_edge_t *edge; -#ifdef AAS_SAMPLE_DEBUG - int lastvertex = 0; -#endif //AAS_SAMPLE_DEBUG - - if (!aasworld.loaded) return qfalse; - - for (i = 0; i < face->numedges; i++) - { - edgenum = aasworld.edgeindex[face->firstedge + i]; - edge = &aasworld.edges[abs(edgenum)]; - //get the first vertex of the edge - firstvertex = edgenum < 0; - VectorCopy(aasworld.vertexes[edge->v[firstvertex]], v0); - //edge vector - VectorSubtract(aasworld.vertexes[edge->v[!firstvertex]], v0, edgevec); - // -#ifdef AAS_SAMPLE_DEBUG - if (lastvertex && lastvertex != edge->v[firstvertex]) - { - botimport.Print(PRT_MESSAGE, "winding not counter clockwise\n"); - } //end if - lastvertex = edge->v[!firstvertex]; -#endif //AAS_SAMPLE_DEBUG - //vector from first edge point to point possible in face - VectorSubtract(point, v0, pointvec); - //get a vector pointing inside the face orthogonal to both the - //edge vector and the normal vector of the plane the face is in - //this vector defines a plane through the origin (first vertex of - //edge) and through both the edge vector and the normal vector - //of the plane - AAS_OrthogonalToVectors(edgevec, pnormal, sepnormal); - //check on wich side of the above plane the point is - //this is done by checking the sign of the dot product of the - //vector orthogonal vector from above and the vector from the - //origin (first vertex of edge) to the point - //if the dotproduct is smaller than zero the point is outside the face - if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse; - } //end for - return qtrue; -} //end of the function AAS_InsideFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon) -{ - int i, firstvertex, edgenum; - vec_t *v1, *v2; - vec3_t edgevec, pointvec, sepnormal; - aas_edge_t *edge; - aas_plane_t *plane; - aas_face_t *face; - - if (!aasworld.loaded) return qfalse; - - face = &aasworld.faces[facenum]; - plane = &aasworld.planes[face->planenum]; - // - for (i = 0; i < face->numedges; i++) - { - edgenum = aasworld.edgeindex[face->firstedge + i]; - edge = &aasworld.edges[abs(edgenum)]; - //get the first vertex of the edge - firstvertex = edgenum < 0; - v1 = aasworld.vertexes[edge->v[firstvertex]]; - v2 = aasworld.vertexes[edge->v[!firstvertex]]; - //edge vector - VectorSubtract(v2, v1, edgevec); - //vector from first edge point to point possible in face - VectorSubtract(point, v1, pointvec); - // - CrossProduct(edgevec, plane->normal, sepnormal); - // - if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse; - } //end for - return qtrue; -} //end of the function AAS_PointInsideFace -//=========================================================================== -// returns the ground face the given point is above in the given area -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point) -{ - int i, facenum; - vec3_t up = {0, 0, 1}; - vec3_t normal; - aas_area_t *area; - aas_face_t *face; - - if (!aasworld.loaded) return NULL; - - area = &aasworld.areas[areanum]; - for (i = 0; i < area->numfaces; i++) - { - facenum = aasworld.faceindex[area->firstface + i]; - face = &aasworld.faces[abs(facenum)]; - //if this is a ground face - if (face->faceflags & FACE_GROUND) - { - //get the up or down normal - if (aasworld.planes[face->planenum].normal[2] < 0) VectorNegate(up, normal); - else VectorCopy(up, normal); - //check if the point is in the face - if (AAS_InsideFace(face, normal, point, 0.01f)) return face; - } //end if - } //end for - return NULL; -} //end of the function AAS_AreaGroundFace -//=========================================================================== -// returns the face the trace end position is situated in -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_FacePlane(int facenum, vec3_t normal, float *dist) -{ - aas_plane_t *plane; - - plane = &aasworld.planes[aasworld.faces[facenum].planenum]; - VectorCopy(plane->normal, normal); - *dist = plane->dist; -} //end of the function AAS_FacePlane -//=========================================================================== -// returns the face the trace end position is situated in -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_face_t *AAS_TraceEndFace(aas_trace_t *trace) -{ - int i, facenum; - aas_area_t *area; - aas_face_t *face, *firstface = NULL; - - if (!aasworld.loaded) return NULL; - - //if started in solid no face was hit - if (trace->startsolid) return NULL; - //trace->lastarea is the last area the trace was in - area = &aasworld.areas[trace->lastarea]; - //check which face the trace.endpos was in - for (i = 0; i < area->numfaces; i++) - { - facenum = aasworld.faceindex[area->firstface + i]; - face = &aasworld.faces[abs(facenum)]; - //if the face is in the same plane as the trace end point - if ((face->planenum & ~1) == (trace->planenum & ~1)) - { - //firstface is used for optimization, if theres only one - //face in the plane then it has to be the good one - //if there are more faces in the same plane then always - //check the one with the fewest edges first -/* if (firstface) - { - if (firstface->numedges < face->numedges) - { - if (AAS_InsideFace(firstface, - aasworld.planes[face->planenum].normal, trace->endpos)) - { - return firstface; - } //end if - firstface = face; - } //end if - else - { - if (AAS_InsideFace(face, - aasworld.planes[face->planenum].normal, trace->endpos)) - { - return face; - } //end if - } //end else - } //end if - else - { - firstface = face; - } //end else*/ - if (AAS_InsideFace(face, - aasworld.planes[face->planenum].normal, trace->endpos, 0.01f)) return face; - } //end if - } //end for - return firstface; -} //end of the function AAS_TraceEndFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_BoxOnPlaneSide2(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p) -{ - int i, sides; - float dist1, dist2; - vec3_t corners[2]; - - for (i = 0; i < 3; i++) - { - if (p->normal[i] < 0) - { - corners[0][i] = absmins[i]; - corners[1][i] = absmaxs[i]; - } //end if - else - { - corners[1][i] = absmins[i]; - corners[0][i] = absmaxs[i]; - } //end else - } //end for - dist1 = DotProduct(p->normal, corners[0]) - p->dist; - dist2 = DotProduct(p->normal, corners[1]) - p->dist; - sides = 0; - if (dist1 >= 0) sides = 1; - if (dist2 < 0) sides |= 2; - - return sides; -} //end of the function AAS_BoxOnPlaneSide2 -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -//int AAS_BoxOnPlaneSide(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p) -#define AAS_BoxOnPlaneSide(absmins, absmaxs, p) (\ - ( (p)->type < 3) ?\ - (\ - ( (p)->dist <= (absmins)[(p)->type]) ?\ - (\ - 1\ - )\ - :\ - (\ - ( (p)->dist >= (absmaxs)[(p)->type]) ?\ - (\ - 2\ - )\ - :\ - (\ - 3\ - )\ - )\ - )\ - :\ - (\ - AAS_BoxOnPlaneSide2((absmins), (absmaxs), (p))\ - )\ -) //end of the function AAS_BoxOnPlaneSide -//=========================================================================== -// remove the links to this entity from all areas -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_UnlinkFromAreas(aas_link_t *areas) -{ - aas_link_t *link, *nextlink; - - for (link = areas; link; link = nextlink) - { - //next area the entity is linked in - nextlink = link->next_area; - //remove the entity from the linked list of this area - if (link->prev_ent) link->prev_ent->next_ent = link->next_ent; - else aasworld.arealinkedentities[link->areanum] = link->next_ent; - if (link->next_ent) link->next_ent->prev_ent = link->prev_ent; - //deallocate the link structure - AAS_DeAllocAASLink(link); - } //end for -} //end of the function AAS_UnlinkFromAreas -//=========================================================================== -// link the entity to the areas the bounding box is totally or partly -// situated in. This is done with recursion down the tree using the -// bounding box to test for plane sides -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== - -typedef struct -{ - int nodenum; //node found after splitting -} aas_linkstack_t; - -aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum) -{ - int side, nodenum; - aas_linkstack_t linkstack[128]; - aas_linkstack_t *lstack_p; - aas_node_t *aasnode; - aas_plane_t *plane; - aas_link_t *link, *areas; - - if (!aasworld.loaded) - { - botimport.Print(PRT_ERROR, "AAS_LinkEntity: aas not loaded\n"); - return NULL; - } //end if - - areas = NULL; - // - lstack_p = linkstack; - //we start with the whole line on the stack - //start with node 1 because node zero is a dummy used for solid leafs - lstack_p->nodenum = 1; //starting at the root of the tree - lstack_p++; - - while (1) - { - //pop up the stack - lstack_p--; - //if the trace stack is empty (ended up with a piece of the - //line to be traced in an area) - if (lstack_p < linkstack) break; - //number of the current node to test the line against - nodenum = lstack_p->nodenum; - //if it is an area - if (nodenum < 0) - { - //NOTE: the entity might have already been linked into this area - // because several node children can point to the same area - for (link = aasworld.arealinkedentities[-nodenum]; link; link = link->next_ent) - { - if (link->entnum == entnum) break; - } //end for - if (link) continue; - // - link = AAS_AllocAASLink(); - if (!link) return areas; - link->entnum = entnum; - link->areanum = -nodenum; - //put the link into the double linked area list of the entity - link->prev_area = NULL; - link->next_area = areas; - if (areas) areas->prev_area = link; - areas = link; - //put the link into the double linked entity list of the area - link->prev_ent = NULL; - link->next_ent = aasworld.arealinkedentities[-nodenum]; - if (aasworld.arealinkedentities[-nodenum]) - aasworld.arealinkedentities[-nodenum]->prev_ent = link; - aasworld.arealinkedentities[-nodenum] = link; - // - continue; - } //end if - //if solid leaf - if (!nodenum) continue; - //the node to test against - aasnode = &aasworld.nodes[nodenum]; - //the current node plane - plane = &aasworld.planes[aasnode->planenum]; - //get the side(s) the box is situated relative to the plane - side = AAS_BoxOnPlaneSide2(absmins, absmaxs, plane); - //if on the front side of the node - if (side & 1) - { - lstack_p->nodenum = aasnode->children[0]; - lstack_p++; - } //end if - if (lstack_p >= &linkstack[127]) - { - botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n"); - break; - } //end if - //if on the back side of the node - if (side & 2) - { - lstack_p->nodenum = aasnode->children[1]; - lstack_p++; - } //end if - if (lstack_p >= &linkstack[127]) - { - botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n"); - break; - } //end if - } //end while - return areas; -} //end of the function AAS_AASLinkEntity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype) -{ - vec3_t mins, maxs; - vec3_t newabsmins, newabsmaxs; - - AAS_PresenceTypeBoundingBox(presencetype, mins, maxs); - VectorSubtract(absmins, maxs, newabsmins); - VectorSubtract(absmaxs, mins, newabsmaxs); - //relink the entity - return AAS_AASLinkEntity(newabsmins, newabsmaxs, entnum); -} //end of the function AAS_LinkEntityClientBBox -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas) -{ - aas_link_t *linkedareas, *link; - int num; - - linkedareas = AAS_AASLinkEntity(absmins, absmaxs, -1); - num = 0; - for (link = linkedareas; link; link = link->next_area) - { - areas[num] = link->areanum; - num++; - if (num >= maxareas) - break; - } //end for - AAS_UnlinkFromAreas(linkedareas); - return num; -} //end of the function AAS_BBoxAreas -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AAS_AreaInfo( int areanum, aas_areainfo_t *info ) -{ - aas_areasettings_t *settings; - if (!info) - return 0; - if (areanum <= 0 || areanum >= aasworld.numareas) - { - botimport.Print(PRT_ERROR, "AAS_AreaInfo: areanum %d out of range\n", areanum); - return 0; - } //end if - settings = &aasworld.areasettings[areanum]; - info->cluster = settings->cluster; - info->contents = settings->contents; - info->flags = settings->areaflags; - info->presencetype = settings->presencetype; - VectorCopy(aasworld.areas[areanum].mins, info->mins); - VectorCopy(aasworld.areas[areanum].maxs, info->maxs); - VectorCopy(aasworld.areas[areanum].center, info->center); - return sizeof(aas_areainfo_t); -} //end of the function AAS_AreaInfo -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -aas_plane_t *AAS_PlaneFromNum(int planenum) -{ - if (!aasworld.loaded) return 0; - - return &aasworld.planes[planenum]; -} //end of the function AAS_PlaneFromNum diff --git a/tools/quake3/bspc/deps/botlib/be_aas_sample.h b/tools/quake3/bspc/deps/botlib/be_aas_sample.h deleted file mode 100644 index ed6c2371..00000000 --- a/tools/quake3/bspc/deps/botlib/be_aas_sample.h +++ /dev/null @@ -1,69 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_aas_sample.h - * - * desc: AAS - * - * $Archive: /source/code/botlib/be_aas_sample.h $ - * - *****************************************************************************/ - -#ifdef AASINTERN -void AAS_InitAASLinkHeap(void); -void AAS_InitAASLinkedEntities(void); -void AAS_FreeAASLinkHeap(void); -void AAS_FreeAASLinkedEntities(void); -aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point); -aas_face_t *AAS_TraceEndFace(aas_trace_t *trace); -aas_plane_t *AAS_PlaneFromNum(int planenum); -aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum); -aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype); -qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon); -qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon); -void AAS_UnlinkFromAreas(aas_link_t *areas); -#endif //AASINTERN - -//returns the mins and maxs of the bounding box for the given presence type -void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs); -//returns the cluster the area is in (negative portal number if the area is a portal) -int AAS_AreaCluster(int areanum); -//returns the presence type(s) of the area -int AAS_AreaPresenceType(int areanum); -//returns the presence type(s) at the given point -int AAS_PointPresenceType(vec3_t point); -//returns the result of the trace of a client bbox -aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, int passent); -//stores the areas the trace went through and returns the number of passed areas -int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); -//returns the areas the bounding box is in -int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas); -//return area information -int AAS_AreaInfo( int areanum, aas_areainfo_t *info ); -//returns the area the point is in -int AAS_PointAreaNum(vec3_t point); -// -int AAS_PointReachabilityAreaIndex( vec3_t point ); -//returns the plane the given face is in -void AAS_FacePlane(int facenum, vec3_t normal, float *dist); - diff --git a/tools/quake3/bspc/deps/botlib/be_ai_char.c b/tools/quake3/bspc/deps/botlib/be_ai_char.c deleted file mode 100644 index 7040304d..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_char.c +++ /dev/null @@ -1,790 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_ai_char.c - * - * desc: bot characters - * - * $Archive: /MissionPack/code/botlib/be_ai_char.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_log.h" -#include "l_memory.h" -#include "l_utils.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "l_libvar.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_ai_char.h" - -#define MAX_CHARACTERISTICS 80 - -#define CT_INTEGER 1 -#define CT_FLOAT 2 -#define CT_STRING 3 - -#define DEFAULT_CHARACTER "bots/default_c.c" - -//characteristic value -union cvalue -{ - int integer; - float _float; - char *string; -}; -//a characteristic -typedef struct bot_characteristic_s -{ - char type; //characteristic type - union cvalue value; //characteristic value -} bot_characteristic_t; - -//a bot character -typedef struct bot_character_s -{ - char filename[MAX_QPATH]; - float skill; - bot_characteristic_t c[1]; //variable sized -} bot_character_t; - -bot_character_t *botcharacters[MAX_CLIENTS + 1]; - -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -bot_character_t *BotCharacterFromHandle(int handle) -{ - if (handle <= 0 || handle > MAX_CLIENTS) - { - botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle); - return NULL; - } //end if - if (!botcharacters[handle]) - { - botimport.Print(PRT_FATAL, "invalid character %d\n", handle); - return NULL; - } //end if - return botcharacters[handle]; -} //end of the function BotCharacterFromHandle -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotDumpCharacter(bot_character_t *ch) -{ - int i; - - Log_Write("%s", ch->filename); - Log_Write("skill %d\n", ch->skill); - Log_Write("{\n"); - for (i = 0; i < MAX_CHARACTERISTICS; i++) - { - switch(ch->c[i].type) - { - case CT_INTEGER: Log_Write(" %4d %d\n", i, ch->c[i].value.integer); break; - case CT_FLOAT: Log_Write(" %4d %f\n", i, ch->c[i].value._float); break; - case CT_STRING: Log_Write(" %4d %s\n", i, ch->c[i].value.string); break; - } //end case - } //end for - Log_Write("}\n"); -} //end of the function BotDumpCharacter -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -void BotFreeCharacterStrings(bot_character_t *ch) -{ - int i; - - for (i = 0; i < MAX_CHARACTERISTICS; i++) - { - if (ch->c[i].type == CT_STRING) - { - FreeMemory(ch->c[i].value.string); - } //end if - } //end for -} //end of the function BotFreeCharacterStrings -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -void BotFreeCharacter2(int handle) -{ - if (handle <= 0 || handle > MAX_CLIENTS) - { - botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle); - return; - } //end if - if (!botcharacters[handle]) - { - botimport.Print(PRT_FATAL, "invalid character %d\n", handle); - return; - } //end if - BotFreeCharacterStrings(botcharacters[handle]); - FreeMemory(botcharacters[handle]); - botcharacters[handle] = NULL; -} //end of the function BotFreeCharacter2 -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -void BotFreeCharacter(int handle) -{ - if (!LibVarGetValue("bot_reloadcharacters")) return; - BotFreeCharacter2(handle); -} //end of the function BotFreeCharacter -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotDefaultCharacteristics(bot_character_t *ch, bot_character_t *defaultch) -{ - int i; - - for (i = 0; i < MAX_CHARACTERISTICS; i++) - { - if (ch->c[i].type) continue; - // - if (defaultch->c[i].type == CT_FLOAT) - { - ch->c[i].type = CT_FLOAT; - ch->c[i].value._float = defaultch->c[i].value._float; - } //end if - else if (defaultch->c[i].type == CT_INTEGER) - { - ch->c[i].type = CT_INTEGER; - ch->c[i].value.integer = defaultch->c[i].value.integer; - } //end else if - else if (defaultch->c[i].type == CT_STRING) - { - ch->c[i].type = CT_STRING; - ch->c[i].value.string = (char *) GetMemory(strlen(defaultch->c[i].value.string)+1); - strcpy(ch->c[i].value.string, defaultch->c[i].value.string); - } //end else if - } //end for -} //end of the function BotDefaultCharacteristics -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_character_t *BotLoadCharacterFromFile(char *charfile, int skill) -{ - int indent, index, foundcharacter; - bot_character_t *ch; - source_t *source; - token_t token; - - foundcharacter = qfalse; - //a bot character is parsed in two phases - PC_SetBaseFolder(BOTFILESBASEFOLDER); - source = LoadSourceFile(charfile); - if (!source) - { - botimport.Print(PRT_ERROR, "counldn't load %s\n", charfile); - return NULL; - } //end if - ch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) + - MAX_CHARACTERISTICS * sizeof(bot_characteristic_t)); - strcpy(ch->filename, charfile); - while(PC_ReadToken(source, &token)) - { - if (!strcmp(token.string, "skill")) - { - if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) - { - FreeSource(source); - BotFreeCharacterStrings(ch); - FreeMemory(ch); - return NULL; - } //end if - if (!PC_ExpectTokenString(source, "{")) - { - FreeSource(source); - BotFreeCharacterStrings(ch); - FreeMemory(ch); - return NULL; - } //end if - //if it's the correct skill - if (skill < 0 || token.intvalue == skill) - { - foundcharacter = qtrue; - ch->skill = token.intvalue; - while(PC_ExpectAnyToken(source, &token)) - { - if (!strcmp(token.string, "}")) break; - if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER)) - { - SourceError(source, "expected integer index, found %s\n", token.string); - FreeSource(source); - BotFreeCharacterStrings(ch); - FreeMemory(ch); - return NULL; - } //end if - index = token.intvalue; - if (index < 0 || index > MAX_CHARACTERISTICS) - { - SourceError(source, "characteristic index out of range [0, %d]\n", MAX_CHARACTERISTICS); - FreeSource(source); - BotFreeCharacterStrings(ch); - FreeMemory(ch); - return NULL; - } //end if - if (ch->c[index].type) - { - SourceError(source, "characteristic %d already initialized\n", index); - FreeSource(source); - BotFreeCharacterStrings(ch); - FreeMemory(ch); - return NULL; - } //end if - if (!PC_ExpectAnyToken(source, &token)) - { - FreeSource(source); - BotFreeCharacterStrings(ch); - FreeMemory(ch); - return NULL; - } //end if - if (token.type == TT_NUMBER) - { - if (token.subtype & TT_FLOAT) - { - ch->c[index].value._float = token.floatvalue; - ch->c[index].type = CT_FLOAT; - } //end if - else - { - ch->c[index].value.integer = token.intvalue; - ch->c[index].type = CT_INTEGER; - } //end else - } //end if - else if (token.type == TT_STRING) - { - StripDoubleQuotes(token.string); - ch->c[index].value.string = GetMemory(strlen(token.string)+1); - strcpy(ch->c[index].value.string, token.string); - ch->c[index].type = CT_STRING; - } //end else if - else - { - SourceError(source, "expected integer, float or string, found %s\n", token.string); - FreeSource(source); - BotFreeCharacterStrings(ch); - FreeMemory(ch); - return NULL; - } //end else - } //end if - break; - } //end if - else - { - indent = 1; - while(indent) - { - if (!PC_ExpectAnyToken(source, &token)) - { - FreeSource(source); - BotFreeCharacterStrings(ch); - FreeMemory(ch); - return NULL; - } //end if - if (!strcmp(token.string, "{")) indent++; - else if (!strcmp(token.string, "}")) indent--; - } //end while - } //end else - } //end if - else - { - SourceError(source, "unknown definition %s\n", token.string); - FreeSource(source); - BotFreeCharacterStrings(ch); - FreeMemory(ch); - return NULL; - } //end else - } //end while - FreeSource(source); - // - if (!foundcharacter) - { - BotFreeCharacterStrings(ch); - FreeMemory(ch); - return NULL; - } //end if - return ch; -} //end of the function BotLoadCharacterFromFile -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotFindCachedCharacter(char *charfile, float skill) -{ - int handle; - - for (handle = 1; handle <= MAX_CLIENTS; handle++) - { - if ( !botcharacters[handle] ) continue; - if ( strcmp( botcharacters[handle]->filename, charfile ) == 0 && - (skill < 0 || fabs(botcharacters[handle]->skill - skill) < 0.01) ) - { - return handle; - } //end if - } //end for - return 0; -} //end of the function BotFindCachedCharacter -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotLoadCachedCharacter(char *charfile, float skill, int reload) -{ - int handle, cachedhandle, intskill; - bot_character_t *ch = NULL; -#ifdef DEBUG - int starttime; - - starttime = Sys_MilliSeconds(); -#endif //DEBUG - - //find a free spot for a character - for (handle = 1; handle <= MAX_CLIENTS; handle++) - { - if (!botcharacters[handle]) break; - } //end for - if (handle > MAX_CLIENTS) return 0; - //try to load a cached character with the given skill - if (!reload) - { - cachedhandle = BotFindCachedCharacter(charfile, skill); - if (cachedhandle) - { - botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile); - return cachedhandle; - } //end if - } //end else - // - intskill = (int) (skill + 0.5); - //try to load the character with the given skill - ch = BotLoadCharacterFromFile(charfile, intskill); - if (ch) - { - botcharacters[handle] = ch; - // - botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", intskill, charfile); -#ifdef DEBUG - if (bot_developer) - { - botimport.Print(PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", intskill, Sys_MilliSeconds() - starttime, charfile); - } //end if -#endif //DEBUG - return handle; - } //end if - // - botimport.Print(PRT_WARNING, "couldn't find skill %d in %s\n", intskill, charfile); - // - if (!reload) - { - //try to load a cached default character with the given skill - cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, skill); - if (cachedhandle) - { - botimport.Print(PRT_MESSAGE, "loaded cached default skill %d from %s\n", intskill, charfile); - return cachedhandle; - } //end if - } //end if - //try to load the default character with the given skill - ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, intskill); - if (ch) - { - botcharacters[handle] = ch; - botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", intskill, charfile); - return handle; - } //end if - // - if (!reload) - { - //try to load a cached character with any skill - cachedhandle = BotFindCachedCharacter(charfile, -1); - if (cachedhandle) - { - botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile); - return cachedhandle; - } //end if - } //end if - //try to load a character with any skill - ch = BotLoadCharacterFromFile(charfile, -1); - if (ch) - { - botcharacters[handle] = ch; - botimport.Print(PRT_MESSAGE, "loaded skill %f from %s\n", ch->skill, charfile); - return handle; - } //end if - // - if (!reload) - { - //try to load a cached character with any skill - cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, -1); - if (cachedhandle) - { - botimport.Print(PRT_MESSAGE, "loaded cached default skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile); - return cachedhandle; - } //end if - } //end if - //try to load a character with any skill - ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, -1); - if (ch) - { - botcharacters[handle] = ch; - botimport.Print(PRT_MESSAGE, "loaded default skill %f from %s\n", ch->skill, charfile); - return handle; - } //end if - // - botimport.Print(PRT_WARNING, "couldn't load any skill from %s\n", charfile); - //couldn't load any character - return 0; -} //end of the function BotLoadCachedCharacter -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotLoadCharacterSkill(char *charfile, float skill) -{ - int ch, defaultch; - - defaultch = BotLoadCachedCharacter(DEFAULT_CHARACTER, skill, qfalse); - ch = BotLoadCachedCharacter(charfile, skill, LibVarGetValue("bot_reloadcharacters")); - - if (defaultch && ch) - { - BotDefaultCharacteristics(botcharacters[ch], botcharacters[defaultch]); - } //end if - - return ch; -} //end of the function BotLoadCharacterSkill -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotInterpolateCharacters(int handle1, int handle2, float desiredskill) -{ - bot_character_t *ch1, *ch2, *out; - int i, handle; - float scale; - - ch1 = BotCharacterFromHandle(handle1); - ch2 = BotCharacterFromHandle(handle2); - if (!ch1 || !ch2) - return 0; - //find a free spot for a character - for (handle = 1; handle <= MAX_CLIENTS; handle++) - { - if (!botcharacters[handle]) break; - } //end for - if (handle > MAX_CLIENTS) return 0; - out = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) + - MAX_CHARACTERISTICS * sizeof(bot_characteristic_t)); - out->skill = desiredskill; - strcpy(out->filename, ch1->filename); - botcharacters[handle] = out; - - scale = (float) (desiredskill - ch1->skill) / (ch2->skill - ch1->skill); - for (i = 0; i < MAX_CHARACTERISTICS; i++) - { - // - if (ch1->c[i].type == CT_FLOAT && ch2->c[i].type == CT_FLOAT) - { - out->c[i].type = CT_FLOAT; - out->c[i].value._float = ch1->c[i].value._float + - (ch2->c[i].value._float - ch1->c[i].value._float) * scale; - } //end if - else if (ch1->c[i].type == CT_INTEGER) - { - out->c[i].type = CT_INTEGER; - out->c[i].value.integer = ch1->c[i].value.integer; - } //end else if - else if (ch1->c[i].type == CT_STRING) - { - out->c[i].type = CT_STRING; - out->c[i].value.string = (char *) GetMemory(strlen(ch1->c[i].value.string)+1); - strcpy(out->c[i].value.string, ch1->c[i].value.string); - } //end else if - } //end for - return handle; -} //end of the function BotInterpolateCharacters -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotLoadCharacter(char *charfile, float skill) -{ - int firstskill, secondskill, handle; - - //make sure the skill is in the valid range - if (skill < 1.0) skill = 1.0; - else if (skill > 5.0) skill = 5.0; - //skill 1, 4 and 5 should be available in the character files - if (skill == 1.0 || skill == 4.0 || skill == 5.0) - { - return BotLoadCharacterSkill(charfile, skill); - } //end if - //check if there's a cached skill - handle = BotFindCachedCharacter(charfile, skill); - if (handle) - { - botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile); - return handle; - } //end if - if (skill < 4.0) - { - //load skill 1 and 4 - firstskill = BotLoadCharacterSkill(charfile, 1); - if (!firstskill) return 0; - secondskill = BotLoadCharacterSkill(charfile, 4); - if (!secondskill) return firstskill; - } //end if - else - { - //load skill 4 and 5 - firstskill = BotLoadCharacterSkill(charfile, 4); - if (!firstskill) return 0; - secondskill = BotLoadCharacterSkill(charfile, 5); - if (!secondskill) return firstskill; - } //end else - //interpolate between the two skills - handle = BotInterpolateCharacters(firstskill, secondskill, skill); - if (!handle) return 0; - //write the character to the log file - BotDumpCharacter(botcharacters[handle]); - // - return handle; -} //end of the function BotLoadCharacter -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int CheckCharacteristicIndex(int character, int index) -{ - bot_character_t *ch; - - ch = BotCharacterFromHandle(character); - if (!ch) return qfalse; - if (index < 0 || index >= MAX_CHARACTERISTICS) - { - botimport.Print(PRT_ERROR, "characteristic %d does not exist\n", index); - return qfalse; - } //end if - if (!ch->c[index].type) - { - botimport.Print(PRT_ERROR, "characteristic %d is not initialized\n", index); - return qfalse; - } //end if - return qtrue; -} //end of the function CheckCharacteristicIndex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float Characteristic_Float(int character, int index) -{ - bot_character_t *ch; - - ch = BotCharacterFromHandle(character); - if (!ch) return 0; - //check if the index is in range - if (!CheckCharacteristicIndex(character, index)) return 0; - //an integer will be converted to a float - if (ch->c[index].type == CT_INTEGER) - { - return (float) ch->c[index].value.integer; - } //end if - //floats are just returned - else if (ch->c[index].type == CT_FLOAT) - { - return ch->c[index].value._float; - } //end else if - //cannot convert a string pointer to a float - else - { - botimport.Print(PRT_ERROR, "characteristic %d is not a float\n", index); - return 0; - } //end else if -// return 0; -} //end of the function Characteristic_Float -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float Characteristic_BFloat(int character, int index, float min, float max) -{ - float value; - bot_character_t *ch; - - ch = BotCharacterFromHandle(character); - if (!ch) return 0; - if (min > max) - { - botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %f and %f\n", index, min, max); - return 0; - } //end if - value = Characteristic_Float(character, index); - if (value < min) return min; - if (value > max) return max; - return value; -} //end of the function Characteristic_BFloat -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Characteristic_Integer(int character, int index) -{ - bot_character_t *ch; - - ch = BotCharacterFromHandle(character); - if (!ch) return 0; - //check if the index is in range - if (!CheckCharacteristicIndex(character, index)) return 0; - //an integer will just be returned - if (ch->c[index].type == CT_INTEGER) - { - return ch->c[index].value.integer; - } //end if - //floats are casted to integers - else if (ch->c[index].type == CT_FLOAT) - { - return (int) ch->c[index].value._float; - } //end else if - else - { - botimport.Print(PRT_ERROR, "characteristic %d is not a integer\n", index); - return 0; - } //end else if -// return 0; -} //end of the function Characteristic_Integer -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Characteristic_BInteger(int character, int index, int min, int max) -{ - int value; - bot_character_t *ch; - - ch = BotCharacterFromHandle(character); - if (!ch) return 0; - if (min > max) - { - botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %d and %d\n", index, min, max); - return 0; - } //end if - value = Characteristic_Integer(character, index); - if (value < min) return min; - if (value > max) return max; - return value; -} //end of the function Characteristic_BInteger -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Characteristic_String(int character, int index, char *buf, int size) -{ - bot_character_t *ch; - - ch = BotCharacterFromHandle(character); - if (!ch) return; - //check if the index is in range - if (!CheckCharacteristicIndex(character, index)) return; - //an integer will be converted to a float - if (ch->c[index].type == CT_STRING) - { - strncpy(buf, ch->c[index].value.string, size-1); - buf[size-1] = '\0'; - return; - } //end if - else - { - botimport.Print(PRT_ERROR, "characteristic %d is not a string\n", index); - return; - } //end else if - return; -} //end of the function Characteristic_String -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotShutdownCharacters(void) -{ - int handle; - - for (handle = 1; handle <= MAX_CLIENTS; handle++) - { - if (botcharacters[handle]) - { - BotFreeCharacter2(handle); - } //end if - } //end for -} //end of the function BotShutdownCharacters - diff --git a/tools/quake3/bspc/deps/botlib/be_ai_char.h b/tools/quake3/bspc/deps/botlib/be_ai_char.h deleted file mode 100644 index 719d68f6..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_char.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// - -/***************************************************************************** - * name: be_ai_char.h - * - * desc: bot characters - * - * $Archive: /source/code/botlib/be_ai_char.h $ - * - *****************************************************************************/ - -//loads a bot character from a file -int BotLoadCharacter(char *charfile, float skill); -//frees a bot character -void BotFreeCharacter(int character); -//returns a float characteristic -float Characteristic_Float(int character, int index); -//returns a bounded float characteristic -float Characteristic_BFloat(int character, int index, float min, float max); -//returns an integer characteristic -int Characteristic_Integer(int character, int index); -//returns a bounded integer characteristic -int Characteristic_BInteger(int character, int index, int min, int max); -//returns a string characteristic -void Characteristic_String(int character, int index, char *buf, int size); -//free cached bot characters -void BotShutdownCharacters(void); diff --git a/tools/quake3/bspc/deps/botlib/be_ai_chat.c b/tools/quake3/bspc/deps/botlib/be_ai_chat.c deleted file mode 100644 index a2491300..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_chat.c +++ /dev/null @@ -1,3017 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_ai_chat.c - * - * desc: bot chat AI - * - * $Archive: /MissionPack/code/botlib/be_ai_chat.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_libvar.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "l_utils.h" -#include "l_log.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_ea.h" -#include "be_ai_chat.h" - - -//escape character -#define ESCAPE_CHAR 0x01 //'_' -// -// "hi ", people, " ", 0, " entered the game" -//becomes: -// "hi _rpeople_ _v0_ entered the game" -// - -//match piece types -#define MT_VARIABLE 1 //variable match piece -#define MT_STRING 2 //string match piece -//reply chat key flags -#define RCKFL_AND 1 //key must be present -#define RCKFL_NOT 2 //key must be absent -#define RCKFL_NAME 4 //name of bot must be present -#define RCKFL_STRING 8 //key is a string -#define RCKFL_VARIABLES 16 //key is a match template -#define RCKFL_BOTNAMES 32 //key is a series of botnames -#define RCKFL_GENDERFEMALE 64 //bot must be female -#define RCKFL_GENDERMALE 128 //bot must be male -#define RCKFL_GENDERLESS 256 //bot must be genderless -//time to ignore a chat message after using it -#define CHATMESSAGE_RECENTTIME 20 - -//the actuall chat messages -typedef struct bot_chatmessage_s -{ - char *chatmessage; //chat message string - float time; //last time used - struct bot_chatmessage_s *next; //next chat message in a list -} bot_chatmessage_t; -//bot chat type with chat lines -typedef struct bot_chattype_s -{ - char name[MAX_CHATTYPE_NAME]; - int numchatmessages; - bot_chatmessage_t *firstchatmessage; - struct bot_chattype_s *next; -} bot_chattype_t; -//bot chat lines -typedef struct bot_chat_s -{ - bot_chattype_t *types; -} bot_chat_t; - -//random string -typedef struct bot_randomstring_s -{ - char *string; - struct bot_randomstring_s *next; -} bot_randomstring_t; -//list with random strings -typedef struct bot_randomlist_s -{ - char *string; - int numstrings; - bot_randomstring_t *firstrandomstring; - struct bot_randomlist_s *next; -} bot_randomlist_t; - -//synonym -typedef struct bot_synonym_s -{ - char *string; - float weight; - struct bot_synonym_s *next; -} bot_synonym_t; -//list with synonyms -typedef struct bot_synonymlist_s -{ - unsigned long int context; - float totalweight; - bot_synonym_t *firstsynonym; - struct bot_synonymlist_s *next; -} bot_synonymlist_t; - -//fixed match string -typedef struct bot_matchstring_s -{ - char *string; - struct bot_matchstring_s *next; -} bot_matchstring_t; - -//piece of a match template -typedef struct bot_matchpiece_s -{ - int type; - bot_matchstring_t *firststring; - int variable; - struct bot_matchpiece_s *next; -} bot_matchpiece_t; -//match template -typedef struct bot_matchtemplate_s -{ - unsigned long int context; - int type; - int subtype; - bot_matchpiece_t *first; - struct bot_matchtemplate_s *next; -} bot_matchtemplate_t; - -//reply chat key -typedef struct bot_replychatkey_s -{ - int flags; - char *string; - bot_matchpiece_t *match; - struct bot_replychatkey_s *next; -} bot_replychatkey_t; -//reply chat -typedef struct bot_replychat_s -{ - bot_replychatkey_t *keys; - float priority; - int numchatmessages; - bot_chatmessage_t *firstchatmessage; - struct bot_replychat_s *next; -} bot_replychat_t; - -//string list -typedef struct bot_stringlist_s -{ - char *string; - struct bot_stringlist_s *next; -} bot_stringlist_t; - -//chat state of a bot -typedef struct bot_chatstate_s -{ - int gender; //0=it, 1=female, 2=male - int client; //client number - char name[32]; //name of the bot - char chatmessage[MAX_MESSAGE_SIZE]; - int handle; - //the console messages visible to the bot - bot_consolemessage_t *firstmessage; //first message is the first typed message - bot_consolemessage_t *lastmessage; //last message is the last typed message, bottom of console - //number of console messages stored in the state - int numconsolemessages; - //the bot chat lines - bot_chat_t *chat; -} bot_chatstate_t; - -typedef struct { - bot_chat_t *chat; - char filename[MAX_QPATH]; - char chatname[MAX_QPATH]; -} bot_ichatdata_t; - -bot_ichatdata_t *ichatdata[MAX_CLIENTS]; - -bot_chatstate_t *botchatstates[MAX_CLIENTS+1]; -//console message heap -bot_consolemessage_t *consolemessageheap = NULL; -bot_consolemessage_t *freeconsolemessages = NULL; -//list with match strings -bot_matchtemplate_t *matchtemplates = NULL; -//list with synonyms -bot_synonymlist_t *synonyms = NULL; -//list with random strings -bot_randomlist_t *randomstrings = NULL; -//reply chats -bot_replychat_t *replychats = NULL; - -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -bot_chatstate_t *BotChatStateFromHandle(int handle) -{ - if (handle <= 0 || handle > MAX_CLIENTS) - { - botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle); - return NULL; - } //end if - if (!botchatstates[handle]) - { - botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle); - return NULL; - } //end if - return botchatstates[handle]; -} //end of the function BotChatStateFromHandle -//=========================================================================== -// initialize the heap with unused console messages -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void InitConsoleMessageHeap(void) -{ - int i, max_messages; - - if (consolemessageheap) FreeMemory(consolemessageheap); - // - max_messages = (int) LibVarValue("max_messages", "1024"); - consolemessageheap = (bot_consolemessage_t *) GetClearedHunkMemory(max_messages * - sizeof(bot_consolemessage_t)); - consolemessageheap[0].prev = NULL; - consolemessageheap[0].next = &consolemessageheap[1]; - for (i = 1; i < max_messages-1; i++) - { - consolemessageheap[i].prev = &consolemessageheap[i - 1]; - consolemessageheap[i].next = &consolemessageheap[i + 1]; - } //end for - consolemessageheap[max_messages-1].prev = &consolemessageheap[max_messages-2]; - consolemessageheap[max_messages-1].next = NULL; - //pointer to the free console messages - freeconsolemessages = consolemessageheap; -} //end of the function InitConsoleMessageHeap -//=========================================================================== -// allocate one console message from the heap -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_consolemessage_t *AllocConsoleMessage(void) -{ - bot_consolemessage_t *message; - message = freeconsolemessages; - if (freeconsolemessages) freeconsolemessages = freeconsolemessages->next; - if (freeconsolemessages) freeconsolemessages->prev = NULL; - return message; -} //end of the function AllocConsoleMessage -//=========================================================================== -// deallocate one console message from the heap -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeConsoleMessage(bot_consolemessage_t *message) -{ - if (freeconsolemessages) freeconsolemessages->prev = message; - message->prev = NULL; - message->next = freeconsolemessages; - freeconsolemessages = message; -} //end of the function FreeConsoleMessage -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotRemoveConsoleMessage(int chatstate, int handle) -{ - bot_consolemessage_t *m, *nextm; - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return; - - for (m = cs->firstmessage; m; m = nextm) - { - nextm = m->next; - if (m->handle == handle) - { - if (m->next) m->next->prev = m->prev; - else cs->lastmessage = m->prev; - if (m->prev) m->prev->next = m->next; - else cs->firstmessage = m->next; - - FreeConsoleMessage(m); - cs->numconsolemessages--; - break; - } //end if - } //end for -} //end of the function BotRemoveConsoleMessage -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotQueueConsoleMessage(int chatstate, int type, char *message) -{ - bot_consolemessage_t *m; - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return; - - m = AllocConsoleMessage(); - if (!m) - { - botimport.Print(PRT_ERROR, "empty console message heap\n"); - return; - } //end if - cs->handle++; - if (cs->handle <= 0 || cs->handle > 8192) cs->handle = 1; - m->handle = cs->handle; - m->time = AAS_Time(); - m->type = type; - strncpy(m->message, message, MAX_MESSAGE_SIZE); - m->next = NULL; - if (cs->lastmessage) - { - cs->lastmessage->next = m; - m->prev = cs->lastmessage; - cs->lastmessage = m; - } //end if - else - { - cs->lastmessage = m; - cs->firstmessage = m; - m->prev = NULL; - } //end if - cs->numconsolemessages++; -} //end of the function BotQueueConsoleMessage -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm) -{ - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return 0; - if (cs->firstmessage) - { - Com_Memcpy(cm, cs->firstmessage, sizeof(bot_consolemessage_t)); - cm->next = cm->prev = NULL; - return cm->handle; - } //end if - return 0; -} //end of the function BotConsoleMessage -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotNumConsoleMessages(int chatstate) -{ - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return 0; - return cs->numconsolemessages; -} //end of the function BotNumConsoleMessages -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int IsWhiteSpace(char c) -{ - if ((c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c >= '0' && c <= '9') - || c == '(' || c == ')' - || c == '?' || c == ':' - || c == '\''|| c == '/' - || c == ',' || c == '.' - || c == '[' || c == ']' - || c == '-' || c == '_' - || c == '+' || c == '=') return qfalse; - return qtrue; -} //end of the function IsWhiteSpace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotRemoveTildes(char *message) -{ - int i; - - //remove all tildes from the chat message - for (i = 0; message[i]; i++) - { - if (message[i] == '~') - { - memmove(&message[i], &message[i+1], strlen(&message[i+1])+1); - } //end if - } //end for -} //end of the function BotRemoveTildes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void UnifyWhiteSpaces(char *string) -{ - char *ptr, *oldptr; - - for (ptr = oldptr = string; *ptr; oldptr = ptr) - { - while(*ptr && IsWhiteSpace(*ptr)) ptr++; - if (ptr > oldptr) - { - //if not at the start and not at the end of the string - //write only one space - if (oldptr > string && *ptr) *oldptr++ = ' '; - //remove all other white spaces - if (ptr > oldptr) memmove(oldptr, ptr, strlen(ptr)+1); - } //end if - while(*ptr && !IsWhiteSpace(*ptr)) ptr++; - } //end while -} //end of the function UnifyWhiteSpaces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int StringContains(char *str1, char *str2, int casesensitive) -{ - int len, i, j, index; - - if (str1 == NULL || str2 == NULL) return -1; - - len = strlen(str1) - strlen(str2); - index = 0; - for (i = 0; i <= len; i++, str1++, index++) - { - for (j = 0; str2[j]; j++) - { - if (casesensitive) - { - if (str1[j] != str2[j]) break; - } //end if - else - { - if (toupper(str1[j]) != toupper(str2[j])) break; - } //end else - } //end for - if (!str2[j]) return index; - } //end for - return -1; -} //end of the function StringContains -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *StringContainsWord(char *str1, char *str2, int casesensitive) -{ - int len, i, j; - - len = strlen(str1) - strlen(str2); - for (i = 0; i <= len; i++, str1++) - { - //if not at the start of the string - if (i) - { - //skip to the start of the next word - while(*str1 && *str1 != ' ' && *str1 != '.' && *str1 != ',' && *str1 != '!') str1++; - if (!*str1) break; - str1++; - } //end for - //compare the word - for (j = 0; str2[j]; j++) - { - if (casesensitive) - { - if (str1[j] != str2[j]) break; - } //end if - else - { - if (toupper(str1[j]) != toupper(str2[j])) break; - } //end else - } //end for - //if there was a word match - if (!str2[j]) - { - //if the first string has an end of word - if (!str1[j] || str1[j] == ' ' || str1[j] == '.' || str1[j] == ',' || str1[j] == '!') return str1; - } //end if - } //end for - return NULL; -} //end of the function StringContainsWord -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void StringReplaceWords(char *string, char *synonym, char *replacement) -{ - char *str, *str2; - - //find the synonym in the string - str = StringContainsWord(string, synonym, qfalse); - //if the synonym occured in the string - while(str) - { - //if the synonym isn't part of the replacement which is already in the string - //usefull for abreviations - str2 = StringContainsWord(string, replacement, qfalse); - while(str2) - { - if (str2 <= str && str < str2 + strlen(replacement)) break; - str2 = StringContainsWord(str2+1, replacement, qfalse); - } //end while - if (!str2) - { - memmove(str + strlen(replacement), str+strlen(synonym), strlen(str+strlen(synonym))+1); - //append the synonum replacement - Com_Memcpy(str, replacement, strlen(replacement)); - } //end if - //find the next synonym in the string - str = StringContainsWord(str+strlen(replacement), synonym, qfalse); - } //end if -} //end of the function StringReplaceWords -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotDumpSynonymList(bot_synonymlist_t *synlist) -{ - FILE *fp; - bot_synonymlist_t *syn; - bot_synonym_t *synonym; - - fp = Log_FilePointer(); - if (!fp) return; - for (syn = synlist; syn; syn = syn->next) - { - fprintf(fp, "%ld : [", syn->context); - for (synonym = syn->firstsynonym; synonym; synonym = synonym->next) - { - fprintf(fp, "(\"%s\", %1.2f)", synonym->string, synonym->weight); - if (synonym->next) fprintf(fp, ", "); - } //end for - fprintf(fp, "]\n"); - } //end for -} //end of the function BotDumpSynonymList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_synonymlist_t *BotLoadSynonyms(char *filename) -{ - int pass, size, contextlevel, numsynonyms; - unsigned long int context, contextstack[32]; - char *ptr = NULL; - source_t *source; - token_t token; - bot_synonymlist_t *synlist, *lastsyn, *syn; - bot_synonym_t *synonym, *lastsynonym; - - size = 0; - synlist = NULL; //make compiler happy - syn = NULL; //make compiler happy - synonym = NULL; //make compiler happy - //the synonyms are parsed in two phases - for (pass = 0; pass < 2; pass++) - { - // - if (pass && size) ptr = (char *) GetClearedHunkMemory(size); - // - PC_SetBaseFolder(BOTFILESBASEFOLDER); - source = LoadSourceFile(filename); - if (!source) - { - botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); - return NULL; - } //end if - // - context = 0; - contextlevel = 0; - synlist = NULL; //list synonyms - lastsyn = NULL; //last synonym in the list - // - while(PC_ReadToken(source, &token)) - { - if (token.type == TT_NUMBER) - { - context |= token.intvalue; - contextstack[contextlevel] = token.intvalue; - contextlevel++; - if (contextlevel >= 32) - { - SourceError(source, "more than 32 context levels"); - FreeSource(source); - return NULL; - } //end if - if (!PC_ExpectTokenString(source, "{")) - { - FreeSource(source); - return NULL; - } //end if - } //end if - else if (token.type == TT_PUNCTUATION) - { - if (!strcmp(token.string, "}")) - { - contextlevel--; - if (contextlevel < 0) - { - SourceError(source, "too many }"); - FreeSource(source); - return NULL; - } //end if - context &= ~contextstack[contextlevel]; - } //end if - else if (!strcmp(token.string, "[")) - { - size += sizeof(bot_synonymlist_t); - if (pass) - { - syn = (bot_synonymlist_t *) ptr; - ptr += sizeof(bot_synonymlist_t); - syn->context = context; - syn->firstsynonym = NULL; - syn->next = NULL; - if (lastsyn) lastsyn->next = syn; - else synlist = syn; - lastsyn = syn; - } //end if - numsynonyms = 0; - lastsynonym = NULL; - while(1) - { - if (!PC_ExpectTokenString(source, "(") || - !PC_ExpectTokenType(source, TT_STRING, 0, &token)) - { - FreeSource(source); - return NULL; - } //end if - StripDoubleQuotes(token.string); - if (strlen(token.string) <= 0) - { - SourceError(source, "empty string", token.string); - FreeSource(source); - return NULL; - } //end if - size += sizeof(bot_synonym_t) + strlen(token.string) + 1; - if (pass) - { - synonym = (bot_synonym_t *) ptr; - ptr += sizeof(bot_synonym_t); - synonym->string = ptr; - ptr += strlen(token.string) + 1; - strcpy(synonym->string, token.string); - // - if (lastsynonym) lastsynonym->next = synonym; - else syn->firstsynonym = synonym; - lastsynonym = synonym; - } //end if - numsynonyms++; - if (!PC_ExpectTokenString(source, ",") || - !PC_ExpectTokenType(source, TT_NUMBER, 0, &token) || - !PC_ExpectTokenString(source, ")")) - { - FreeSource(source); - return NULL; - } //end if - if (pass) - { - synonym->weight = token.floatvalue; - syn->totalweight += synonym->weight; - } //end if - if (PC_CheckTokenString(source, "]")) break; - if (!PC_ExpectTokenString(source, ",")) - { - FreeSource(source); - return NULL; - } //end if - } //end while - if (numsynonyms < 2) - { - SourceError(source, "synonym must have at least two entries\n"); - FreeSource(source); - return NULL; - } //end if - } //end else - else - { - SourceError(source, "unexpected %s", token.string); - FreeSource(source); - return NULL; - } //end if - } //end else if - } //end while - // - FreeSource(source); - // - if (contextlevel > 0) - { - SourceError(source, "missing }"); - return NULL; - } //end if - } //end for - botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); - // - //BotDumpSynonymList(synlist); - // - return synlist; -} //end of the function BotLoadSynonyms -//=========================================================================== -// replace all the synonyms in the string -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotReplaceSynonyms(char *string, unsigned long int context) -{ - bot_synonymlist_t *syn; - bot_synonym_t *synonym; - - for (syn = synonyms; syn; syn = syn->next) - { - if (!(syn->context & context)) continue; - for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next) - { - StringReplaceWords(string, synonym->string, syn->firstsynonym->string); - } //end for - } //end for -} //end of the function BotReplaceSynonyms -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotReplaceWeightedSynonyms(char *string, unsigned long int context) -{ - bot_synonymlist_t *syn; - bot_synonym_t *synonym, *replacement; - float weight, curweight; - - for (syn = synonyms; syn; syn = syn->next) - { - if (!(syn->context & context)) continue; - //choose a weighted random replacement synonym - weight = random() * syn->totalweight; - if (!weight) continue; - curweight = 0; - for (replacement = syn->firstsynonym; replacement; replacement = replacement->next) - { - curweight += replacement->weight; - if (weight < curweight) break; - } //end for - if (!replacement) continue; - //replace all synonyms with the replacement - for (synonym = syn->firstsynonym; synonym; synonym = synonym->next) - { - if (synonym == replacement) continue; - StringReplaceWords(string, synonym->string, replacement->string); - } //end for - } //end for -} //end of the function BotReplaceWeightedSynonyms -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotReplaceReplySynonyms(char *string, unsigned long int context) -{ - char *str1, *str2, *replacement; - bot_synonymlist_t *syn; - bot_synonym_t *synonym; - - for (str1 = string; *str1; ) - { - //go to the start of the next word - while(*str1 && *str1 <= ' ') str1++; - if (!*str1) break; - // - for (syn = synonyms; syn; syn = syn->next) - { - if (!(syn->context & context)) continue; - for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next) - { - str2 = synonym->string; - //if the synonym is not at the front of the string continue - str2 = StringContainsWord(str1, synonym->string, qfalse); - if (!str2 || str2 != str1) continue; - // - replacement = syn->firstsynonym->string; - //if the replacement IS in front of the string continue - str2 = StringContainsWord(str1, replacement, qfalse); - if (str2 && str2 == str1) continue; - // - memmove(str1 + strlen(replacement), str1+strlen(synonym->string), - strlen(str1+strlen(synonym->string)) + 1); - //append the synonum replacement - Com_Memcpy(str1, replacement, strlen(replacement)); - // - break; - } //end for - //if a synonym has been replaced - if (synonym) break; - } //end for - //skip over this word - while(*str1 && *str1 > ' ') str1++; - if (!*str1) break; - } //end while -} //end of the function BotReplaceReplySynonyms -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotLoadChatMessage(source_t *source, char *chatmessagestring) -{ - char *ptr; - token_t token; - - ptr = chatmessagestring; - *ptr = 0; - // - while(1) - { - if (!PC_ExpectAnyToken(source, &token)) return qfalse; - //fixed string - if (token.type == TT_STRING) - { - StripDoubleQuotes(token.string); - if (strlen(ptr) + strlen(token.string) + 1 > MAX_MESSAGE_SIZE) - { - SourceError(source, "chat message too long\n"); - return qfalse; - } //end if - strcat(ptr, token.string); - } //end else if - //variable string - else if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER)) - { - if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE) - { - SourceError(source, "chat message too long\n"); - return qfalse; - } //end if - sprintf(&ptr[strlen(ptr)], "%cv%ld%c", ESCAPE_CHAR, token.intvalue, ESCAPE_CHAR); - } //end if - //random string - else if (token.type == TT_NAME) - { - if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE) - { - SourceError(source, "chat message too long\n"); - return qfalse; - } //end if - sprintf(&ptr[strlen(ptr)], "%cr%s%c", ESCAPE_CHAR, token.string, ESCAPE_CHAR); - } //end else if - else - { - SourceError(source, "unknown message component %s\n", token.string); - return qfalse; - } //end else - if (PC_CheckTokenString(source, ";")) break; - if (!PC_ExpectTokenString(source, ",")) return qfalse; - } //end while - // - return qtrue; -} //end of the function BotLoadChatMessage -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotDumpRandomStringList(bot_randomlist_t *randomlist) -{ - FILE *fp; - bot_randomlist_t *random; - bot_randomstring_t *rs; - - fp = Log_FilePointer(); - if (!fp) return; - for (random = randomlist; random; random = random->next) - { - fprintf(fp, "%s = {", random->string); - for (rs = random->firstrandomstring; rs; rs = rs->next) - { - fprintf(fp, "\"%s\"", rs->string); - if (rs->next) fprintf(fp, ", "); - else fprintf(fp, "}\n"); - } //end for - } //end for -} //end of the function BotDumpRandomStringList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_randomlist_t *BotLoadRandomStrings(char *filename) -{ - int pass, size; - char *ptr = NULL, chatmessagestring[MAX_MESSAGE_SIZE]; - source_t *source; - token_t token; - bot_randomlist_t *randomlist, *lastrandom, *random; - bot_randomstring_t *randomstring; - -#ifdef DEBUG - int starttime = Sys_MilliSeconds(); -#endif //DEBUG - - size = 0; - randomlist = NULL; - random = NULL; - //the synonyms are parsed in two phases - for (pass = 0; pass < 2; pass++) - { - // - if (pass && size) ptr = (char *) GetClearedHunkMemory(size); - // - PC_SetBaseFolder(BOTFILESBASEFOLDER); - source = LoadSourceFile(filename); - if (!source) - { - botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); - return NULL; - } //end if - // - randomlist = NULL; //list - lastrandom = NULL; //last - // - while(PC_ReadToken(source, &token)) - { - if (token.type != TT_NAME) - { - SourceError(source, "unknown random %s", token.string); - FreeSource(source); - return NULL; - } //end if - size += sizeof(bot_randomlist_t) + strlen(token.string) + 1; - if (pass) - { - random = (bot_randomlist_t *) ptr; - ptr += sizeof(bot_randomlist_t); - random->string = ptr; - ptr += strlen(token.string) + 1; - strcpy(random->string, token.string); - random->firstrandomstring = NULL; - random->numstrings = 0; - // - if (lastrandom) lastrandom->next = random; - else randomlist = random; - lastrandom = random; - } //end if - if (!PC_ExpectTokenString(source, "=") || - !PC_ExpectTokenString(source, "{")) - { - FreeSource(source); - return NULL; - } //end if - while(!PC_CheckTokenString(source, "}")) - { - if (!BotLoadChatMessage(source, chatmessagestring)) - { - FreeSource(source); - return NULL; - } //end if - size += sizeof(bot_randomstring_t) + strlen(chatmessagestring) + 1; - if (pass) - { - randomstring = (bot_randomstring_t *) ptr; - ptr += sizeof(bot_randomstring_t); - randomstring->string = ptr; - ptr += strlen(chatmessagestring) + 1; - strcpy(randomstring->string, chatmessagestring); - // - random->numstrings++; - randomstring->next = random->firstrandomstring; - random->firstrandomstring = randomstring; - } //end if - } //end while - } //end while - //free the source after one pass - FreeSource(source); - } //end for - botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); - // -#ifdef DEBUG - botimport.Print(PRT_MESSAGE, "random strings %d msec\n", Sys_MilliSeconds() - starttime); - //BotDumpRandomStringList(randomlist); -#endif //DEBUG - // - return randomlist; -} //end of the function BotLoadRandomStrings -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *RandomString(char *name) -{ - bot_randomlist_t *random; - bot_randomstring_t *rs; - int i; - - for (random = randomstrings; random; random = random->next) - { - if (!strcmp(random->string, name)) - { - i = random() * random->numstrings; - for (rs = random->firstrandomstring; rs; rs = rs->next) - { - if (--i < 0) break; - } //end for - if (rs) - { - return rs->string; - } //end if - } //end for - } //end for - return NULL; -} //end of the function RandomString -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotDumpMatchTemplates(bot_matchtemplate_t *matches) -{ - FILE *fp; - bot_matchtemplate_t *mt; - bot_matchpiece_t *mp; - bot_matchstring_t *ms; - - fp = Log_FilePointer(); - if (!fp) return; - for (mt = matches; mt; mt = mt->next) - { - fprintf(fp, "{ " ); - for (mp = mt->first; mp; mp = mp->next) - { - if (mp->type == MT_STRING) - { - for (ms = mp->firststring; ms; ms = ms->next) - { - fprintf(fp, "\"%s\"", ms->string); - if (ms->next) fprintf(fp, "|"); - } //end for - } //end if - else if (mp->type == MT_VARIABLE) - { - fprintf(fp, "%d", mp->variable); - } //end else if - if (mp->next) fprintf(fp, ", "); - } //end for - fprintf(fp, " = (%d, %d);}\n", mt->type, mt->subtype); - } //end for -} //end of the function BotDumpMatchTemplates -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotFreeMatchPieces(bot_matchpiece_t *matchpieces) -{ - bot_matchpiece_t *mp, *nextmp; - bot_matchstring_t *ms, *nextms; - - for (mp = matchpieces; mp; mp = nextmp) - { - nextmp = mp->next; - if (mp->type == MT_STRING) - { - for (ms = mp->firststring; ms; ms = nextms) - { - nextms = ms->next; - FreeMemory(ms); - } //end for - } //end if - FreeMemory(mp); - } //end for -} //end of the function BotFreeMatchPieces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_matchpiece_t *BotLoadMatchPieces(source_t *source, char *endtoken) -{ - int lastwasvariable, emptystring; - token_t token; - bot_matchpiece_t *matchpiece, *firstpiece, *lastpiece; - bot_matchstring_t *matchstring, *lastmatchstring; - - firstpiece = NULL; - lastpiece = NULL; - // - lastwasvariable = qfalse; - // - while(PC_ReadToken(source, &token)) - { - if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER)) - { - if (token.intvalue < 0 || token.intvalue >= MAX_MATCHVARIABLES) - { - SourceError(source, "can't have more than %d match variables\n", MAX_MATCHVARIABLES); - FreeSource(source); - BotFreeMatchPieces(firstpiece); - return NULL; - } //end if - if (lastwasvariable) - { - SourceError(source, "not allowed to have adjacent variables\n"); - FreeSource(source); - BotFreeMatchPieces(firstpiece); - return NULL; - } //end if - lastwasvariable = qtrue; - // - matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t)); - matchpiece->type = MT_VARIABLE; - matchpiece->variable = token.intvalue; - matchpiece->next = NULL; - if (lastpiece) lastpiece->next = matchpiece; - else firstpiece = matchpiece; - lastpiece = matchpiece; - } //end if - else if (token.type == TT_STRING) - { - // - matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t)); - matchpiece->firststring = NULL; - matchpiece->type = MT_STRING; - matchpiece->variable = 0; - matchpiece->next = NULL; - if (lastpiece) lastpiece->next = matchpiece; - else firstpiece = matchpiece; - lastpiece = matchpiece; - // - lastmatchstring = NULL; - emptystring = qfalse; - // - do - { - if (matchpiece->firststring) - { - if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) - { - FreeSource(source); - BotFreeMatchPieces(firstpiece); - return NULL; - } //end if - } //end if - StripDoubleQuotes(token.string); - matchstring = (bot_matchstring_t *) GetClearedHunkMemory(sizeof(bot_matchstring_t) + strlen(token.string) + 1); - matchstring->string = (char *) matchstring + sizeof(bot_matchstring_t); - strcpy(matchstring->string, token.string); - if (!strlen(token.string)) emptystring = qtrue; - matchstring->next = NULL; - if (lastmatchstring) lastmatchstring->next = matchstring; - else matchpiece->firststring = matchstring; - lastmatchstring = matchstring; - } while(PC_CheckTokenString(source, "|")); - //if there was no empty string found - if (!emptystring) lastwasvariable = qfalse; - } //end if - else - { - SourceError(source, "invalid token %s\n", token.string); - FreeSource(source); - BotFreeMatchPieces(firstpiece); - return NULL; - } //end else - if (PC_CheckTokenString(source, endtoken)) break; - if (!PC_ExpectTokenString(source, ",")) - { - FreeSource(source); - BotFreeMatchPieces(firstpiece); - return NULL; - } //end if - } //end while - return firstpiece; -} //end of the function BotLoadMatchPieces -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotFreeMatchTemplates(bot_matchtemplate_t *mt) -{ - bot_matchtemplate_t *nextmt; - - for (; mt; mt = nextmt) - { - nextmt = mt->next; - BotFreeMatchPieces(mt->first); - FreeMemory(mt); - } //end for -} //end of the function BotFreeMatchTemplates -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_matchtemplate_t *BotLoadMatchTemplates(char *matchfile) -{ - source_t *source; - token_t token; - bot_matchtemplate_t *matchtemplate, *matches, *lastmatch; - unsigned long int context; - - PC_SetBaseFolder(BOTFILESBASEFOLDER); - source = LoadSourceFile(matchfile); - if (!source) - { - botimport.Print(PRT_ERROR, "counldn't load %s\n", matchfile); - return NULL; - } //end if - // - matches = NULL; //list with matches - lastmatch = NULL; //last match in the list - - while(PC_ReadToken(source, &token)) - { - if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER)) - { - SourceError(source, "expected integer, found %s\n", token.string); - BotFreeMatchTemplates(matches); - FreeSource(source); - return NULL; - } //end if - //the context - context = token.intvalue; - // - if (!PC_ExpectTokenString(source, "{")) - { - BotFreeMatchTemplates(matches); - FreeSource(source); - return NULL; - } //end if - // - while(PC_ReadToken(source, &token)) - { - if (!strcmp(token.string, "}")) break; - // - PC_UnreadLastToken(source); - // - matchtemplate = (bot_matchtemplate_t *) GetClearedHunkMemory(sizeof(bot_matchtemplate_t)); - matchtemplate->context = context; - matchtemplate->next = NULL; - //add the match template to the list - if (lastmatch) lastmatch->next = matchtemplate; - else matches = matchtemplate; - lastmatch = matchtemplate; - //load the match template - matchtemplate->first = BotLoadMatchPieces(source, "="); - if (!matchtemplate->first) - { - BotFreeMatchTemplates(matches); - return NULL; - } //end if - //read the match type - if (!PC_ExpectTokenString(source, "(") || - !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) - { - BotFreeMatchTemplates(matches); - FreeSource(source); - return NULL; - } //end if - matchtemplate->type = token.intvalue; - //read the match subtype - if (!PC_ExpectTokenString(source, ",") || - !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) - { - BotFreeMatchTemplates(matches); - FreeSource(source); - return NULL; - } //end if - matchtemplate->subtype = token.intvalue; - //read trailing punctuations - if (!PC_ExpectTokenString(source, ")") || - !PC_ExpectTokenString(source, ";")) - { - BotFreeMatchTemplates(matches); - FreeSource(source); - return NULL; - } //end if - } //end while - } //end while - //free the source - FreeSource(source); - botimport.Print(PRT_MESSAGE, "loaded %s\n", matchfile); - // - //BotDumpMatchTemplates(matches); - // - return matches; -} //end of the function BotLoadMatchTemplates -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int StringsMatch(bot_matchpiece_t *pieces, bot_match_t *match) -{ - int lastvariable, index; - char *strptr, *newstrptr; - bot_matchpiece_t *mp; - bot_matchstring_t *ms; - - //no last variable - lastvariable = -1; - //pointer to the string to compare the match string with - strptr = match->string; - //Log_Write("match: %s", strptr); - //compare the string with the current match string - for (mp = pieces; mp; mp = mp->next) - { - //if it is a piece of string - if (mp->type == MT_STRING) - { - newstrptr = NULL; - for (ms = mp->firststring; ms; ms = ms->next) - { - if (!strlen(ms->string)) - { - newstrptr = strptr; - break; - } //end if - //Log_Write("MT_STRING: %s", mp->string); - index = StringContains(strptr, ms->string, qfalse); - if (index >= 0) - { - newstrptr = strptr + index; - if (lastvariable >= 0) - { - match->variables[lastvariable].length = - (newstrptr - match->string) - match->variables[lastvariable].offset; - //newstrptr - match->variables[lastvariable].ptr; - lastvariable = -1; - break; - } //end if - else if (index == 0) - { - break; - } //end else - newstrptr = NULL; - } //end if - } //end for - if (!newstrptr) return qfalse; - strptr = newstrptr + strlen(ms->string); - } //end if - //if it is a variable piece of string - else if (mp->type == MT_VARIABLE) - { - //Log_Write("MT_VARIABLE"); - match->variables[mp->variable].offset = strptr - match->string; - lastvariable = mp->variable; - } //end else if - } //end for - //if a match was found - if (!mp && (lastvariable >= 0 || !strlen(strptr))) - { - //if the last piece was a variable string - if (lastvariable >= 0) - { - assert( match->variables[lastvariable].offset >= 0 ); // bk001204 - match->variables[lastvariable].length = - strlen(&match->string[ (int) match->variables[lastvariable].offset]); - } //end if - return qtrue; - } //end if - return qfalse; -} //end of the function StringsMatch -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotFindMatch(char *str, bot_match_t *match, unsigned long int context) -{ - int i; - bot_matchtemplate_t *ms; - - strncpy(match->string, str, MAX_MESSAGE_SIZE); - //remove any trailing enters - while(strlen(match->string) && - match->string[strlen(match->string)-1] == '\n') - { - match->string[strlen(match->string)-1] = '\0'; - } //end while - //compare the string with all the match strings - for (ms = matchtemplates; ms; ms = ms->next) - { - if (!(ms->context & context)) continue; - //reset the match variable offsets - for (i = 0; i < MAX_MATCHVARIABLES; i++) match->variables[i].offset = -1; - // - if (StringsMatch(ms->first, match)) - { - match->type = ms->type; - match->subtype = ms->subtype; - return qtrue; - } //end if - } //end for - return qfalse; -} //end of the function BotFindMatch -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size) -{ - if (variable < 0 || variable >= MAX_MATCHVARIABLES) - { - botimport.Print(PRT_FATAL, "BotMatchVariable: variable out of range\n"); - strcpy(buf, ""); - return; - } //end if - - if (match->variables[variable].offset >= 0) - { - if (match->variables[variable].length < size) - size = match->variables[variable].length+1; - assert( match->variables[variable].offset >= 0 ); // bk001204 - strncpy(buf, &match->string[ (int) match->variables[variable].offset], size-1); - buf[size-1] = '\0'; - } //end if - else - { - strcpy(buf, ""); - } //end else - return; -} //end of the function BotMatchVariable -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_stringlist_t *BotFindStringInList(bot_stringlist_t *list, char *string) -{ - bot_stringlist_t *s; - - for (s = list; s; s = s->next) - { - if (!strcmp(s->string, string)) return s; - } //end for - return NULL; -} //end of the function BotFindStringInList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_stringlist_t *BotCheckChatMessageIntegrety(char *message, bot_stringlist_t *stringlist) -{ - int i; - char *msgptr; - char temp[MAX_MESSAGE_SIZE]; - bot_stringlist_t *s; - - msgptr = message; - // - while(*msgptr) - { - if (*msgptr == ESCAPE_CHAR) - { - msgptr++; - switch(*msgptr) - { - case 'v': //variable - { - //step over the 'v' - msgptr++; - while(*msgptr && *msgptr != ESCAPE_CHAR) msgptr++; - //step over the trailing escape char - if (*msgptr) msgptr++; - break; - } //end case - case 'r': //random - { - //step over the 'r' - msgptr++; - for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++) - { - temp[i] = *msgptr++; - } //end while - temp[i] = '\0'; - //step over the trailing escape char - if (*msgptr) msgptr++; - //find the random keyword - if (!RandomString(temp)) - { - if (!BotFindStringInList(stringlist, temp)) - { - Log_Write("%s = {\"%s\"} //MISSING RANDOM\r\n", temp, temp); - s = GetClearedMemory(sizeof(bot_stringlist_t) + strlen(temp) + 1); - s->string = (char *) s + sizeof(bot_stringlist_t); - strcpy(s->string, temp); - s->next = stringlist; - stringlist = s; - } //end if - } //end if - break; - } //end case - default: - { - botimport.Print(PRT_FATAL, "BotCheckChatMessageIntegrety: message \"%s\" invalid escape char\n", message); - break; - } //end default - } //end switch - } //end if - else - { - msgptr++; - } //end else - } //end while - return stringlist; -} //end of the function BotCheckChatMessageIntegrety -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotCheckInitialChatIntegrety(bot_chat_t *chat) -{ - bot_chattype_t *t; - bot_chatmessage_t *cm; - bot_stringlist_t *stringlist, *s, *nexts; - - stringlist = NULL; - for (t = chat->types; t; t = t->next) - { - for (cm = t->firstchatmessage; cm; cm = cm->next) - { - stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist); - } //end for - } //end for - for (s = stringlist; s; s = nexts) - { - nexts = s->next; - FreeMemory(s); - } //end for -} //end of the function BotCheckInitialChatIntegrety -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotCheckReplyChatIntegrety(bot_replychat_t *replychat) -{ - bot_replychat_t *rp; - bot_chatmessage_t *cm; - bot_stringlist_t *stringlist, *s, *nexts; - - stringlist = NULL; - for (rp = replychat; rp; rp = rp->next) - { - for (cm = rp->firstchatmessage; cm; cm = cm->next) - { - stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist); - } //end for - } //end for - for (s = stringlist; s; s = nexts) - { - nexts = s->next; - FreeMemory(s); - } //end for -} //end of the function BotCheckReplyChatIntegrety -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotDumpReplyChat(bot_replychat_t *replychat) -{ - FILE *fp; - bot_replychat_t *rp; - bot_replychatkey_t *key; - bot_chatmessage_t *cm; - bot_matchpiece_t *mp; - - fp = Log_FilePointer(); - if (!fp) return; - fprintf(fp, "BotDumpReplyChat:\n"); - for (rp = replychat; rp; rp = rp->next) - { - fprintf(fp, "["); - for (key = rp->keys; key; key = key->next) - { - if (key->flags & RCKFL_AND) fprintf(fp, "&"); - else if (key->flags & RCKFL_NOT) fprintf(fp, "!"); - // - if (key->flags & RCKFL_NAME) fprintf(fp, "name"); - else if (key->flags & RCKFL_GENDERFEMALE) fprintf(fp, "female"); - else if (key->flags & RCKFL_GENDERMALE) fprintf(fp, "male"); - else if (key->flags & RCKFL_GENDERLESS) fprintf(fp, "it"); - else if (key->flags & RCKFL_VARIABLES) - { - fprintf(fp, "("); - for (mp = key->match; mp; mp = mp->next) - { - if (mp->type == MT_STRING) fprintf(fp, "\"%s\"", mp->firststring->string); - else fprintf(fp, "%d", mp->variable); - if (mp->next) fprintf(fp, ", "); - } //end for - fprintf(fp, ")"); - } //end if - else if (key->flags & RCKFL_STRING) - { - fprintf(fp, "\"%s\"", key->string); - } //end if - if (key->next) fprintf(fp, ", "); - else fprintf(fp, "] = %1.0f\n", rp->priority); - } //end for - fprintf(fp, "{\n"); - for (cm = rp->firstchatmessage; cm; cm = cm->next) - { - fprintf(fp, "\t\"%s\";\n", cm->chatmessage); - } //end for - fprintf(fp, "}\n"); - } //end for -} //end of the function BotDumpReplyChat -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotFreeReplyChat(bot_replychat_t *replychat) -{ - bot_replychat_t *rp, *nextrp; - bot_replychatkey_t *key, *nextkey; - bot_chatmessage_t *cm, *nextcm; - - for (rp = replychat; rp; rp = nextrp) - { - nextrp = rp->next; - for (key = rp->keys; key; key = nextkey) - { - nextkey = key->next; - if (key->match) BotFreeMatchPieces(key->match); - if (key->string) FreeMemory(key->string); - FreeMemory(key); - } //end for - for (cm = rp->firstchatmessage; cm; cm = nextcm) - { - nextcm = cm->next; - FreeMemory(cm); - } //end for - FreeMemory(rp); - } //end for -} //end of the function BotFreeReplyChat -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotCheckValidReplyChatKeySet(source_t *source, bot_replychatkey_t *keys) -{ - int allprefixed, hasvariableskey, hasstringkey; - bot_matchpiece_t *m; - bot_matchstring_t *ms; - bot_replychatkey_t *key, *key2; - - // - allprefixed = qtrue; - hasvariableskey = hasstringkey = qfalse; - for (key = keys; key; key = key->next) - { - if (!(key->flags & (RCKFL_AND|RCKFL_NOT))) - { - allprefixed = qfalse; - if (key->flags & RCKFL_VARIABLES) - { - for (m = key->match; m; m = m->next) - { - if (m->type == MT_VARIABLE) hasvariableskey = qtrue; - } //end for - } //end if - else if (key->flags & RCKFL_STRING) - { - hasstringkey = qtrue; - } //end else if - } //end if - else if ((key->flags & RCKFL_AND) && (key->flags & RCKFL_STRING)) - { - for (key2 = keys; key2; key2 = key2->next) - { - if (key2 == key) continue; - if (key2->flags & RCKFL_NOT) continue; - if (key2->flags & RCKFL_VARIABLES) - { - for (m = key2->match; m; m = m->next) - { - if (m->type == MT_STRING) - { - for (ms = m->firststring; ms; ms = ms->next) - { - if (StringContains(ms->string, key->string, qfalse) != -1) - { - break; - } //end if - } //end for - if (ms) break; - } //end if - else if (m->type == MT_VARIABLE) - { - break; - } //end if - } //end for - if (!m) - { - SourceWarning(source, "one of the match templates does not " - "leave space for the key %s with the & prefix", key->string); - } //end if - } //end if - } //end for - } //end else - if ((key->flags & RCKFL_NOT) && (key->flags & RCKFL_STRING)) - { - for (key2 = keys; key2; key2 = key2->next) - { - if (key2 == key) continue; - if (key2->flags & RCKFL_NOT) continue; - if (key2->flags & RCKFL_STRING) - { - if (StringContains(key2->string, key->string, qfalse) != -1) - { - SourceWarning(source, "the key %s with prefix ! is inside the key %s", key->string, key2->string); - } //end if - } //end if - else if (key2->flags & RCKFL_VARIABLES) - { - for (m = key2->match; m; m = m->next) - { - if (m->type == MT_STRING) - { - for (ms = m->firststring; ms; ms = ms->next) - { - if (StringContains(ms->string, key->string, qfalse) != -1) - { - SourceWarning(source, "the key %s with prefix ! is inside " - "the match template string %s", key->string, ms->string); - } //end if - } //end for - } //end if - } //end for - } //end else if - } //end for - } //end if - } //end for - if (allprefixed) SourceWarning(source, "all keys have a & or ! prefix"); - if (hasvariableskey && hasstringkey) - { - SourceWarning(source, "variables from the match template(s) could be " - "invalid when outputting one of the chat messages"); - } //end if -} //end of the function BotCheckValidReplyChatKeySet -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_replychat_t *BotLoadReplyChat(char *filename) -{ - char chatmessagestring[MAX_MESSAGE_SIZE]; - char namebuffer[MAX_MESSAGE_SIZE]; - source_t *source; - token_t token; - bot_chatmessage_t *chatmessage = NULL; - bot_replychat_t *replychat, *replychatlist; - bot_replychatkey_t *key; - - PC_SetBaseFolder(BOTFILESBASEFOLDER); - source = LoadSourceFile(filename); - if (!source) - { - botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); - return NULL; - } //end if - // - replychatlist = NULL; - // - while(PC_ReadToken(source, &token)) - { - if (strcmp(token.string, "[")) - { - SourceError(source, "expected [, found %s", token.string); - BotFreeReplyChat(replychatlist); - FreeSource(source); - return NULL; - } //end if - // - replychat = GetClearedHunkMemory(sizeof(bot_replychat_t)); - replychat->keys = NULL; - replychat->next = replychatlist; - replychatlist = replychat; - //read the keys, there must be at least one key - do - { - //allocate a key - key = (bot_replychatkey_t *) GetClearedHunkMemory(sizeof(bot_replychatkey_t)); - key->flags = 0; - key->string = NULL; - key->match = NULL; - key->next = replychat->keys; - replychat->keys = key; - //check for MUST BE PRESENT and MUST BE ABSENT keys - if (PC_CheckTokenString(source, "&")) key->flags |= RCKFL_AND; - else if (PC_CheckTokenString(source, "!")) key->flags |= RCKFL_NOT; - //special keys - if (PC_CheckTokenString(source, "name")) key->flags |= RCKFL_NAME; - else if (PC_CheckTokenString(source, "female")) key->flags |= RCKFL_GENDERFEMALE; - else if (PC_CheckTokenString(source, "male")) key->flags |= RCKFL_GENDERMALE; - else if (PC_CheckTokenString(source, "it")) key->flags |= RCKFL_GENDERLESS; - else if (PC_CheckTokenString(source, "(")) //match key - { - key->flags |= RCKFL_VARIABLES; - key->match = BotLoadMatchPieces(source, ")"); - if (!key->match) - { - BotFreeReplyChat(replychatlist); - return NULL; - } //end if - } //end else if - else if (PC_CheckTokenString(source, "<")) //bot names - { - key->flags |= RCKFL_BOTNAMES; - strcpy(namebuffer, ""); - do - { - if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) - { - BotFreeReplyChat(replychatlist); - FreeSource(source); - return NULL; - } //end if - StripDoubleQuotes(token.string); - if (strlen(namebuffer)) strcat(namebuffer, "\\"); - strcat(namebuffer, token.string); - } while(PC_CheckTokenString(source, ",")); - if (!PC_ExpectTokenString(source, ">")) - { - BotFreeReplyChat(replychatlist); - FreeSource(source); - return NULL; - } //end if - key->string = (char *) GetClearedHunkMemory(strlen(namebuffer) + 1); - strcpy(key->string, namebuffer); - } //end else if - else //normal string key - { - key->flags |= RCKFL_STRING; - if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) - { - BotFreeReplyChat(replychatlist); - FreeSource(source); - return NULL; - } //end if - StripDoubleQuotes(token.string); - key->string = (char *) GetClearedHunkMemory(strlen(token.string) + 1); - strcpy(key->string, token.string); - } //end else - // - PC_CheckTokenString(source, ","); - } while(!PC_CheckTokenString(source, "]")); - // - BotCheckValidReplyChatKeySet(source, replychat->keys); - //read the = sign and the priority - if (!PC_ExpectTokenString(source, "=") || - !PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) - { - BotFreeReplyChat(replychatlist); - FreeSource(source); - return NULL; - } //end if - replychat->priority = token.floatvalue; - //read the leading { - if (!PC_ExpectTokenString(source, "{")) - { - BotFreeReplyChat(replychatlist); - FreeSource(source); - return NULL; - } //end if - replychat->numchatmessages = 0; - //while the trailing } is not found - while(!PC_CheckTokenString(source, "}")) - { - if (!BotLoadChatMessage(source, chatmessagestring)) - { - BotFreeReplyChat(replychatlist); - FreeSource(source); - return NULL; - } //end if - chatmessage = (bot_chatmessage_t *) GetClearedHunkMemory(sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1); - chatmessage->chatmessage = (char *) chatmessage + sizeof(bot_chatmessage_t); - strcpy(chatmessage->chatmessage, chatmessagestring); - chatmessage->time = -2*CHATMESSAGE_RECENTTIME; - chatmessage->next = replychat->firstchatmessage; - //add the chat message to the reply chat - replychat->firstchatmessage = chatmessage; - replychat->numchatmessages++; - } //end while - } //end while - FreeSource(source); - botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); - // - //BotDumpReplyChat(replychatlist); - if (bot_developer) - { - BotCheckReplyChatIntegrety(replychatlist); - } //end if - // - if (!replychatlist) botimport.Print(PRT_MESSAGE, "no rchats\n"); - // - return replychatlist; -} //end of the function BotLoadReplyChat -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotDumpInitialChat(bot_chat_t *chat) -{ - bot_chattype_t *t; - bot_chatmessage_t *m; - - Log_Write("{"); - for (t = chat->types; t; t = t->next) - { - Log_Write(" type \"%s\"", t->name); - Log_Write(" {"); - Log_Write(" numchatmessages = %d", t->numchatmessages); - for (m = t->firstchatmessage; m; m = m->next) - { - Log_Write(" \"%s\"", m->chatmessage); - } //end for - Log_Write(" }"); - } //end for - Log_Write("}"); -} //end of the function BotDumpInitialChat -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_chat_t *BotLoadInitialChat(char *chatfile, char *chatname) -{ - int pass, foundchat, indent, size; - char *ptr = NULL; - char chatmessagestring[MAX_MESSAGE_SIZE]; - source_t *source; - token_t token; - bot_chat_t *chat = NULL; - bot_chattype_t *chattype = NULL; - bot_chatmessage_t *chatmessage = NULL; -#ifdef DEBUG - int starttime; - - starttime = Sys_MilliSeconds(); -#endif //DEBUG - // - size = 0; - foundchat = qfalse; - //a bot chat is parsed in two phases - for (pass = 0; pass < 2; pass++) - { - //allocate memory - if (pass && size) ptr = (char *) GetClearedMemory(size); - //load the source file - PC_SetBaseFolder(BOTFILESBASEFOLDER); - source = LoadSourceFile(chatfile); - if (!source) - { - botimport.Print(PRT_ERROR, "counldn't load %s\n", chatfile); - return NULL; - } //end if - //chat structure - if (pass) - { - chat = (bot_chat_t *) ptr; - ptr += sizeof(bot_chat_t); - } //end if - size = sizeof(bot_chat_t); - // - while(PC_ReadToken(source, &token)) - { - if (!strcmp(token.string, "chat")) - { - if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) - { - FreeSource(source); - return NULL; - } //end if - StripDoubleQuotes(token.string); - //after the chat name we expect a opening brace - if (!PC_ExpectTokenString(source, "{")) - { - FreeSource(source); - return NULL; - } //end if - //if the chat name is found - if (!Q_stricmp(token.string, chatname)) - { - foundchat = qtrue; - //read the chat types - while(1) - { - if (!PC_ExpectAnyToken(source, &token)) - { - FreeSource(source); - return NULL; - } //end if - if (!strcmp(token.string, "}")) break; - if (strcmp(token.string, "type")) - { - SourceError(source, "expected type found %s\n", token.string); - FreeSource(source); - return NULL; - } //end if - //expect the chat type name - if (!PC_ExpectTokenType(source, TT_STRING, 0, &token) || - !PC_ExpectTokenString(source, "{")) - { - FreeSource(source); - return NULL; - } //end if - StripDoubleQuotes(token.string); - if (pass) - { - chattype = (bot_chattype_t *) ptr; - strncpy(chattype->name, token.string, MAX_CHATTYPE_NAME); - chattype->firstchatmessage = NULL; - //add the chat type to the chat - chattype->next = chat->types; - chat->types = chattype; - // - ptr += sizeof(bot_chattype_t); - } //end if - size += sizeof(bot_chattype_t); - //read the chat messages - while(!PC_CheckTokenString(source, "}")) - { - if (!BotLoadChatMessage(source, chatmessagestring)) - { - FreeSource(source); - return NULL; - } //end if - if (pass) - { - chatmessage = (bot_chatmessage_t *) ptr; - chatmessage->time = -2*CHATMESSAGE_RECENTTIME; - //put the chat message in the list - chatmessage->next = chattype->firstchatmessage; - chattype->firstchatmessage = chatmessage; - //store the chat message - ptr += sizeof(bot_chatmessage_t); - chatmessage->chatmessage = ptr; - strcpy(chatmessage->chatmessage, chatmessagestring); - ptr += strlen(chatmessagestring) + 1; - //the number of chat messages increased - chattype->numchatmessages++; - } //end if - size += sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1; - } //end if - } //end while - } //end if - else //skip the bot chat - { - indent = 1; - while(indent) - { - if (!PC_ExpectAnyToken(source, &token)) - { - FreeSource(source); - return NULL; - } //end if - if (!strcmp(token.string, "{")) indent++; - else if (!strcmp(token.string, "}")) indent--; - } //end while - } //end else - } //end if - else - { - SourceError(source, "unknown definition %s\n", token.string); - FreeSource(source); - return NULL; - } //end else - } //end while - //free the source - FreeSource(source); - //if the requested character is not found - if (!foundchat) - { - botimport.Print(PRT_ERROR, "couldn't find chat %s in %s\n", chatname, chatfile); - return NULL; - } //end if - } //end for - // - botimport.Print(PRT_MESSAGE, "loaded %s from %s\n", chatname, chatfile); - // - //BotDumpInitialChat(chat); - if (bot_developer) - { - BotCheckInitialChatIntegrety(chat); - } //end if -#ifdef DEBUG - botimport.Print(PRT_MESSAGE, "initial chats loaded in %d msec\n", Sys_MilliSeconds() - starttime); -#endif //DEBUG - //character was read succesfully - return chat; -} //end of the function BotLoadInitialChat -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotFreeChatFile(int chatstate) -{ - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return; - if (cs->chat) FreeMemory(cs->chat); - cs->chat = NULL; -} //end of the function BotFreeChatFile -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotLoadChatFile(int chatstate, char *chatfile, char *chatname) -{ - bot_chatstate_t *cs; - int n, avail = 0; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return BLERR_CANNOTLOADICHAT; - BotFreeChatFile(chatstate); - - if (!LibVarGetValue("bot_reloadcharacters")) - { - avail = -1; - for( n = 0; n < MAX_CLIENTS; n++ ) { - if( !ichatdata[n] ) { - if( avail == -1 ) { - avail = n; - } - continue; - } - if( strcmp( chatfile, ichatdata[n]->filename ) != 0 ) { - continue; - } - if( strcmp( chatname, ichatdata[n]->chatname ) != 0 ) { - continue; - } - cs->chat = ichatdata[n]->chat; - // botimport.Print( PRT_MESSAGE, "retained %s from %s\n", chatname, chatfile ); - return BLERR_NOERROR; - } - - if( avail == -1 ) { - botimport.Print(PRT_FATAL, "ichatdata table full; couldn't load chat %s from %s\n", chatname, chatfile); - return BLERR_CANNOTLOADICHAT; - } - } - - cs->chat = BotLoadInitialChat(chatfile, chatname); - if (!cs->chat) - { - botimport.Print(PRT_FATAL, "couldn't load chat %s from %s\n", chatname, chatfile); - return BLERR_CANNOTLOADICHAT; - } //end if - if (!LibVarGetValue("bot_reloadcharacters")) - { - ichatdata[avail] = GetClearedMemory( sizeof(bot_ichatdata_t) ); - ichatdata[avail]->chat = cs->chat; - Q_strncpyz( ichatdata[avail]->chatname, chatname, sizeof(ichatdata[avail]->chatname) ); - Q_strncpyz( ichatdata[avail]->filename, chatfile, sizeof(ichatdata[avail]->filename) ); - } //end if - - return BLERR_NOERROR; -} //end of the function BotLoadChatFile -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotExpandChatMessage(char *outmessage, char *message, unsigned long mcontext, - bot_match_t *match, unsigned long vcontext, int reply) -{ - int num, len, i, expansion; - char *outputbuf, *ptr, *msgptr; - char temp[MAX_MESSAGE_SIZE]; - - expansion = qfalse; - msgptr = message; - outputbuf = outmessage; - len = 0; - // - while(*msgptr) - { - if (*msgptr == ESCAPE_CHAR) - { - msgptr++; - switch(*msgptr) - { - case 'v': //variable - { - msgptr++; - num = 0; - while(*msgptr && *msgptr != ESCAPE_CHAR) - { - num = num * 10 + (*msgptr++) - '0'; - } //end while - //step over the trailing escape char - if (*msgptr) msgptr++; - if (num > MAX_MATCHVARIABLES) - { - botimport.Print(PRT_ERROR, "BotConstructChat: message %s variable %d out of range\n", message, num); - return qfalse; - } //end if - if (match->variables[num].offset >= 0) - { - assert( match->variables[num].offset >= 0 ); // bk001204 - ptr = &match->string[ (int) match->variables[num].offset]; - for (i = 0; i < match->variables[num].length; i++) - { - temp[i] = ptr[i]; - } //end for - temp[i] = 0; - //if it's a reply message - if (reply) - { - //replace the reply synonyms in the variables - BotReplaceReplySynonyms(temp, vcontext); - } //end if - else - { - //replace synonyms in the variable context - BotReplaceSynonyms(temp, vcontext); - } //end else - // - if (len + strlen(temp) >= MAX_MESSAGE_SIZE) - { - botimport.Print(PRT_ERROR, "BotConstructChat: message %s too long\n", message); - return qfalse; - } //end if - strcpy(&outputbuf[len], temp); - len += strlen(temp); - } //end if - break; - } //end case - case 'r': //random - { - msgptr++; - for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++) - { - temp[i] = *msgptr++; - } //end while - temp[i] = '\0'; - //step over the trailing escape char - if (*msgptr) msgptr++; - //find the random keyword - ptr = RandomString(temp); - if (!ptr) - { - botimport.Print(PRT_ERROR, "BotConstructChat: unknown random string %s\n", temp); - return qfalse; - } //end if - if (len + strlen(ptr) >= MAX_MESSAGE_SIZE) - { - botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message); - return qfalse; - } //end if - strcpy(&outputbuf[len], ptr); - len += strlen(ptr); - expansion = qtrue; - break; - } //end case - default: - { - botimport.Print(PRT_FATAL, "BotConstructChat: message \"%s\" invalid escape char\n", message); - break; - } //end default - } //end switch - } //end if - else - { - outputbuf[len++] = *msgptr++; - if (len >= MAX_MESSAGE_SIZE) - { - botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message); - break; - } //end if - } //end else - } //end while - outputbuf[len] = '\0'; - //replace synonyms weighted in the message context - BotReplaceWeightedSynonyms(outputbuf, mcontext); - //return true if a random was expanded - return expansion; -} //end of the function BotExpandChatMessage -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotConstructChatMessage(bot_chatstate_t *chatstate, char *message, unsigned long mcontext, - bot_match_t *match, unsigned long vcontext, int reply) -{ - int i; - char srcmessage[MAX_MESSAGE_SIZE]; - - strcpy(srcmessage, message); - for (i = 0; i < 10; i++) - { - if (!BotExpandChatMessage(chatstate->chatmessage, srcmessage, mcontext, match, vcontext, reply)) - { - break; - } //end if - strcpy(srcmessage, chatstate->chatmessage); - } //end for - if (i >= 10) - { - botimport.Print(PRT_WARNING, "too many expansions in chat message\n"); - botimport.Print(PRT_WARNING, "%s\n", chatstate->chatmessage); - } //end if -} //end of the function BotConstructChatMessage -//=========================================================================== -// randomly chooses one of the chat message of the given type -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *BotChooseInitialChatMessage(bot_chatstate_t *cs, char *type) -{ - int n, numchatmessages; - float besttime; - bot_chattype_t *t; - bot_chatmessage_t *m, *bestchatmessage; - bot_chat_t *chat; - - chat = cs->chat; - for (t = chat->types; t; t = t->next) - { - if (!Q_stricmp(t->name, type)) - { - numchatmessages = 0; - for (m = t->firstchatmessage; m; m = m->next) - { - if (m->time > AAS_Time()) continue; - numchatmessages++; - } //end if - //if all chat messages have been used recently - if (numchatmessages <= 0) - { - besttime = 0; - bestchatmessage = NULL; - for (m = t->firstchatmessage; m; m = m->next) - { - if (!besttime || m->time < besttime) - { - bestchatmessage = m; - besttime = m->time; - } //end if - } //end for - if (bestchatmessage) return bestchatmessage->chatmessage; - } //end if - else //choose a chat message randomly - { - n = random() * numchatmessages; - for (m = t->firstchatmessage; m; m = m->next) - { - if (m->time > AAS_Time()) continue; - if (--n < 0) - { - m->time = AAS_Time() + CHATMESSAGE_RECENTTIME; - return m->chatmessage; - } //end if - } //end for - } //end else - return NULL; - } //end if - } //end for - return NULL; -} //end of the function BotChooseInitialChatMessage -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotNumInitialChats(int chatstate, char *type) -{ - bot_chatstate_t *cs; - bot_chattype_t *t; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return 0; - - for (t = cs->chat->types; t; t = t->next) - { - if (!Q_stricmp(t->name, type)) - { - if (LibVarGetValue("bot_testichat")) { - botimport.Print(PRT_MESSAGE, "%s has %d chat lines\n", type, t->numchatmessages); - botimport.Print(PRT_MESSAGE, "-------------------\n"); - } - return t->numchatmessages; - } //end if - } //end for - return 0; -} //end of the function BotNumInitialChats -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7) -{ - char *message; - int index; - bot_match_t match; - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return; - //if no chat file is loaded - if (!cs->chat) return; - //choose a chat message randomly of the given type - message = BotChooseInitialChatMessage(cs, type); - //if there's no message of the given type - if (!message) - { -#ifdef DEBUG - botimport.Print(PRT_MESSAGE, "no chat messages of type %s\n", type); -#endif //DEBUG - return; - } //end if - // - Com_Memset(&match, 0, sizeof(match)); - index = 0; - if( var0 ) { - strcat(match.string, var0); - match.variables[0].offset = index; - match.variables[0].length = strlen(var0); - index += strlen(var0); - } - if( var1 ) { - strcat(match.string, var1); - match.variables[1].offset = index; - match.variables[1].length = strlen(var1); - index += strlen(var1); - } - if( var2 ) { - strcat(match.string, var2); - match.variables[2].offset = index; - match.variables[2].length = strlen(var2); - index += strlen(var2); - } - if( var3 ) { - strcat(match.string, var3); - match.variables[3].offset = index; - match.variables[3].length = strlen(var3); - index += strlen(var3); - } - if( var4 ) { - strcat(match.string, var4); - match.variables[4].offset = index; - match.variables[4].length = strlen(var4); - index += strlen(var4); - } - if( var5 ) { - strcat(match.string, var5); - match.variables[5].offset = index; - match.variables[5].length = strlen(var5); - index += strlen(var5); - } - if( var6 ) { - strcat(match.string, var6); - match.variables[6].offset = index; - match.variables[6].length = strlen(var6); - index += strlen(var6); - } - if( var7 ) { - strcat(match.string, var7); - match.variables[7].offset = index; - match.variables[7].length = strlen(var7); - index += strlen(var7); - } - // - BotConstructChatMessage(cs, message, mcontext, &match, 0, qfalse); -} //end of the function BotInitialChat -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotPrintReplyChatKeys(bot_replychat_t *replychat) -{ - bot_replychatkey_t *key; - bot_matchpiece_t *mp; - - botimport.Print(PRT_MESSAGE, "["); - for (key = replychat->keys; key; key = key->next) - { - if (key->flags & RCKFL_AND) botimport.Print(PRT_MESSAGE, "&"); - else if (key->flags & RCKFL_NOT) botimport.Print(PRT_MESSAGE, "!"); - // - if (key->flags & RCKFL_NAME) botimport.Print(PRT_MESSAGE, "name"); - else if (key->flags & RCKFL_GENDERFEMALE) botimport.Print(PRT_MESSAGE, "female"); - else if (key->flags & RCKFL_GENDERMALE) botimport.Print(PRT_MESSAGE, "male"); - else if (key->flags & RCKFL_GENDERLESS) botimport.Print(PRT_MESSAGE, "it"); - else if (key->flags & RCKFL_VARIABLES) - { - botimport.Print(PRT_MESSAGE, "("); - for (mp = key->match; mp; mp = mp->next) - { - if (mp->type == MT_STRING) botimport.Print(PRT_MESSAGE, "\"%s\"", mp->firststring->string); - else botimport.Print(PRT_MESSAGE, "%d", mp->variable); - if (mp->next) botimport.Print(PRT_MESSAGE, ", "); - } //end for - botimport.Print(PRT_MESSAGE, ")"); - } //end if - else if (key->flags & RCKFL_STRING) - { - botimport.Print(PRT_MESSAGE, "\"%s\"", key->string); - } //end if - if (key->next) botimport.Print(PRT_MESSAGE, ", "); - else botimport.Print(PRT_MESSAGE, "] = %1.0f\n", replychat->priority); - } //end for - botimport.Print(PRT_MESSAGE, "{\n"); -} //end of the function BotPrintReplyChatKeys -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7) -{ - bot_replychat_t *rchat, *bestrchat; - bot_replychatkey_t *key; - bot_chatmessage_t *m, *bestchatmessage; - bot_match_t match, bestmatch; - int bestpriority, num, found, res, numchatmessages, index; - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return qfalse; - Com_Memset(&match, 0, sizeof(bot_match_t)); - strcpy(match.string, message); - bestpriority = -1; - bestchatmessage = NULL; - bestrchat = NULL; - //go through all the reply chats - for (rchat = replychats; rchat; rchat = rchat->next) - { - found = qfalse; - for (key = rchat->keys; key; key = key->next) - { - res = qfalse; - //get the match result - if (key->flags & RCKFL_NAME) res = (StringContains(message, cs->name, qfalse) != -1); - else if (key->flags & RCKFL_BOTNAMES) res = (StringContains(key->string, cs->name, qfalse) != -1); - else if (key->flags & RCKFL_GENDERFEMALE) res = (cs->gender == CHAT_GENDERFEMALE); - else if (key->flags & RCKFL_GENDERMALE) res = (cs->gender == CHAT_GENDERMALE); - else if (key->flags & RCKFL_GENDERLESS) res = (cs->gender == CHAT_GENDERLESS); - else if (key->flags & RCKFL_VARIABLES) res = StringsMatch(key->match, &match); - else if (key->flags & RCKFL_STRING) res = (StringContainsWord(message, key->string, qfalse) != NULL); - //if the key must be present - if (key->flags & RCKFL_AND) - { - if (!res) - { - found = qfalse; - break; - } //end if - } //end else if - //if the key must be absent - else if (key->flags & RCKFL_NOT) - { - if (res) - { - found = qfalse; - break; - } //end if - } //end if - else if (res) - { - found = qtrue; - } //end else - } //end for - // - if (found) - { - if (rchat->priority > bestpriority) - { - numchatmessages = 0; - for (m = rchat->firstchatmessage; m; m = m->next) - { - if (m->time > AAS_Time()) continue; - numchatmessages++; - } //end if - num = random() * numchatmessages; - for (m = rchat->firstchatmessage; m; m = m->next) - { - if (--num < 0) break; - if (m->time > AAS_Time()) continue; - } //end for - //if the reply chat has a message - if (m) - { - Com_Memcpy(&bestmatch, &match, sizeof(bot_match_t)); - bestchatmessage = m; - bestrchat = rchat; - bestpriority = rchat->priority; - } //end if - } //end if - } //end if - } //end for - if (bestchatmessage) - { - index = strlen(bestmatch.string); - if( var0 ) { - strcat(bestmatch.string, var0); - bestmatch.variables[0].offset = index; - bestmatch.variables[0].length = strlen(var0); - index += strlen(var0); - } - if( var1 ) { - strcat(bestmatch.string, var1); - bestmatch.variables[1].offset = index; - bestmatch.variables[1].length = strlen(var1); - index += strlen(var1); - } - if( var2 ) { - strcat(bestmatch.string, var2); - bestmatch.variables[2].offset = index; - bestmatch.variables[2].length = strlen(var2); - index += strlen(var2); - } - if( var3 ) { - strcat(bestmatch.string, var3); - bestmatch.variables[3].offset = index; - bestmatch.variables[3].length = strlen(var3); - index += strlen(var3); - } - if( var4 ) { - strcat(bestmatch.string, var4); - bestmatch.variables[4].offset = index; - bestmatch.variables[4].length = strlen(var4); - index += strlen(var4); - } - if( var5 ) { - strcat(bestmatch.string, var5); - bestmatch.variables[5].offset = index; - bestmatch.variables[5].length = strlen(var5); - index += strlen(var5); - } - if( var6 ) { - strcat(bestmatch.string, var6); - bestmatch.variables[6].offset = index; - bestmatch.variables[6].length = strlen(var6); - index += strlen(var6); - } - if( var7 ) { - strcat(bestmatch.string, var7); - bestmatch.variables[7].offset = index; - bestmatch.variables[7].length = strlen(var7); - index += strlen(var7); - } - if (LibVarGetValue("bot_testrchat")) - { - for (m = bestrchat->firstchatmessage; m; m = m->next) - { - BotConstructChatMessage(cs, m->chatmessage, mcontext, &bestmatch, vcontext, qtrue); - BotRemoveTildes(cs->chatmessage); - botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage); - } //end if - } //end if - else - { - bestchatmessage->time = AAS_Time() + CHATMESSAGE_RECENTTIME; - BotConstructChatMessage(cs, bestchatmessage->chatmessage, mcontext, &bestmatch, vcontext, qtrue); - } //end else - return qtrue; - } //end if - return qfalse; -} //end of the function BotReplyChat -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotChatLength(int chatstate) -{ - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return 0; - return strlen(cs->chatmessage); -} //end of the function BotChatLength -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotEnterChat(int chatstate, int clientto, int sendto) -{ - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return; - - if (strlen(cs->chatmessage)) - { - BotRemoveTildes(cs->chatmessage); - if (LibVarGetValue("bot_testichat")) { - botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage); - } - else { - switch(sendto) { - case CHAT_TEAM: - EA_Command(cs->client, va("say_team %s", cs->chatmessage)); - break; - case CHAT_TELL: - EA_Command(cs->client, va("tell %d %s", clientto, cs->chatmessage)); - break; - default: //CHAT_ALL - EA_Command(cs->client, va("say %s", cs->chatmessage)); - break; - } - } - //clear the chat message from the state - strcpy(cs->chatmessage, ""); - } //end if -} //end of the function BotEnterChat -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotGetChatMessage(int chatstate, char *buf, int size) -{ - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return; - - BotRemoveTildes(cs->chatmessage); - strncpy(buf, cs->chatmessage, size-1); - buf[size-1] = '\0'; - //clear the chat message from the state - strcpy(cs->chatmessage, ""); -} //end of the function BotGetChatMessage -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotSetChatGender(int chatstate, int gender) -{ - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return; - switch(gender) - { - case CHAT_GENDERFEMALE: cs->gender = CHAT_GENDERFEMALE; break; - case CHAT_GENDERMALE: cs->gender = CHAT_GENDERMALE; break; - default: cs->gender = CHAT_GENDERLESS; break; - } //end switch -} //end of the function BotSetChatGender -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotSetChatName(int chatstate, char *name, int client) -{ - bot_chatstate_t *cs; - - cs = BotChatStateFromHandle(chatstate); - if (!cs) return; - cs->client = client; - Com_Memset(cs->name, 0, sizeof(cs->name)); - strncpy(cs->name, name, sizeof(cs->name)); - cs->name[sizeof(cs->name)-1] = '\0'; -} //end of the function BotSetChatName -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotResetChatAI(void) -{ - bot_replychat_t *rchat; - bot_chatmessage_t *m; - - for (rchat = replychats; rchat; rchat = rchat->next) - { - for (m = rchat->firstchatmessage; m; m = m->next) - { - m->time = 0; - } //end for - } //end for -} //end of the function BotResetChatAI -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -int BotAllocChatState(void) -{ - int i; - - for (i = 1; i <= MAX_CLIENTS; i++) - { - if (!botchatstates[i]) - { - botchatstates[i] = GetClearedMemory(sizeof(bot_chatstate_t)); - return i; - } //end if - } //end for - return 0; -} //end of the function BotAllocChatState -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -void BotFreeChatState(int handle) -{ - bot_chatstate_t *cs; - bot_consolemessage_t m; - int h; - - if (handle <= 0 || handle > MAX_CLIENTS) - { - botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle); - return; - } //end if - if (!botchatstates[handle]) - { - botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle); - return; - } //end if - cs = botchatstates[handle]; - if (LibVarGetValue("bot_reloadcharacters")) - { - BotFreeChatFile(handle); - } //end if - //free all the console messages left in the chat state - for (h = BotNextConsoleMessage(handle, &m); h; h = BotNextConsoleMessage(handle, &m)) - { - //remove the console message - BotRemoveConsoleMessage(handle, h); - } //end for - FreeMemory(botchatstates[handle]); - botchatstates[handle] = NULL; -} //end of the function BotFreeChatState -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotSetupChatAI(void) -{ - char *file; - -#ifdef DEBUG - int starttime = Sys_MilliSeconds(); -#endif //DEBUG - - file = LibVarString("synfile", "syn.c"); - synonyms = BotLoadSynonyms(file); - file = LibVarString("rndfile", "rnd.c"); - randomstrings = BotLoadRandomStrings(file); - file = LibVarString("matchfile", "match.c"); - matchtemplates = BotLoadMatchTemplates(file); - // - if (!LibVarValue("nochat", "0")) - { - file = LibVarString("rchatfile", "rchat.c"); - replychats = BotLoadReplyChat(file); - } //end if - - InitConsoleMessageHeap(); - -#ifdef DEBUG - botimport.Print(PRT_MESSAGE, "setup chat AI %d msec\n", Sys_MilliSeconds() - starttime); -#endif //DEBUG - return BLERR_NOERROR; -} //end of the function BotSetupChatAI -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotShutdownChatAI(void) -{ - int i; - - //free all remaining chat states - for(i = 0; i < MAX_CLIENTS; i++) - { - if (botchatstates[i]) - { - BotFreeChatState(i); - } //end if - } //end for - //free all cached chats - for(i = 0; i < MAX_CLIENTS; i++) - { - if (ichatdata[i]) - { - FreeMemory(ichatdata[i]->chat); - FreeMemory(ichatdata[i]); - ichatdata[i] = NULL; - } //end if - } //end for - if (consolemessageheap) FreeMemory(consolemessageheap); - consolemessageheap = NULL; - if (matchtemplates) BotFreeMatchTemplates(matchtemplates); - matchtemplates = NULL; - if (randomstrings) FreeMemory(randomstrings); - randomstrings = NULL; - if (synonyms) FreeMemory(synonyms); - synonyms = NULL; - if (replychats) BotFreeReplyChat(replychats); - replychats = NULL; -} //end of the function BotShutdownChatAI diff --git a/tools/quake3/bspc/deps/botlib/be_ai_chat.h b/tools/quake3/bspc/deps/botlib/be_ai_chat.h deleted file mode 100644 index 53a56d7b..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_chat.h +++ /dev/null @@ -1,113 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// -/***************************************************************************** - * name: be_ai_chat.h - * - * desc: char AI - * - * $Archive: /source/code/botlib/be_ai_chat.h $ - * - *****************************************************************************/ - -#define MAX_MESSAGE_SIZE 256 -#define MAX_CHATTYPE_NAME 32 -#define MAX_MATCHVARIABLES 8 - -#define CHAT_GENDERLESS 0 -#define CHAT_GENDERFEMALE 1 -#define CHAT_GENDERMALE 2 - -#define CHAT_ALL 0 -#define CHAT_TEAM 1 -#define CHAT_TELL 2 - -//a console message -typedef struct bot_consolemessage_s -{ - int handle; - float time; //message time - int type; //message type - char message[MAX_MESSAGE_SIZE]; //message - struct bot_consolemessage_s *prev, *next; //prev and next in list -} bot_consolemessage_t; - -//match variable -typedef struct bot_matchvariable_s -{ - char offset; - int length; -} bot_matchvariable_t; -//returned to AI when a match is found -typedef struct bot_match_s -{ - char string[MAX_MESSAGE_SIZE]; - int type; - int subtype; - bot_matchvariable_t variables[MAX_MATCHVARIABLES]; -} bot_match_t; - -//setup the chat AI -int BotSetupChatAI(void); -//shutdown the chat AI -void BotShutdownChatAI(void); -//returns the handle to a newly allocated chat state -int BotAllocChatState(void); -//frees the chatstate -void BotFreeChatState(int handle); -//adds a console message to the chat state -void BotQueueConsoleMessage(int chatstate, int type, char *message); -//removes the console message from the chat state -void BotRemoveConsoleMessage(int chatstate, int handle); -//returns the next console message from the state -int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm); -//returns the number of console messages currently stored in the state -int BotNumConsoleMessages(int chatstate); -//selects a chat message of the given type -void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); -//returns the number of initial chat messages of the given type -int BotNumInitialChats(int chatstate, char *type); -//find and select a reply for the given message -int BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); -//returns the length of the currently selected chat message -int BotChatLength(int chatstate); -//enters the selected chat message -void BotEnterChat(int chatstate, int clientto, int sendto); -//get the chat message ready to be output -void BotGetChatMessage(int chatstate, char *buf, int size); -//checks if the first string contains the second one, returns index into first string or -1 if not found -int StringContains(char *str1, char *str2, int casesensitive); -//finds a match for the given string using the match templates -int BotFindMatch(char *str, bot_match_t *match, unsigned long int context); -//returns a variable from a match -void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size); -//unify all the white spaces in the string -void UnifyWhiteSpaces(char *string); -//replace all the context related synonyms in the string -void BotReplaceSynonyms(char *string, unsigned long int context); -//loads a chat file for the chat state -int BotLoadChatFile(int chatstate, char *chatfile, char *chatname); -//store the gender of the bot in the chat state -void BotSetChatGender(int chatstate, int gender); -//store the bot name in the chat state -void BotSetChatName(int chatstate, char *name, int client); - diff --git a/tools/quake3/bspc/deps/botlib/be_ai_gen.c b/tools/quake3/bspc/deps/botlib/be_ai_gen.c deleted file mode 100644 index 3892a2a0..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_gen.c +++ /dev/null @@ -1,134 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_ai_gen.c - * - * desc: genetic selection - * - * $Archive: /MissionPack/code/botlib/be_ai_gen.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_log.h" -#include "l_utils.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_ai_gen.h" - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int GeneticSelection(int numranks, float *rankings) -{ - float sum, select; - int i, index; - - sum = 0; - for (i = 0; i < numranks; i++) - { - if (rankings[i] < 0) continue; - sum += rankings[i]; - } //end for - if (sum > 0) - { - //select a bot where the ones with the higest rankings have - //the highest chance of being selected - select = random() * sum; - for (i = 0; i < numranks; i++) - { - if (rankings[i] < 0) continue; - sum -= rankings[i]; - if (sum <= 0) return i; - } //end for - } //end if - //select a bot randomly - index = random() * numranks; - for (i = 0; i < numranks; i++) - { - if (rankings[index] >= 0) return index; - index = (index + 1) % numranks; - } //end for - return 0; -} //end of the function GeneticSelection -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child) -{ - float rankings[256], max; - int i; - - if (numranks > 256) - { - botimport.Print(PRT_WARNING, "GeneticParentsAndChildSelection: too many bots\n"); - *parent1 = *parent2 = *child = 0; - return qfalse; - } //end if - for (max = 0, i = 0; i < numranks; i++) - { - if (ranks[i] < 0) continue; - max++; - } //end for - if (max < 3) - { - botimport.Print(PRT_WARNING, "GeneticParentsAndChildSelection: too few valid bots\n"); - *parent1 = *parent2 = *child = 0; - return qfalse; - } //end if - Com_Memcpy(rankings, ranks, sizeof(float) * numranks); - //select first parent - *parent1 = GeneticSelection(numranks, rankings); - rankings[*parent1] = -1; - //select second parent - *parent2 = GeneticSelection(numranks, rankings); - rankings[*parent2] = -1; - //reverse the rankings - max = 0; - for (i = 0; i < numranks; i++) - { - if (rankings[i] < 0) continue; - if (rankings[i] > max) max = rankings[i]; - } //end for - for (i = 0; i < numranks; i++) - { - if (rankings[i] < 0) continue; - rankings[i] = max - rankings[i]; - } //end for - //select child - *child = GeneticSelection(numranks, rankings); - return qtrue; -} //end of the function GeneticParentsAndChildSelection diff --git a/tools/quake3/bspc/deps/botlib/be_ai_gen.h b/tools/quake3/bspc/deps/botlib/be_ai_gen.h deleted file mode 100644 index ce9ba92f..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_gen.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// - -/***************************************************************************** - * name: be_ai_gen.h - * - * desc: genetic selection - * - * $Archive: /source/code/botlib/be_ai_gen.h $ - * - *****************************************************************************/ - -int GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child); diff --git a/tools/quake3/bspc/deps/botlib/be_ai_goal.c b/tools/quake3/bspc/deps/botlib/be_ai_goal.c deleted file mode 100644 index 9eaeb626..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_goal.c +++ /dev/null @@ -1,1821 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_ai_goal.c - * - * desc: goal AI - * - * $Archive: /MissionPack/code/botlib/be_ai_goal.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_utils.h" -#include "l_libvar.h" -#include "l_memory.h" -#include "l_log.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_ai_weight.h" -#include "be_ai_goal.h" -#include "be_ai_move.h" - -//#define DEBUG_AI_GOAL -#ifdef RANDOMIZE -#define UNDECIDEDFUZZY -#endif //RANDOMIZE -#define DROPPEDWEIGHT -//minimum avoid goal time -#define AVOID_MINIMUM_TIME 10 -//default avoid goal time -#define AVOID_DEFAULT_TIME 30 -//avoid dropped goal time -#define AVOID_DROPPED_TIME 10 -// -#define TRAVELTIME_SCALE 0.01 -//item flags -#define IFL_NOTFREE 1 //not in free for all -#define IFL_NOTTEAM 2 //not in team play -#define IFL_NOTSINGLE 4 //not in single player -#define IFL_NOTBOT 8 //bot should never go for this -#define IFL_ROAM 16 //bot roam goal - -//location in the map "target_location" -typedef struct maplocation_s -{ - vec3_t origin; - int areanum; - char name[MAX_EPAIRKEY]; - struct maplocation_s *next; -} maplocation_t; - -//camp spots "info_camp" -typedef struct campspot_s -{ - vec3_t origin; - int areanum; - char name[MAX_EPAIRKEY]; - float range; - float weight; - float wait; - float random; - struct campspot_s *next; -} campspot_t; - -//FIXME: these are game specific -typedef enum { - GT_FFA, // free for all - GT_TOURNAMENT, // one on one tournament - GT_SINGLE_PLAYER, // single player tournament - - //-- team games go after this -- - - GT_TEAM, // team deathmatch - GT_CTF, // capture the flag -#ifdef MISSIONPACK - GT_1FCTF, - GT_OBELISK, - GT_HARVESTER, -#endif - GT_MAX_GAME_TYPE -} gametype_t; - -typedef struct levelitem_s -{ - int number; //number of the level item - int iteminfo; //index into the item info - int flags; //item flags - float weight; //fixed roam weight - vec3_t origin; //origin of the item - int goalareanum; //area the item is in - vec3_t goalorigin; //goal origin within the area - int entitynum; //entity number - float timeout; //item is removed after this time - struct levelitem_s *prev, *next; -} levelitem_t; - -typedef struct iteminfo_s -{ - char classname[32]; //classname of the item - char name[MAX_STRINGFIELD]; //name of the item - char model[MAX_STRINGFIELD]; //model of the item - int modelindex; //model index - int type; //item type - int index; //index in the inventory - float respawntime; //respawn time - vec3_t mins; //mins of the item - vec3_t maxs; //maxs of the item - int number; //number of the item info -} iteminfo_t; - -#define ITEMINFO_OFS(x) (int)&(((iteminfo_t *)0)->x) - -fielddef_t iteminfo_fields[] = -{ -{"name", ITEMINFO_OFS(name), FT_STRING}, -{"model", ITEMINFO_OFS(model), FT_STRING}, -{"modelindex", ITEMINFO_OFS(modelindex), FT_INT}, -{"type", ITEMINFO_OFS(type), FT_INT}, -{"index", ITEMINFO_OFS(index), FT_INT}, -{"respawntime", ITEMINFO_OFS(respawntime), FT_FLOAT}, -{"mins", ITEMINFO_OFS(mins), FT_FLOAT|FT_ARRAY, 3}, -{"maxs", ITEMINFO_OFS(maxs), FT_FLOAT|FT_ARRAY, 3}, -{0, 0, 0} -}; - -structdef_t iteminfo_struct = -{ - sizeof(iteminfo_t), iteminfo_fields -}; - -typedef struct itemconfig_s -{ - int numiteminfo; - iteminfo_t *iteminfo; -} itemconfig_t; - -//goal state -typedef struct bot_goalstate_s -{ - struct weightconfig_s *itemweightconfig; //weight config - int *itemweightindex; //index from item to weight - // - int client; //client using this goal state - int lastreachabilityarea; //last area with reachabilities the bot was in - // - bot_goal_t goalstack[MAX_GOALSTACK]; //goal stack - int goalstacktop; //the top of the goal stack - // - int avoidgoals[MAX_AVOIDGOALS]; //goals to avoid - float avoidgoaltimes[MAX_AVOIDGOALS]; //times to avoid the goals -} bot_goalstate_t; - -bot_goalstate_t *botgoalstates[MAX_CLIENTS + 1]; // bk001206 - FIXME: init? -//item configuration -itemconfig_t *itemconfig = NULL; // bk001206 - init -//level items -levelitem_t *levelitemheap = NULL; // bk001206 - init -levelitem_t *freelevelitems = NULL; // bk001206 - init -levelitem_t *levelitems = NULL; // bk001206 - init -int numlevelitems = 0; -//map locations -maplocation_t *maplocations = NULL; // bk001206 - init -//camp spots -campspot_t *campspots = NULL; // bk001206 - init -//the game type -int g_gametype = 0; // bk001206 - init -//additional dropped item weight -libvar_t *droppedweight = NULL; // bk001206 - init - -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -bot_goalstate_t *BotGoalStateFromHandle(int handle) -{ - if (handle <= 0 || handle > MAX_CLIENTS) - { - botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle); - return NULL; - } //end if - if (!botgoalstates[handle]) - { - botimport.Print(PRT_FATAL, "invalid goal state %d\n", handle); - return NULL; - } //end if - return botgoalstates[handle]; -} //end of the function BotGoalStateFromHandle -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child) -{ - bot_goalstate_t *p1, *p2, *c; - - p1 = BotGoalStateFromHandle(parent1); - p2 = BotGoalStateFromHandle(parent2); - c = BotGoalStateFromHandle(child); - - InterbreedWeightConfigs(p1->itemweightconfig, p2->itemweightconfig, - c->itemweightconfig); -} //end of the function BotInterbreedingGoalFuzzyLogic -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotSaveGoalFuzzyLogic(int goalstate, char *filename) -{ - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - - //WriteWeightConfig(filename, gs->itemweightconfig); -} //end of the function BotSaveGoalFuzzyLogic -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotMutateGoalFuzzyLogic(int goalstate, float range) -{ - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - - EvolveWeightConfig(gs->itemweightconfig); -} //end of the function BotMutateGoalFuzzyLogic -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -itemconfig_t *LoadItemConfig(char *filename) -{ - int max_iteminfo; - token_t token; - char path[MAX_PATH]; - source_t *source; - itemconfig_t *ic; - iteminfo_t *ii; - - max_iteminfo = (int) LibVarValue("max_iteminfo", "256"); - if (max_iteminfo < 0) - { - botimport.Print(PRT_ERROR, "max_iteminfo = %d\n", max_iteminfo); - max_iteminfo = 256; - LibVarSet( "max_iteminfo", "256" ); - } - - strncpy( path, filename, MAX_PATH ); - PC_SetBaseFolder(BOTFILESBASEFOLDER); - source = LoadSourceFile( path ); - if( !source ) { - botimport.Print( PRT_ERROR, "counldn't load %s\n", path ); - return NULL; - } //end if - //initialize item config - ic = (itemconfig_t *) GetClearedHunkMemory(sizeof(itemconfig_t) + - max_iteminfo * sizeof(iteminfo_t)); - ic->iteminfo = (iteminfo_t *) ((char *) ic + sizeof(itemconfig_t)); - ic->numiteminfo = 0; - //parse the item config file - while(PC_ReadToken(source, &token)) - { - if (!strcmp(token.string, "iteminfo")) - { - if (ic->numiteminfo >= max_iteminfo) - { - SourceError(source, "more than %d item info defined\n", max_iteminfo); - FreeMemory(ic); - FreeSource(source); - return NULL; - } //end if - ii = &ic->iteminfo[ic->numiteminfo]; - Com_Memset(ii, 0, sizeof(iteminfo_t)); - if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) - { - FreeMemory(ic); - FreeMemory(source); - return NULL; - } //end if - StripDoubleQuotes(token.string); - strncpy(ii->classname, token.string, sizeof(ii->classname)-1); - if (!ReadStructure(source, &iteminfo_struct, (char *) ii)) - { - FreeMemory(ic); - FreeSource(source); - return NULL; - } //end if - ii->number = ic->numiteminfo; - ic->numiteminfo++; - } //end if - else - { - SourceError(source, "unknown definition %s\n", token.string); - FreeMemory(ic); - FreeSource(source); - return NULL; - } //end else - } //end while - FreeSource(source); - // - if (!ic->numiteminfo) botimport.Print(PRT_WARNING, "no item info loaded\n"); - botimport.Print(PRT_MESSAGE, "loaded %s\n", path); - return ic; -} //end of the function LoadItemConfig -//=========================================================================== -// index to find the weight function of an iteminfo -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int *ItemWeightIndex(weightconfig_t *iwc, itemconfig_t *ic) -{ - int *index, i; - - //initialize item weight index - index = (int *) GetClearedMemory(sizeof(int) * ic->numiteminfo); - - for (i = 0; i < ic->numiteminfo; i++) - { - index[i] = FindFuzzyWeight(iwc, ic->iteminfo[i].classname); - if (index[i] < 0) - { - Log_Write("item info %d \"%s\" has no fuzzy weight\r\n", i, ic->iteminfo[i].classname); - } //end if - } //end for - return index; -} //end of the function ItemWeightIndex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void InitLevelItemHeap(void) -{ - int i, max_levelitems; - - if (levelitemheap) FreeMemory(levelitemheap); - - max_levelitems = (int) LibVarValue("max_levelitems", "256"); - levelitemheap = (levelitem_t *) GetClearedMemory(max_levelitems * sizeof(levelitem_t)); - - for (i = 0; i < max_levelitems-1; i++) - { - levelitemheap[i].next = &levelitemheap[i + 1]; - } //end for - levelitemheap[max_levelitems-1].next = NULL; - // - freelevelitems = levelitemheap; -} //end of the function InitLevelItemHeap -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -levelitem_t *AllocLevelItem(void) -{ - levelitem_t *li; - - li = freelevelitems; - if (!li) - { - botimport.Print(PRT_FATAL, "out of level items\n"); - return NULL; - } //end if - // - freelevelitems = freelevelitems->next; - Com_Memset(li, 0, sizeof(levelitem_t)); - return li; -} //end of the function AllocLevelItem -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeLevelItem(levelitem_t *li) -{ - li->next = freelevelitems; - freelevelitems = li; -} //end of the function FreeLevelItem -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AddLevelItemToList(levelitem_t *li) -{ - if (levelitems) levelitems->prev = li; - li->prev = NULL; - li->next = levelitems; - levelitems = li; -} //end of the function AddLevelItemToList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RemoveLevelItemFromList(levelitem_t *li) -{ - if (li->prev) li->prev->next = li->next; - else levelitems = li->next; - if (li->next) li->next->prev = li->prev; -} //end of the function RemoveLevelItemFromList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotFreeInfoEntities(void) -{ - maplocation_t *ml, *nextml; - campspot_t *cs, *nextcs; - - for (ml = maplocations; ml; ml = nextml) - { - nextml = ml->next; - FreeMemory(ml); - } //end for - maplocations = NULL; - for (cs = campspots; cs; cs = nextcs) - { - nextcs = cs->next; - FreeMemory(cs); - } //end for - campspots = NULL; -} //end of the function BotFreeInfoEntities -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotInitInfoEntities(void) -{ - char classname[MAX_EPAIRKEY]; - maplocation_t *ml; - campspot_t *cs; - int ent, numlocations, numcampspots; - - BotFreeInfoEntities(); - // - numlocations = 0; - numcampspots = 0; - for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) - { - if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; - - //map locations - if (!strcmp(classname, "target_location")) - { - ml = (maplocation_t *) GetClearedMemory(sizeof(maplocation_t)); - AAS_VectorForBSPEpairKey(ent, "origin", ml->origin); - AAS_ValueForBSPEpairKey(ent, "message", ml->name, sizeof(ml->name)); - ml->areanum = AAS_PointAreaNum(ml->origin); - ml->next = maplocations; - maplocations = ml; - numlocations++; - } //end if - //camp spots - else if (!strcmp(classname, "info_camp")) - { - cs = (campspot_t *) GetClearedMemory(sizeof(campspot_t)); - AAS_VectorForBSPEpairKey(ent, "origin", cs->origin); - //cs->origin[2] += 16; - AAS_ValueForBSPEpairKey(ent, "message", cs->name, sizeof(cs->name)); - AAS_FloatForBSPEpairKey(ent, "range", &cs->range); - AAS_FloatForBSPEpairKey(ent, "weight", &cs->weight); - AAS_FloatForBSPEpairKey(ent, "wait", &cs->wait); - AAS_FloatForBSPEpairKey(ent, "random", &cs->random); - cs->areanum = AAS_PointAreaNum(cs->origin); - if (!cs->areanum) - { - botimport.Print(PRT_MESSAGE, "camp spot at %1.1f %1.1f %1.1f in solid\n", cs->origin[0], cs->origin[1], cs->origin[2]); - FreeMemory(cs); - continue; - } //end if - cs->next = campspots; - campspots = cs; - //AAS_DrawPermanentCross(cs->origin, 4, LINECOLOR_YELLOW); - numcampspots++; - } //end else if - } //end for - if (bot_developer) - { - botimport.Print(PRT_MESSAGE, "%d map locations\n", numlocations); - botimport.Print(PRT_MESSAGE, "%d camp spots\n", numcampspots); - } //end if -} //end of the function BotInitInfoEntities -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotInitLevelItems(void) -{ - int i, spawnflags, value; - char classname[MAX_EPAIRKEY]; - vec3_t origin, end; - int ent, goalareanum; - itemconfig_t *ic; - levelitem_t *li; - bsp_trace_t trace; - - //initialize the map locations and camp spots - BotInitInfoEntities(); - - //initialize the level item heap - InitLevelItemHeap(); - levelitems = NULL; - numlevelitems = 0; - // - ic = itemconfig; - if (!ic) return; - - //if there's no AAS file loaded - if (!AAS_Loaded()) return; - - //update the modelindexes of the item info - for (i = 0; i < ic->numiteminfo; i++) - { - //ic->iteminfo[i].modelindex = AAS_IndexFromModel(ic->iteminfo[i].model); - if (!ic->iteminfo[i].modelindex) - { - Log_Write("item %s has modelindex 0", ic->iteminfo[i].classname); - } //end if - } //end for - - for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) - { - if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; - // - spawnflags = 0; - AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); - // - for (i = 0; i < ic->numiteminfo; i++) - { - if (!strcmp(classname, ic->iteminfo[i].classname)) break; - } //end for - if (i >= ic->numiteminfo) - { - Log_Write("entity %s unknown item\r\n", classname); - continue; - } //end if - //get the origin of the item - if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) - { - botimport.Print(PRT_ERROR, "item %s without origin\n", classname); - continue; - } //end else - // - goalareanum = 0; - //if it is a floating item - if (spawnflags & 1) - { - //if the item is not floating in water - if (!(AAS_PointContents(origin) & CONTENTS_WATER)) - { - VectorCopy(origin, end); - end[2] -= 32; - trace = AAS_Trace(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, end, -1, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); - //if the item not near the ground - if (trace.fraction >= 1) - { - //if the item is not reachable from a jumppad - goalareanum = AAS_BestReachableFromJumpPadArea(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs); - Log_Write("item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum); - //botimport.Print(PRT_MESSAGE, "item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum); - if (!goalareanum) continue; - } //end if - } //end if - } //end if - - li = AllocLevelItem(); - if (!li) return; - // - li->number = ++numlevelitems; - li->timeout = 0; - li->entitynum = 0; - // - li->flags = 0; - AAS_IntForBSPEpairKey(ent, "notfree", &value); - if (value) li->flags |= IFL_NOTFREE; - AAS_IntForBSPEpairKey(ent, "notteam", &value); - if (value) li->flags |= IFL_NOTTEAM; - AAS_IntForBSPEpairKey(ent, "notsingle", &value); - if (value) li->flags |= IFL_NOTSINGLE; - AAS_IntForBSPEpairKey(ent, "notbot", &value); - if (value) li->flags |= IFL_NOTBOT; - if (!strcmp(classname, "item_botroam")) - { - li->flags |= IFL_ROAM; - AAS_FloatForBSPEpairKey(ent, "weight", &li->weight); - } //end if - //if not a stationary item - if (!(spawnflags & 1)) - { - if (!AAS_DropToFloor(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs)) - { - botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n", - classname, origin[0], origin[1], origin[2]); - } //end if - } //end if - //item info of the level item - li->iteminfo = i; - //origin of the item - VectorCopy(origin, li->origin); - // - if (goalareanum) - { - li->goalareanum = goalareanum; - VectorCopy(origin, li->goalorigin); - } //end if - else - { - //get the item goal area and goal origin - li->goalareanum = AAS_BestReachableArea(origin, - ic->iteminfo[i].mins, ic->iteminfo[i].maxs, - li->goalorigin); - if (!li->goalareanum) - { - botimport.Print(PRT_MESSAGE, "%s not reachable for bots at (%1.1f %1.1f %1.1f)\n", - classname, origin[0], origin[1], origin[2]); - } //end if - } //end else - // - AddLevelItemToList(li); - } //end for - botimport.Print(PRT_MESSAGE, "found %d level items\n", numlevelitems); -} //end of the function BotInitLevelItems -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotGoalName(int number, char *name, int size) -{ - levelitem_t *li; - - if (!itemconfig) return; - // - for (li = levelitems; li; li = li->next) - { - if (li->number == number) - { - strncpy(name, itemconfig->iteminfo[li->iteminfo].name, size-1); - name[size-1] = '\0'; - return; - } //end for - } //end for - strcpy(name, ""); - return; -} //end of the function BotGoalName -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotResetAvoidGoals(int goalstate) -{ - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return; - Com_Memset(gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof(int)); - Com_Memset(gs->avoidgoaltimes, 0, MAX_AVOIDGOALS * sizeof(float)); -} //end of the function BotResetAvoidGoals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotDumpAvoidGoals(int goalstate) -{ - int i; - bot_goalstate_t *gs; - char name[32]; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return; - for (i = 0; i < MAX_AVOIDGOALS; i++) - { - if (gs->avoidgoaltimes[i] >= AAS_Time()) - { - BotGoalName(gs->avoidgoals[i], name, 32); - Log_Write("avoid goal %s, number %d for %f seconds", name, - gs->avoidgoals[i], gs->avoidgoaltimes[i] - AAS_Time()); - } //end if - } //end for -} //end of the function BotDumpAvoidGoals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotAddToAvoidGoals(bot_goalstate_t *gs, int number, float avoidtime) -{ - int i; - - for (i = 0; i < MAX_AVOIDGOALS; i++) - { - //if the avoid goal is already stored - if (gs->avoidgoals[i] == number) - { - gs->avoidgoals[i] = number; - gs->avoidgoaltimes[i] = AAS_Time() + avoidtime; - return; - } //end if - } //end for - - for (i = 0; i < MAX_AVOIDGOALS; i++) - { - //if this avoid goal has expired - if (gs->avoidgoaltimes[i] < AAS_Time()) - { - gs->avoidgoals[i] = number; - gs->avoidgoaltimes[i] = AAS_Time() + avoidtime; - return; - } //end if - } //end for -} //end of the function BotAddToAvoidGoals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotRemoveFromAvoidGoals(int goalstate, int number) -{ - int i; - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return; - //don't use the goals the bot wants to avoid - for (i = 0; i < MAX_AVOIDGOALS; i++) - { - if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time()) - { - gs->avoidgoaltimes[i] = 0; - return; - } //end if - } //end for -} //end of the function BotRemoveFromAvoidGoals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float BotAvoidGoalTime(int goalstate, int number) -{ - int i; - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return 0; - //don't use the goals the bot wants to avoid - for (i = 0; i < MAX_AVOIDGOALS; i++) - { - if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time()) - { - return gs->avoidgoaltimes[i] - AAS_Time(); - } //end if - } //end for - return 0; -} //end of the function BotAvoidGoalTime -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime) -{ - bot_goalstate_t *gs; - levelitem_t *li; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) - return; - if (avoidtime < 0) - { - if (!itemconfig) - return; - // - for (li = levelitems; li; li = li->next) - { - if (li->number == number) - { - avoidtime = itemconfig->iteminfo[li->iteminfo].respawntime; - if (!avoidtime) - avoidtime = AVOID_DEFAULT_TIME; - if (avoidtime < AVOID_MINIMUM_TIME) - avoidtime = AVOID_MINIMUM_TIME; - BotAddToAvoidGoals(gs, number, avoidtime); - return; - } //end for - } //end for - return; - } //end if - else - { - BotAddToAvoidGoals(gs, number, avoidtime); - } //end else -} //end of the function BotSetAvoidGoalTime -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotGetLevelItemGoal(int index, char *name, bot_goal_t *goal) -{ - levelitem_t *li; - - if (!itemconfig) return -1; - li = levelitems; - if (index >= 0) - { - for (; li; li = li->next) - { - if (li->number == index) - { - li = li->next; - break; - } //end if - } //end for - } //end for - for (; li; li = li->next) - { - // - if (g_gametype == GT_SINGLE_PLAYER) { - if (li->flags & IFL_NOTSINGLE) continue; - } - else if (g_gametype >= GT_TEAM) { - if (li->flags & IFL_NOTTEAM) continue; - } - else { - if (li->flags & IFL_NOTFREE) continue; - } - if (li->flags & IFL_NOTBOT) continue; - // - if (!Q_stricmp(name, itemconfig->iteminfo[li->iteminfo].name)) - { - goal->areanum = li->goalareanum; - VectorCopy(li->goalorigin, goal->origin); - goal->entitynum = li->entitynum; - VectorCopy(itemconfig->iteminfo[li->iteminfo].mins, goal->mins); - VectorCopy(itemconfig->iteminfo[li->iteminfo].maxs, goal->maxs); - goal->number = li->number; - goal->flags = GFL_ITEM; - if (li->timeout) goal->flags |= GFL_DROPPED; - //botimport.Print(PRT_MESSAGE, "found li %s\n", itemconfig->iteminfo[li->iteminfo].name); - return li->number; - } //end if - } //end for - return -1; -} //end of the function BotGetLevelItemGoal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotGetMapLocationGoal(char *name, bot_goal_t *goal) -{ - maplocation_t *ml; - vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8}; - - for (ml = maplocations; ml; ml = ml->next) - { - if (!Q_stricmp(ml->name, name)) - { - goal->areanum = ml->areanum; - VectorCopy(ml->origin, goal->origin); - goal->entitynum = 0; - VectorCopy(mins, goal->mins); - VectorCopy(maxs, goal->maxs); - return qtrue; - } //end if - } //end for - return qfalse; -} //end of the function BotGetMapLocationGoal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotGetNextCampSpotGoal(int num, bot_goal_t *goal) -{ - int i; - campspot_t *cs; - vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8}; - - if (num < 0) num = 0; - i = num; - for (cs = campspots; cs; cs = cs->next) - { - if (--i < 0) - { - goal->areanum = cs->areanum; - VectorCopy(cs->origin, goal->origin); - goal->entitynum = 0; - VectorCopy(mins, goal->mins); - VectorCopy(maxs, goal->maxs); - return num+1; - } //end if - } //end for - return 0; -} //end of the function BotGetNextCampSpotGoal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotFindEntityForLevelItem(levelitem_t *li) -{ - int ent, modelindex; - itemconfig_t *ic; - aas_entityinfo_t entinfo; - vec3_t dir; - - ic = itemconfig; - if (!itemconfig) return; - for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent)) - { - //get the model index of the entity - modelindex = AAS_EntityModelindex(ent); - // - if (!modelindex) continue; - //get info about the entity - AAS_EntityInfo(ent, &entinfo); - //if the entity is still moving - if (entinfo.origin[0] != entinfo.lastvisorigin[0] || - entinfo.origin[1] != entinfo.lastvisorigin[1] || - entinfo.origin[2] != entinfo.lastvisorigin[2]) continue; - // - if (ic->iteminfo[li->iteminfo].modelindex == modelindex) - { - //check if the entity is very close - VectorSubtract(li->origin, entinfo.origin, dir); - if (VectorLength(dir) < 30) - { - //found an entity for this level item - li->entitynum = ent; - } //end if - } //end if - } //end for -} //end of the function BotFindEntityForLevelItem -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== - -//NOTE: enum entityType_t in bg_public.h -#define ET_ITEM 2 - -void BotUpdateEntityItems(void) -{ - int ent, i, modelindex; - vec3_t dir; - levelitem_t *li, *nextli; - aas_entityinfo_t entinfo; - itemconfig_t *ic; - - //timeout current entity items if necessary - for (li = levelitems; li; li = nextli) - { - nextli = li->next; - //if it is a item that will time out - if (li->timeout) - { - //timeout the item - if (li->timeout < AAS_Time()) - { - RemoveLevelItemFromList(li); - FreeLevelItem(li); - } //end if - } //end if - } //end for - //find new entity items - ic = itemconfig; - if (!itemconfig) return; - // - for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent)) - { - if (AAS_EntityType(ent) != ET_ITEM) continue; - //get the model index of the entity - modelindex = AAS_EntityModelindex(ent); - // - if (!modelindex) continue; - //get info about the entity - AAS_EntityInfo(ent, &entinfo); - //FIXME: don't do this - //skip all floating items for now - //if (entinfo.groundent != ENTITYNUM_WORLD) continue; - //if the entity is still moving - if (entinfo.origin[0] != entinfo.lastvisorigin[0] || - entinfo.origin[1] != entinfo.lastvisorigin[1] || - entinfo.origin[2] != entinfo.lastvisorigin[2]) continue; - //check if the entity is already stored as a level item - for (li = levelitems; li; li = li->next) - { - //if the level item is linked to an entity - if (li->entitynum && li->entitynum == ent) - { - //the entity is re-used if the models are different - if (ic->iteminfo[li->iteminfo].modelindex != modelindex) - { - //remove this level item - RemoveLevelItemFromList(li); - FreeLevelItem(li); - li = NULL; - break; - } //end if - else - { - if (entinfo.origin[0] != li->origin[0] || - entinfo.origin[1] != li->origin[1] || - entinfo.origin[2] != li->origin[2]) - { - VectorCopy(entinfo.origin, li->origin); - //also update the goal area number - li->goalareanum = AAS_BestReachableArea(li->origin, - ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs, - li->goalorigin); - } //end if - break; - } //end else - } //end if - } //end for - if (li) continue; - //try to link the entity to a level item - for (li = levelitems; li; li = li->next) - { - //if this level item is already linked - if (li->entitynum) continue; - // - if (g_gametype == GT_SINGLE_PLAYER) { - if (li->flags & IFL_NOTSINGLE) continue; - } - else if (g_gametype >= GT_TEAM) { - if (li->flags & IFL_NOTTEAM) continue; - } - else { - if (li->flags & IFL_NOTFREE) continue; - } - //if the model of the level item and the entity are the same - if (ic->iteminfo[li->iteminfo].modelindex == modelindex) - { - //check if the entity is very close - VectorSubtract(li->origin, entinfo.origin, dir); - if (VectorLength(dir) < 30) - { - //found an entity for this level item - li->entitynum = ent; - //if the origin is different - if (entinfo.origin[0] != li->origin[0] || - entinfo.origin[1] != li->origin[1] || - entinfo.origin[2] != li->origin[2]) - { - //update the level item origin - VectorCopy(entinfo.origin, li->origin); - //also update the goal area number - li->goalareanum = AAS_BestReachableArea(li->origin, - ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs, - li->goalorigin); - } //end if -#ifdef DEBUG - Log_Write("linked item %s to an entity", ic->iteminfo[li->iteminfo].classname); -#endif //DEBUG - break; - } //end if - } //end else - } //end for - if (li) continue; - //check if the model is from a known item - for (i = 0; i < ic->numiteminfo; i++) - { - if (ic->iteminfo[i].modelindex == modelindex) - { - break; - } //end if - } //end for - //if the model is not from a known item - if (i >= ic->numiteminfo) continue; - //allocate a new level item - li = AllocLevelItem(); - // - if (!li) continue; - //entity number of the level item - li->entitynum = ent; - //number for the level item - li->number = numlevelitems + ent; - //set the item info index for the level item - li->iteminfo = i; - //origin of the item - VectorCopy(entinfo.origin, li->origin); - //get the item goal area and goal origin - li->goalareanum = AAS_BestReachableArea(li->origin, - ic->iteminfo[i].mins, ic->iteminfo[i].maxs, - li->goalorigin); - //never go for items dropped into jumppads - if (AAS_AreaJumpPad(li->goalareanum)) - { - FreeLevelItem(li); - continue; - } //end if - //time this item out after 30 seconds - //dropped items disappear after 30 seconds - li->timeout = AAS_Time() + 30; - //add the level item to the list - AddLevelItemToList(li); - //botimport.Print(PRT_MESSAGE, "found new level item %s\n", ic->iteminfo[i].classname); - } //end for - /* - for (li = levelitems; li; li = li->next) - { - if (!li->entitynum) - { - BotFindEntityForLevelItem(li); - } //end if - } //end for*/ -} //end of the function BotUpdateEntityItems -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotDumpGoalStack(int goalstate) -{ - int i; - bot_goalstate_t *gs; - char name[32]; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return; - for (i = 1; i <= gs->goalstacktop; i++) - { - BotGoalName(gs->goalstack[i].number, name, 32); - Log_Write("%d: %s", i, name); - } //end for -} //end of the function BotDumpGoalStack -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotPushGoal(int goalstate, bot_goal_t *goal) -{ - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return; - if (gs->goalstacktop >= MAX_GOALSTACK-1) - { - botimport.Print(PRT_ERROR, "goal heap overflow\n"); - BotDumpGoalStack(goalstate); - return; - } //end if - gs->goalstacktop++; - Com_Memcpy(&gs->goalstack[gs->goalstacktop], goal, sizeof(bot_goal_t)); -} //end of the function BotPushGoal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotPopGoal(int goalstate) -{ - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return; - if (gs->goalstacktop > 0) gs->goalstacktop--; -} //end of the function BotPopGoal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotEmptyGoalStack(int goalstate) -{ - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return; - gs->goalstacktop = 0; -} //end of the function BotEmptyGoalStack -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotGetTopGoal(int goalstate, bot_goal_t *goal) -{ - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return qfalse; - if (!gs->goalstacktop) return qfalse; - Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop], sizeof(bot_goal_t)); - return qtrue; -} //end of the function BotGetTopGoal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotGetSecondGoal(int goalstate, bot_goal_t *goal) -{ - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return qfalse; - if (gs->goalstacktop <= 1) return qfalse; - Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop-1], sizeof(bot_goal_t)); - return qtrue; -} //end of the function BotGetSecondGoal -//=========================================================================== -// pops a new long term goal on the goal stack in the goalstate -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags) -{ - int areanum, t, weightnum; - float weight, bestweight, avoidtime; - iteminfo_t *iteminfo; - itemconfig_t *ic; - levelitem_t *li, *bestitem; - bot_goal_t goal; - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) - return qfalse; - if (!gs->itemweightconfig) - return qfalse; - //get the area the bot is in - areanum = BotReachabilityArea(origin, gs->client); - //if the bot is in solid or if the area the bot is in has no reachability links - if (!areanum || !AAS_AreaReachability(areanum)) - { - //use the last valid area the bot was in - areanum = gs->lastreachabilityarea; - } //end if - //remember the last area with reachabilities the bot was in - gs->lastreachabilityarea = areanum; - //if still in solid - if (!areanum) - return qfalse; - //the item configuration - ic = itemconfig; - if (!itemconfig) - return qfalse; - //best weight and item so far - bestweight = 0; - bestitem = NULL; - Com_Memset(&goal, 0, sizeof(bot_goal_t)); - //go through the items in the level - for (li = levelitems; li; li = li->next) - { - if (g_gametype == GT_SINGLE_PLAYER) { - if (li->flags & IFL_NOTSINGLE) - continue; - } - else if (g_gametype >= GT_TEAM) { - if (li->flags & IFL_NOTTEAM) - continue; - } - else { - if (li->flags & IFL_NOTFREE) - continue; - } - if (li->flags & IFL_NOTBOT) - continue; - //if the item is not in a possible goal area - if (!li->goalareanum) - continue; - //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk) - if (!li->entitynum && !(li->flags & IFL_ROAM)) - continue; - //get the fuzzy weight function for this item - iteminfo = &ic->iteminfo[li->iteminfo]; - weightnum = gs->itemweightindex[iteminfo->number]; - if (weightnum < 0) - continue; - -#ifdef UNDECIDEDFUZZY - weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum); -#else - weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum); -#endif //UNDECIDEDFUZZY -#ifdef DROPPEDWEIGHT - //HACK: to make dropped items more attractive - if (li->timeout) - weight += droppedweight->value; -#endif //DROPPEDWEIGHT - //use weight scale for item_botroam - if (li->flags & IFL_ROAM) weight *= li->weight; - // - if (weight > 0) - { - //get the travel time towards the goal area - t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags); - //if the goal is reachable - if (t > 0) - { - //if this item won't respawn before we get there - avoidtime = BotAvoidGoalTime(goalstate, li->number); - if (avoidtime - t * 0.009 > 0) - continue; - // - weight /= (float) t * TRAVELTIME_SCALE; - // - if (weight > bestweight) - { - bestweight = weight; - bestitem = li; - } //end if - } //end if - } //end if - } //end for - //if no goal item found - if (!bestitem) - { - /* - //if not in lava or slime - if (!AAS_AreaLava(areanum) && !AAS_AreaSlime(areanum)) - { - if (AAS_RandomGoalArea(areanum, travelflags, &goal.areanum, goal.origin)) - { - VectorSet(goal.mins, -15, -15, -15); - VectorSet(goal.maxs, 15, 15, 15); - goal.entitynum = 0; - goal.number = 0; - goal.flags = GFL_ROAM; - goal.iteminfo = 0; - //push the goal on the stack - BotPushGoal(goalstate, &goal); - // -#ifdef DEBUG - botimport.Print(PRT_MESSAGE, "chosen roam goal area %d\n", goal.areanum); -#endif //DEBUG - return qtrue; - } //end if - } //end if - */ - return qfalse; - } //end if - //create a bot goal for this item - iteminfo = &ic->iteminfo[bestitem->iteminfo]; - VectorCopy(bestitem->goalorigin, goal.origin); - VectorCopy(iteminfo->mins, goal.mins); - VectorCopy(iteminfo->maxs, goal.maxs); - goal.areanum = bestitem->goalareanum; - goal.entitynum = bestitem->entitynum; - goal.number = bestitem->number; - goal.flags = GFL_ITEM; - if (bestitem->timeout) - goal.flags |= GFL_DROPPED; - if (bestitem->flags & IFL_ROAM) - goal.flags |= GFL_ROAM; - goal.iteminfo = bestitem->iteminfo; - //if it's a dropped item - if (bestitem->timeout) - { - avoidtime = AVOID_DROPPED_TIME; - } //end if - else - { - avoidtime = iteminfo->respawntime; - if (!avoidtime) - avoidtime = AVOID_DEFAULT_TIME; - if (avoidtime < AVOID_MINIMUM_TIME) - avoidtime = AVOID_MINIMUM_TIME; - } //end else - //add the chosen goal to the goals to avoid for a while - BotAddToAvoidGoals(gs, bestitem->number, avoidtime); - //push the goal on the stack - BotPushGoal(goalstate, &goal); - // - return qtrue; -} //end of the function BotChooseLTGItem -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags, - bot_goal_t *ltg, float maxtime) -{ - int areanum, t, weightnum, ltg_time; - float weight, bestweight, avoidtime; - iteminfo_t *iteminfo; - itemconfig_t *ic; - levelitem_t *li, *bestitem; - bot_goal_t goal; - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) - return qfalse; - if (!gs->itemweightconfig) - return qfalse; - //get the area the bot is in - areanum = BotReachabilityArea(origin, gs->client); - //if the bot is in solid or if the area the bot is in has no reachability links - if (!areanum || !AAS_AreaReachability(areanum)) - { - //use the last valid area the bot was in - areanum = gs->lastreachabilityarea; - } //end if - //remember the last area with reachabilities the bot was in - gs->lastreachabilityarea = areanum; - //if still in solid - if (!areanum) - return qfalse; - // - if (ltg) ltg_time = AAS_AreaTravelTimeToGoalArea(areanum, origin, ltg->areanum, travelflags); - else ltg_time = 99999; - //the item configuration - ic = itemconfig; - if (!itemconfig) - return qfalse; - //best weight and item so far - bestweight = 0; - bestitem = NULL; - Com_Memset(&goal, 0, sizeof(bot_goal_t)); - //go through the items in the level - for (li = levelitems; li; li = li->next) - { - if (g_gametype == GT_SINGLE_PLAYER) { - if (li->flags & IFL_NOTSINGLE) - continue; - } - else if (g_gametype >= GT_TEAM) { - if (li->flags & IFL_NOTTEAM) - continue; - } - else { - if (li->flags & IFL_NOTFREE) - continue; - } - if (li->flags & IFL_NOTBOT) - continue; - //if the item is in a possible goal area - if (!li->goalareanum) - continue; - //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk) - if (!li->entitynum && !(li->flags & IFL_ROAM)) - continue; - //get the fuzzy weight function for this item - iteminfo = &ic->iteminfo[li->iteminfo]; - weightnum = gs->itemweightindex[iteminfo->number]; - if (weightnum < 0) - continue; - // -#ifdef UNDECIDEDFUZZY - weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum); -#else - weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum); -#endif //UNDECIDEDFUZZY -#ifdef DROPPEDWEIGHT - //HACK: to make dropped items more attractive - if (li->timeout) - weight += droppedweight->value; -#endif //DROPPEDWEIGHT - //use weight scale for item_botroam - if (li->flags & IFL_ROAM) weight *= li->weight; - // - if (weight > 0) - { - //get the travel time towards the goal area - t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags); - //if the goal is reachable - if (t > 0 && t < maxtime) - { - //if this item won't respawn before we get there - avoidtime = BotAvoidGoalTime(goalstate, li->number); - if (avoidtime - t * 0.009 > 0) - continue; - // - weight /= (float) t * TRAVELTIME_SCALE; - // - if (weight > bestweight) - { - t = 0; - if (ltg && !li->timeout) - { - //get the travel time from the goal to the long term goal - t = AAS_AreaTravelTimeToGoalArea(li->goalareanum, li->goalorigin, ltg->areanum, travelflags); - } //end if - //if the travel back is possible and doesn't take too long - if (t <= ltg_time) - { - bestweight = weight; - bestitem = li; - } //end if - } //end if - } //end if - } //end if - } //end for - //if no goal item found - if (!bestitem) - return qfalse; - //create a bot goal for this item - iteminfo = &ic->iteminfo[bestitem->iteminfo]; - VectorCopy(bestitem->goalorigin, goal.origin); - VectorCopy(iteminfo->mins, goal.mins); - VectorCopy(iteminfo->maxs, goal.maxs); - goal.areanum = bestitem->goalareanum; - goal.entitynum = bestitem->entitynum; - goal.number = bestitem->number; - goal.flags = GFL_ITEM; - if (bestitem->timeout) - goal.flags |= GFL_DROPPED; - if (bestitem->flags & IFL_ROAM) - goal.flags |= GFL_ROAM; - goal.iteminfo = bestitem->iteminfo; - //if it's a dropped item - if (bestitem->timeout) - { - avoidtime = AVOID_DROPPED_TIME; - } //end if - else - { - avoidtime = iteminfo->respawntime; - if (!avoidtime) - avoidtime = AVOID_DEFAULT_TIME; - if (avoidtime < AVOID_MINIMUM_TIME) - avoidtime = AVOID_MINIMUM_TIME; - } //end else - //add the chosen goal to the goals to avoid for a while - BotAddToAvoidGoals(gs, bestitem->number, avoidtime); - //push the goal on the stack - BotPushGoal(goalstate, &goal); - // - return qtrue; -} //end of the function BotChooseNBGItem -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotTouchingGoal(vec3_t origin, bot_goal_t *goal) -{ - int i; - vec3_t boxmins, boxmaxs; - vec3_t absmins, absmaxs; - vec3_t safety_maxs = {0, 0, 0}; //{4, 4, 10}; - vec3_t safety_mins = {0, 0, 0}; //{-4, -4, 0}; - - AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, boxmins, boxmaxs); - VectorSubtract(goal->mins, boxmaxs, absmins); - VectorSubtract(goal->maxs, boxmins, absmaxs); - VectorAdd(absmins, goal->origin, absmins); - VectorAdd(absmaxs, goal->origin, absmaxs); - //make the box a little smaller for safety - VectorSubtract(absmaxs, safety_maxs, absmaxs); - VectorSubtract(absmins, safety_mins, absmins); - - for (i = 0; i < 3; i++) - { - if (origin[i] < absmins[i] || origin[i] > absmaxs[i]) return qfalse; - } //end for - return qtrue; -} //end of the function BotTouchingGoal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal) -{ - aas_entityinfo_t entinfo; - bsp_trace_t trace; - vec3_t middle; - - if (!(goal->flags & GFL_ITEM)) return qfalse; - // - VectorAdd(goal->mins, goal->mins, middle); - VectorScale(middle, 0.5, middle); - VectorAdd(goal->origin, middle, middle); - // - trace = AAS_Trace(eye, NULL, NULL, middle, viewer, CONTENTS_SOLID); - //if the goal middle point is visible - if (trace.fraction >= 1) - { - //the goal entity number doesn't have to be valid - //just assume it's valid - if (goal->entitynum <= 0) - return qfalse; - // - //if the entity data isn't valid - AAS_EntityInfo(goal->entitynum, &entinfo); - //NOTE: for some wacko reason entities are sometimes - // not updated - //if (!entinfo.valid) return qtrue; - if (entinfo.ltime < AAS_Time() - 0.5) - return qtrue; - } //end if - return qfalse; -} //end of the function BotItemGoalInVisButNotVisible -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotResetGoalState(int goalstate) -{ - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return; - Com_Memset(gs->goalstack, 0, MAX_GOALSTACK * sizeof(bot_goal_t)); - gs->goalstacktop = 0; - BotResetAvoidGoals(goalstate); -} //end of the function BotResetGoalState -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotLoadItemWeights(int goalstate, char *filename) -{ - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return BLERR_CANNOTLOADITEMWEIGHTS; - //load the weight configuration - gs->itemweightconfig = ReadWeightConfig(filename); - if (!gs->itemweightconfig) - { - botimport.Print(PRT_FATAL, "couldn't load weights\n"); - return BLERR_CANNOTLOADITEMWEIGHTS; - } //end if - //if there's no item configuration - if (!itemconfig) return BLERR_CANNOTLOADITEMWEIGHTS; - //create the item weight index - gs->itemweightindex = ItemWeightIndex(gs->itemweightconfig, itemconfig); - //everything went ok - return BLERR_NOERROR; -} //end of the function BotLoadItemWeights -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotFreeItemWeights(int goalstate) -{ - bot_goalstate_t *gs; - - gs = BotGoalStateFromHandle(goalstate); - if (!gs) return; - if (gs->itemweightconfig) FreeWeightConfig(gs->itemweightconfig); - if (gs->itemweightindex) FreeMemory(gs->itemweightindex); -} //end of the function BotFreeItemWeights -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotAllocGoalState(int client) -{ - int i; - - for (i = 1; i <= MAX_CLIENTS; i++) - { - if (!botgoalstates[i]) - { - botgoalstates[i] = GetClearedMemory(sizeof(bot_goalstate_t)); - botgoalstates[i]->client = client; - return i; - } //end if - } //end for - return 0; -} //end of the function BotAllocGoalState -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -void BotFreeGoalState(int handle) -{ - if (handle <= 0 || handle > MAX_CLIENTS) - { - botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle); - return; - } //end if - if (!botgoalstates[handle]) - { - botimport.Print(PRT_FATAL, "invalid goal state handle %d\n", handle); - return; - } //end if - BotFreeItemWeights(handle); - FreeMemory(botgoalstates[handle]); - botgoalstates[handle] = NULL; -} //end of the function BotFreeGoalState -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotSetupGoalAI(void) -{ - char *filename; - - //check if teamplay is on - g_gametype = LibVarValue("g_gametype", "0"); - //item configuration file - filename = LibVarString("itemconfig", "items.c"); - //load the item configuration - itemconfig = LoadItemConfig(filename); - if (!itemconfig) - { - botimport.Print(PRT_FATAL, "couldn't load item config\n"); - return BLERR_CANNOTLOADITEMCONFIG; - } //end if - // - droppedweight = LibVar("droppedweight", "1000"); - //everything went ok - return BLERR_NOERROR; -} //end of the function BotSetupGoalAI -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotShutdownGoalAI(void) -{ - int i; - - if (itemconfig) FreeMemory(itemconfig); - itemconfig = NULL; - if (levelitemheap) FreeMemory(levelitemheap); - levelitemheap = NULL; - freelevelitems = NULL; - levelitems = NULL; - numlevelitems = 0; - - BotFreeInfoEntities(); - - for (i = 1; i <= MAX_CLIENTS; i++) - { - if (botgoalstates[i]) - { - BotFreeGoalState(i); - } //end if - } //end for -} //end of the function BotShutdownGoalAI diff --git a/tools/quake3/bspc/deps/botlib/be_ai_goal.h b/tools/quake3/bspc/deps/botlib/be_ai_goal.h deleted file mode 100644 index 80dad08d..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_goal.h +++ /dev/null @@ -1,118 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// -/***************************************************************************** - * name: be_ai_goal.h - * - * desc: goal AI - * - * $Archive: /source/code/botlib/be_ai_goal.h $ - * - *****************************************************************************/ - -#define MAX_AVOIDGOALS 256 -#define MAX_GOALSTACK 8 - -#define GFL_NONE 0 -#define GFL_ITEM 1 -#define GFL_ROAM 2 -#define GFL_DROPPED 4 - -//a bot goal -typedef struct bot_goal_s -{ - vec3_t origin; //origin of the goal - int areanum; //area number of the goal - vec3_t mins, maxs; //mins and maxs of the goal - int entitynum; //number of the goal entity - int number; //goal number - int flags; //goal flags - int iteminfo; //item information -} bot_goal_t; - -//reset the whole goal state, but keep the item weights -void BotResetGoalState(int goalstate); -//reset avoid goals -void BotResetAvoidGoals(int goalstate); -//remove the goal with the given number from the avoid goals -void BotRemoveFromAvoidGoals(int goalstate, int number); -//push a goal onto the goal stack -void BotPushGoal(int goalstate, bot_goal_t *goal); -//pop a goal from the goal stack -void BotPopGoal(int goalstate); -//empty the bot's goal stack -void BotEmptyGoalStack(int goalstate); -//dump the avoid goals -void BotDumpAvoidGoals(int goalstate); -//dump the goal stack -void BotDumpGoalStack(int goalstate); -//get the name name of the goal with the given number -void BotGoalName(int number, char *name, int size); -//get the top goal from the stack -int BotGetTopGoal(int goalstate, bot_goal_t *goal); -//get the second goal on the stack -int BotGetSecondGoal(int goalstate, bot_goal_t *goal); -//choose the best long term goal item for the bot -int BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags); -//choose the best nearby goal item for the bot -//the item may not be further away from the current bot position than maxtime -//also the travel time from the nearby goal towards the long term goal may not -//be larger than the travel time towards the long term goal from the current bot position -int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags, - bot_goal_t *ltg, float maxtime); -//returns true if the bot touches the goal -int BotTouchingGoal(vec3_t origin, bot_goal_t *goal); -//returns true if the goal should be visible but isn't -int BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal); -//search for a goal for the given classname, the index can be used -//as a start point for the search when multiple goals are available with that same classname -int BotGetLevelItemGoal(int index, char *classname, bot_goal_t *goal); -//get the next camp spot in the map -int BotGetNextCampSpotGoal(int num, bot_goal_t *goal); -//get the map location with the given name -int BotGetMapLocationGoal(char *name, bot_goal_t *goal); -//returns the avoid goal time -float BotAvoidGoalTime(int goalstate, int number); -//set the avoid goal time -void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime); -//initializes the items in the level -void BotInitLevelItems(void); -//regularly update dynamic entity items (dropped weapons, flags etc.) -void BotUpdateEntityItems(void); -//interbreed the goal fuzzy logic -void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child); -//save the goal fuzzy logic to disk -void BotSaveGoalFuzzyLogic(int goalstate, char *filename); -//mutate the goal fuzzy logic -void BotMutateGoalFuzzyLogic(int goalstate, float range); -//loads item weights for the bot -int BotLoadItemWeights(int goalstate, char *filename); -//frees the item weights of the bot -void BotFreeItemWeights(int goalstate); -//returns the handle of a newly allocated goal state -int BotAllocGoalState(int client); -//free the given goal state -void BotFreeGoalState(int handle); -//setup the goal AI -int BotSetupGoalAI(void); -//shut down the goal AI -void BotShutdownGoalAI(void); diff --git a/tools/quake3/bspc/deps/botlib/be_ai_move.c b/tools/quake3/bspc/deps/botlib/be_ai_move.c deleted file mode 100644 index 6a9a92af..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_move.c +++ /dev/null @@ -1,3610 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_ai_move.c - * - * desc: bot movement AI - * - * $Archive: /MissionPack/code/botlib/be_ai_move.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_libvar.h" -#include "l_utils.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" - -#include "be_ea.h" -#include "be_ai_goal.h" -#include "be_ai_move.h" - - -//#define DEBUG_AI_MOVE -//#define DEBUG_ELEVATOR -//#define DEBUG_GRAPPLE - -// bk001204 - redundant bot_avoidspot_t, see be_ai_move.h - -//movement state -//NOTE: the moveflags MFL_ONGROUND, MFL_TELEPORTED, MFL_WATERJUMP and -// MFL_GRAPPLEPULL must be set outside the movement code -typedef struct bot_movestate_s -{ - //input vars (all set outside the movement code) - vec3_t origin; //origin of the bot - vec3_t velocity; //velocity of the bot - vec3_t viewoffset; //view offset - int entitynum; //entity number of the bot - int client; //client number of the bot - float thinktime; //time the bot thinks - int presencetype; //presencetype of the bot - vec3_t viewangles; //view angles of the bot - //state vars - int areanum; //area the bot is in - int lastareanum; //last area the bot was in - int lastgoalareanum; //last goal area number - int lastreachnum; //last reachability number - vec3_t lastorigin; //origin previous cycle - int reachareanum; //area number of the reachabilty - int moveflags; //movement flags - int jumpreach; //set when jumped - float grapplevisible_time; //last time the grapple was visible - float lastgrappledist; //last distance to the grapple end - float reachability_time; //time to use current reachability - int avoidreach[MAX_AVOIDREACH]; //reachabilities to avoid - float avoidreachtimes[MAX_AVOIDREACH]; //times to avoid the reachabilities - int avoidreachtries[MAX_AVOIDREACH]; //number of tries before avoiding - // - bot_avoidspot_t avoidspots[MAX_AVOIDSPOTS]; //spots to avoid - int numavoidspots; -} bot_movestate_t; - -//used to avoid reachability links for some time after being used -#define AVOIDREACH -#define AVOIDREACH_TIME 6 //avoid links for 6 seconds after use -#define AVOIDREACH_TRIES 4 -//prediction times -#define PREDICTIONTIME_JUMP 3 //in seconds -#define PREDICTIONTIME_MOVE 2 //in seconds -//weapon indexes for weapon jumping -#define WEAPONINDEX_ROCKET_LAUNCHER 5 -#define WEAPONINDEX_BFG 9 - -#define MODELTYPE_FUNC_PLAT 1 -#define MODELTYPE_FUNC_BOB 2 -#define MODELTYPE_FUNC_DOOR 3 -#define MODELTYPE_FUNC_STATIC 4 - -libvar_t *sv_maxstep; -libvar_t *sv_maxbarrier; -libvar_t *sv_gravity; -libvar_t *weapindex_rocketlauncher; -libvar_t *weapindex_bfg10k; -libvar_t *weapindex_grapple; -libvar_t *entitytypemissile; -libvar_t *offhandgrapple; -libvar_t *cmd_grappleoff; -libvar_t *cmd_grappleon; -//type of model, func_plat or func_bobbing -int modeltypes[MAX_MODELS]; - -bot_movestate_t *botmovestates[MAX_CLIENTS+1]; - -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -int BotAllocMoveState(void) -{ - int i; - - for (i = 1; i <= MAX_CLIENTS; i++) - { - if (!botmovestates[i]) - { - botmovestates[i] = GetClearedMemory(sizeof(bot_movestate_t)); - return i; - } //end if - } //end for - return 0; -} //end of the function BotAllocMoveState -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -void BotFreeMoveState(int handle) -{ - if (handle <= 0 || handle > MAX_CLIENTS) - { - botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); - return; - } //end if - if (!botmovestates[handle]) - { - botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); - return; - } //end if - FreeMemory(botmovestates[handle]); - botmovestates[handle] = NULL; -} //end of the function BotFreeMoveState -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -bot_movestate_t *BotMoveStateFromHandle(int handle) -{ - if (handle <= 0 || handle > MAX_CLIENTS) - { - botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); - return NULL; - } //end if - if (!botmovestates[handle]) - { - botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); - return NULL; - } //end if - return botmovestates[handle]; -} //end of the function BotMoveStateFromHandle -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -void BotInitMoveState(int handle, bot_initmove_t *initmove) -{ - bot_movestate_t *ms; - - ms = BotMoveStateFromHandle(handle); - if (!ms) return; - VectorCopy(initmove->origin, ms->origin); - VectorCopy(initmove->velocity, ms->velocity); - VectorCopy(initmove->viewoffset, ms->viewoffset); - ms->entitynum = initmove->entitynum; - ms->client = initmove->client; - ms->thinktime = initmove->thinktime; - ms->presencetype = initmove->presencetype; - VectorCopy(initmove->viewangles, ms->viewangles); - // - ms->moveflags &= ~MFL_ONGROUND; - if (initmove->or_moveflags & MFL_ONGROUND) ms->moveflags |= MFL_ONGROUND; - ms->moveflags &= ~MFL_TELEPORTED; - if (initmove->or_moveflags & MFL_TELEPORTED) ms->moveflags |= MFL_TELEPORTED; - ms->moveflags &= ~MFL_WATERJUMP; - if (initmove->or_moveflags & MFL_WATERJUMP) ms->moveflags |= MFL_WATERJUMP; - ms->moveflags &= ~MFL_WALK; - if (initmove->or_moveflags & MFL_WALK) ms->moveflags |= MFL_WALK; - ms->moveflags &= ~MFL_GRAPPLEPULL; - if (initmove->or_moveflags & MFL_GRAPPLEPULL) ms->moveflags |= MFL_GRAPPLEPULL; -} //end of the function BotInitMoveState -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -float AngleDiff(float ang1, float ang2) -{ - float diff; - - diff = ang1 - ang2; - if (ang1 > ang2) - { - if (diff > 180.0) diff -= 360.0; - } //end if - else - { - if (diff < -180.0) diff += 360.0; - } //end else - return diff; -} //end of the function AngleDiff -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotFuzzyPointReachabilityArea(vec3_t origin) -{ - int firstareanum, j, x, y, z; - int areas[10], numareas, areanum, bestareanum; - float dist, bestdist; - vec3_t points[10], v, end; - - firstareanum = 0; - areanum = AAS_PointAreaNum(origin); - if (areanum) - { - firstareanum = areanum; - if (AAS_AreaReachability(areanum)) return areanum; - } //end if - VectorCopy(origin, end); - end[2] += 4; - numareas = AAS_TraceAreas(origin, end, areas, points, 10); - for (j = 0; j < numareas; j++) - { - if (AAS_AreaReachability(areas[j])) return areas[j]; - } //end for - bestdist = 999999; - bestareanum = 0; - for (z = 1; z >= -1; z -= 1) - { - for (x = 1; x >= -1; x -= 1) - { - for (y = 1; y >= -1; y -= 1) - { - VectorCopy(origin, end); - end[0] += x * 8; - end[1] += y * 8; - end[2] += z * 12; - numareas = AAS_TraceAreas(origin, end, areas, points, 10); - for (j = 0; j < numareas; j++) - { - if (AAS_AreaReachability(areas[j])) - { - VectorSubtract(points[j], origin, v); - dist = VectorLength(v); - if (dist < bestdist) - { - bestareanum = areas[j]; - bestdist = dist; - } //end if - } //end if - if (!firstareanum) firstareanum = areas[j]; - } //end for - } //end for - } //end for - if (bestareanum) return bestareanum; - } //end for - return firstareanum; -} //end of the function BotFuzzyPointReachabilityArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotReachabilityArea(vec3_t origin, int client) -{ - int modelnum, modeltype, reachnum, areanum; - aas_reachability_t reach; - vec3_t org, end, mins, maxs, up = {0, 0, 1}; - bsp_trace_t bsptrace; - aas_trace_t trace; - - //check if the bot is standing on something - AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs); - VectorMA(origin, -3, up, end); - bsptrace = AAS_Trace(origin, mins, maxs, end, client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); - if (!bsptrace.startsolid && bsptrace.fraction < 1 && bsptrace.ent != ENTITYNUM_NONE) - { - //if standing on the world the bot should be in a valid area - if (bsptrace.ent == ENTITYNUM_WORLD) - { - return BotFuzzyPointReachabilityArea(origin); - } //end if - - modelnum = AAS_EntityModelindex(bsptrace.ent); - modeltype = modeltypes[modelnum]; - - //if standing on a func_plat or func_bobbing then the bot is assumed to be - //in the area the reachability points to - if (modeltype == MODELTYPE_FUNC_PLAT || modeltype == MODELTYPE_FUNC_BOB) - { - reachnum = AAS_NextModelReachability(0, modelnum); - if (reachnum) - { - AAS_ReachabilityFromNum(reachnum, &reach); - return reach.areanum; - } //end if - } //end else if - - //if the bot is swimming the bot should be in a valid area - if (AAS_Swimming(origin)) - { - return BotFuzzyPointReachabilityArea(origin); - } //end if - // - areanum = BotFuzzyPointReachabilityArea(origin); - //if the bot is in an area with reachabilities - if (areanum && AAS_AreaReachability(areanum)) return areanum; - //trace down till the ground is hit because the bot is standing on some other entity - VectorCopy(origin, org); - VectorCopy(org, end); - end[2] -= 800; - trace = AAS_TraceClientBBox(org, end, PRESENCE_CROUCH, -1); - if (!trace.startsolid) - { - VectorCopy(trace.endpos, org); - } //end if - // - return BotFuzzyPointReachabilityArea(org); - } //end if - // - return BotFuzzyPointReachabilityArea(origin); -} //end of the function BotReachabilityArea -//=========================================================================== -// returns the reachability area the bot is in -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -/* -int BotReachabilityArea(vec3_t origin, int testground) -{ - int firstareanum, i, j, x, y, z; - int areas[10], numareas, areanum, bestareanum; - float dist, bestdist; - vec3_t org, end, points[10], v; - aas_trace_t trace; - - firstareanum = 0; - for (i = 0; i < 2; i++) - { - VectorCopy(origin, org); - //if test at the ground (used when bot is standing on an entity) - if (i > 0) - { - VectorCopy(origin, end); - end[2] -= 800; - trace = AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1); - if (!trace.startsolid) - { - VectorCopy(trace.endpos, org); - } //end if - } //end if - - firstareanum = 0; - areanum = AAS_PointAreaNum(org); - if (areanum) - { - firstareanum = areanum; - if (AAS_AreaReachability(areanum)) return areanum; - } //end if - bestdist = 999999; - bestareanum = 0; - for (z = 1; z >= -1; z -= 1) - { - for (x = 1; x >= -1; x -= 1) - { - for (y = 1; y >= -1; y -= 1) - { - VectorCopy(org, end); - end[0] += x * 8; - end[1] += y * 8; - end[2] += z * 12; - numareas = AAS_TraceAreas(org, end, areas, points, 10); - for (j = 0; j < numareas; j++) - { - if (AAS_AreaReachability(areas[j])) - { - VectorSubtract(points[j], org, v); - dist = VectorLength(v); - if (dist < bestdist) - { - bestareanum = areas[j]; - bestdist = dist; - } //end if - } //end if - } //end for - } //end for - } //end for - if (bestareanum) return bestareanum; - } //end for - if (!testground) break; - } //end for -//#ifdef DEBUG - //botimport.Print(PRT_MESSAGE, "no reachability area\n"); -//#endif //DEBUG - return firstareanum; -} //end of the function BotReachabilityArea*/ -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotOnMover(vec3_t origin, int entnum, aas_reachability_t *reach) -{ - int i, modelnum; - vec3_t mins, maxs, modelorigin, org, end; - vec3_t angles = {0, 0, 0}; - vec3_t boxmins = {-16, -16, -8}, boxmaxs = {16, 16, 8}; - bsp_trace_t trace; - - modelnum = reach->facenum & 0x0000FFFF; - //get some bsp model info - AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); - // - if (!AAS_OriginOfMoverWithModelNum(modelnum, modelorigin)) - { - botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); - return qfalse; - } //end if - // - for (i = 0; i < 2; i++) - { - if (origin[i] > modelorigin[i] + maxs[i] + 16) return qfalse; - if (origin[i] < modelorigin[i] + mins[i] - 16) return qfalse; - } //end for - // - VectorCopy(origin, org); - org[2] += 24; - VectorCopy(origin, end); - end[2] -= 48; - // - trace = AAS_Trace(org, boxmins, boxmaxs, end, entnum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); - if (!trace.startsolid && !trace.allsolid) - { - //NOTE: the reachability face number is the model number of the elevator - if (trace.ent != ENTITYNUM_NONE && AAS_EntityModelNum(trace.ent) == modelnum) - { - return qtrue; - } //end if - } //end if - return qfalse; -} //end of the function BotOnMover -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int MoverDown(aas_reachability_t *reach) -{ - int modelnum; - vec3_t mins, maxs, origin; - vec3_t angles = {0, 0, 0}; - - modelnum = reach->facenum & 0x0000FFFF; - //get some bsp model info - AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); - // - if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) - { - botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); - return qfalse; - } //end if - //if the top of the plat is below the reachability start point - if (origin[2] + maxs[2] < reach->start[2]) return qtrue; - return qfalse; -} //end of the function MoverDown -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -void BotSetBrushModelTypes(void) -{ - int ent, modelnum; - char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; - - Com_Memset(modeltypes, 0, MAX_MODELS * sizeof(int)); - // - for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) - { - if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; - if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) continue; - if (model[0]) modelnum = atoi(model+1); - else modelnum = 0; - - if (modelnum < 0 || modelnum > MAX_MODELS) - { - botimport.Print(PRT_MESSAGE, "entity %s model number out of range\n", classname); - continue; - } //end if - - if (!Q_stricmp(classname, "func_bobbing")) - modeltypes[modelnum] = MODELTYPE_FUNC_BOB; - else if (!Q_stricmp(classname, "func_plat")) - modeltypes[modelnum] = MODELTYPE_FUNC_PLAT; - else if (!Q_stricmp(classname, "func_door")) - modeltypes[modelnum] = MODELTYPE_FUNC_DOOR; - else if (!Q_stricmp(classname, "func_static")) - modeltypes[modelnum] = MODELTYPE_FUNC_STATIC; - } //end for -} //end of the function BotSetBrushModelTypes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotOnTopOfEntity(bot_movestate_t *ms) -{ - vec3_t mins, maxs, end, up = {0, 0, 1}; - bsp_trace_t trace; - - AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); - VectorMA(ms->origin, -3, up, end); - trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); - if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) - { - return trace.ent; - } //end if - return -1; -} //end of the function BotOnTopOfEntity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotValidTravel(vec3_t origin, aas_reachability_t *reach, int travelflags) -{ - //if the reachability uses an unwanted travel type - if (AAS_TravelFlagForType(reach->traveltype) & ~travelflags) return qfalse; - //don't go into areas with bad travel types - if (AAS_AreaContentsTravelFlags(reach->areanum) & ~travelflags) return qfalse; - return qtrue; -} //end of the function BotValidTravel -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotAddToAvoidReach(bot_movestate_t *ms, int number, float avoidtime) -{ - int i; - - for (i = 0; i < MAX_AVOIDREACH; i++) - { - if (ms->avoidreach[i] == number) - { - if (ms->avoidreachtimes[i] > AAS_Time()) ms->avoidreachtries[i]++; - else ms->avoidreachtries[i] = 1; - ms->avoidreachtimes[i] = AAS_Time() + avoidtime; - return; - } //end if - } //end for - //add the reachability to the reachabilities to avoid for a while - for (i = 0; i < MAX_AVOIDREACH; i++) - { - if (ms->avoidreachtimes[i] < AAS_Time()) - { - ms->avoidreach[i] = number; - ms->avoidreachtimes[i] = AAS_Time() + avoidtime; - ms->avoidreachtries[i] = 1; - return; - } //end if - } //end for -} //end of the function BotAddToAvoidReach -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2) -{ - vec3_t proj, dir; - int j; - - AAS_ProjectPointOntoVector(p, lp1, lp2, proj); - for (j = 0; j < 3; j++) - if ((proj[j] > lp1[j] && proj[j] > lp2[j]) || - (proj[j] < lp1[j] && proj[j] < lp2[j])) - break; - if (j < 3) { - if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j])) - VectorSubtract(p, lp1, dir); - else - VectorSubtract(p, lp2, dir); - return VectorLengthSquared(dir); - } - VectorSubtract(p, proj, dir); - return VectorLengthSquared(dir); -} //end of the function DistanceFromLineSquared -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float VectorDistanceSquared(vec3_t p1, vec3_t p2) -{ - vec3_t dir; - VectorSubtract(p2, p1, dir); - return VectorLengthSquared(dir); -} //end of the function VectorDistanceSquared -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotAvoidSpots(vec3_t origin, aas_reachability_t *reach, bot_avoidspot_t *avoidspots, int numavoidspots) -{ - int checkbetween, i, type; - float squareddist, squaredradius; - - switch(reach->traveltype & TRAVELTYPE_MASK) - { - case TRAVEL_WALK: checkbetween = qtrue; break; - case TRAVEL_CROUCH: checkbetween = qtrue; break; - case TRAVEL_BARRIERJUMP: checkbetween = qtrue; break; - case TRAVEL_LADDER: checkbetween = qtrue; break; - case TRAVEL_WALKOFFLEDGE: checkbetween = qfalse; break; - case TRAVEL_JUMP: checkbetween = qfalse; break; - case TRAVEL_SWIM: checkbetween = qtrue; break; - case TRAVEL_WATERJUMP: checkbetween = qtrue; break; - case TRAVEL_TELEPORT: checkbetween = qfalse; break; - case TRAVEL_ELEVATOR: checkbetween = qfalse; break; - case TRAVEL_GRAPPLEHOOK: checkbetween = qfalse; break; - case TRAVEL_ROCKETJUMP: checkbetween = qfalse; break; - case TRAVEL_BFGJUMP: checkbetween = qfalse; break; - case TRAVEL_JUMPPAD: checkbetween = qfalse; break; - case TRAVEL_FUNCBOB: checkbetween = qfalse; break; - default: checkbetween = qtrue; break; - } //end switch - - type = AVOID_CLEAR; - for (i = 0; i < numavoidspots; i++) - { - squaredradius = Square(avoidspots[i].radius); - squareddist = DistanceFromLineSquared(avoidspots[i].origin, origin, reach->start); - // if moving towards the avoid spot - if (squareddist < squaredradius && - VectorDistanceSquared(avoidspots[i].origin, origin) > squareddist) - { - type = avoidspots[i].type; - } //end if - else if (checkbetween) { - squareddist = DistanceFromLineSquared(avoidspots[i].origin, reach->start, reach->end); - // if moving towards the avoid spot - if (squareddist < squaredradius && - VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist) - { - type = avoidspots[i].type; - } //end if - } //end if - else - { - VectorDistanceSquared(avoidspots[i].origin, reach->end); - // if the reachability leads closer to the avoid spot - if (squareddist < squaredradius && - VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist) - { - type = avoidspots[i].type; - } //end if - } //end else - if (type == AVOID_ALWAYS) - return type; - } //end for - return type; -} //end of the function BotAvoidSpots -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type) -{ - bot_movestate_t *ms; - - ms = BotMoveStateFromHandle(movestate); - if (!ms) return; - if (type == AVOID_CLEAR) - { - ms->numavoidspots = 0; - return; - } //end if - - if (ms->numavoidspots >= MAX_AVOIDSPOTS) - return; - VectorCopy(origin, ms->avoidspots[ms->numavoidspots].origin); - ms->avoidspots[ms->numavoidspots].radius = radius; - ms->avoidspots[ms->numavoidspots].type = type; - ms->numavoidspots++; -} //end of the function BotAddAvoidSpot -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotGetReachabilityToGoal(vec3_t origin, int areanum, - int lastgoalareanum, int lastareanum, - int *avoidreach, float *avoidreachtimes, int *avoidreachtries, - bot_goal_t *goal, int travelflags, int movetravelflags, - struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags) -{ - int i, t, besttime, bestreachnum, reachnum; - aas_reachability_t reach; - - //if not in a valid area - if (!areanum) return 0; - // - if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goal->areanum)) - { - travelflags |= TFL_DONOTENTER; - movetravelflags |= TFL_DONOTENTER; - } //end if - //use the routing to find the next area to go to - besttime = 0; - bestreachnum = 0; - // - for (reachnum = AAS_NextAreaReachability(areanum, 0); reachnum; - reachnum = AAS_NextAreaReachability(areanum, reachnum)) - { -#ifdef AVOIDREACH - //check if it isn't an reachability to avoid - for (i = 0; i < MAX_AVOIDREACH; i++) - { - if (avoidreach[i] == reachnum && avoidreachtimes[i] >= AAS_Time()) break; - } //end for - if (i != MAX_AVOIDREACH && avoidreachtries[i] > AVOIDREACH_TRIES) - { -#ifdef DEBUG - if (bot_developer) - { - botimport.Print(PRT_MESSAGE, "avoiding reachability %d\n", avoidreach[i]); - } //end if -#endif //DEBUG - continue; - } //end if -#endif //AVOIDREACH - //get the reachability from the number - AAS_ReachabilityFromNum(reachnum, &reach); - //NOTE: do not go back to the previous area if the goal didn't change - //NOTE: is this actually avoidance of local routing minima between two areas??? - if (lastgoalareanum == goal->areanum && reach.areanum == lastareanum) continue; - //if (AAS_AreaContentsTravelFlags(reach.areanum) & ~travelflags) continue; - //if the travel isn't valid - if (!BotValidTravel(origin, &reach, movetravelflags)) continue; - //get the travel time - t = AAS_AreaTravelTimeToGoalArea(reach.areanum, reach.end, goal->areanum, travelflags); - //if the goal area isn't reachable from the reachable area - if (!t) continue; - //if the bot should not use this reachability to avoid bad spots - if (BotAvoidSpots(origin, &reach, avoidspots, numavoidspots)) { - if (flags) { - *flags |= MOVERESULT_BLOCKEDBYAVOIDSPOT; - } - continue; - } - //add the travel time towards the area - t += reach.traveltime;// + AAS_AreaTravelTime(areanum, origin, reach.start); - //if the travel time is better than the ones already found - if (!besttime || t < besttime) - { - besttime = t; - bestreachnum = reachnum; - } //end if - } //end for - // - return bestreachnum; -} //end of the function BotGetReachabilityToGoal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotAddToTarget(vec3_t start, vec3_t end, float maxdist, float *dist, vec3_t target) -{ - vec3_t dir; - float curdist; - - VectorSubtract(end, start, dir); - curdist = VectorNormalize(dir); - if (*dist + curdist < maxdist) - { - VectorCopy(end, target); - *dist += curdist; - return qfalse; - } //end if - else - { - VectorMA(start, maxdist - *dist, dir, target); - *dist = maxdist; - return qtrue; - } //end else -} //end of the function BotAddToTarget - -int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target) -{ - aas_reachability_t reach; - int reachnum, lastareanum; - bot_movestate_t *ms; - vec3_t end; - float dist; - - ms = BotMoveStateFromHandle(movestate); - if (!ms) return qfalse; - reachnum = 0; - //if the bot has no goal or no last reachability - if (!ms->lastreachnum || !goal) return qfalse; - - reachnum = ms->lastreachnum; - VectorCopy(ms->origin, end); - lastareanum = ms->lastareanum; - dist = 0; - while(reachnum && dist < lookahead) - { - AAS_ReachabilityFromNum(reachnum, &reach); - if (BotAddToTarget(end, reach.start, lookahead, &dist, target)) return qtrue; - //never look beyond teleporters - if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_TELEPORT) return qtrue; - //never look beyond the weapon jump point - if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) return qtrue; - if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_BFGJUMP) return qtrue; - //don't add jump pad distances - if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_JUMPPAD && - (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR && - (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB) - { - if (BotAddToTarget(reach.start, reach.end, lookahead, &dist, target)) return qtrue; - } //end if - reachnum = BotGetReachabilityToGoal(reach.end, reach.areanum, - ms->lastgoalareanum, lastareanum, - ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, - goal, travelflags, travelflags, NULL, 0, NULL); - VectorCopy(reach.end, end); - lastareanum = reach.areanum; - if (lastareanum == goal->areanum) - { - BotAddToTarget(reach.end, goal->origin, lookahead, &dist, target); - return qtrue; - } //end if - } //end while - // - return qfalse; -} //end of the function BotMovementViewTarget -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotVisible(int ent, vec3_t eye, vec3_t target) -{ - bsp_trace_t trace; - - trace = AAS_Trace(eye, NULL, NULL, target, ent, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); - if (trace.fraction >= 1) return qtrue; - return qfalse; -} //end of the function BotVisible -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target) -{ - aas_reachability_t reach; - int reachnum, lastgoalareanum, lastareanum, i; - int avoidreach[MAX_AVOIDREACH]; - float avoidreachtimes[MAX_AVOIDREACH]; - int avoidreachtries[MAX_AVOIDREACH]; - vec3_t end; - - //if the bot has no goal or no last reachability - if (!goal) return qfalse; - //if the areanum is not valid - if (!areanum) return qfalse; - //if the goal areanum is not valid - if (!goal->areanum) return qfalse; - - Com_Memset(avoidreach, 0, MAX_AVOIDREACH * sizeof(int)); - lastgoalareanum = goal->areanum; - lastareanum = areanum; - VectorCopy(origin, end); - //only do 20 hops - for (i = 0; i < 20 && (areanum != goal->areanum); i++) - { - // - reachnum = BotGetReachabilityToGoal(end, areanum, - lastgoalareanum, lastareanum, - avoidreach, avoidreachtimes, avoidreachtries, - goal, travelflags, travelflags, NULL, 0, NULL); - if (!reachnum) return qfalse; - AAS_ReachabilityFromNum(reachnum, &reach); - // - if (BotVisible(goal->entitynum, goal->origin, reach.start)) - { - VectorCopy(reach.start, target); - return qtrue; - } //end if - // - if (BotVisible(goal->entitynum, goal->origin, reach.end)) - { - VectorCopy(reach.end, target); - return qtrue; - } //end if - // - if (reach.areanum == goal->areanum) - { - VectorCopy(reach.end, target); - return qtrue; - } //end if - // - lastareanum = areanum; - areanum = reach.areanum; - VectorCopy(reach.end, end); - // - } //end while - // - return qfalse; -} //end of the function BotPredictVisiblePosition -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void MoverBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter) -{ - int modelnum; - vec3_t mins, maxs, origin, mids; - vec3_t angles = {0, 0, 0}; - - modelnum = reach->facenum & 0x0000FFFF; - //get some bsp model info - AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); - // - if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) - { - botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); - } //end if - //get a point just above the plat in the bottom position - VectorAdd(mins, maxs, mids); - VectorMA(origin, 0.5, mids, bottomcenter); - bottomcenter[2] = reach->start[2]; -} //end of the function MoverBottomCenter -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum) -{ - float dist, startz; - vec3_t start, end; - aas_trace_t trace; - - //do gap checking - startz = origin[2]; - //this enables walking down stairs more fluidly - { - VectorCopy(origin, start); - VectorCopy(origin, end); - end[2] -= 60; - trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum); - if (trace.fraction >= 1) return 1; - startz = trace.endpos[2] + 1; - } - // - for (dist = 8; dist <= 100; dist += 8) - { - VectorMA(origin, dist, hordir, start); - start[2] = startz + 24; - VectorCopy(start, end); - end[2] -= 48 + sv_maxbarrier->value; - trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum); - //if solid is found the bot can't walk any further and fall into a gap - if (!trace.startsolid) - { - //if it is a gap - if (trace.endpos[2] < startz - sv_maxstep->value - 8) - { - VectorCopy(trace.endpos, end); - end[2] -= 20; - if (AAS_PointContents(end) & CONTENTS_WATER) break; - //if a gap is found slow down - //botimport.Print(PRT_MESSAGE, "gap at %f\n", dist); - return dist; - } //end if - startz = trace.endpos[2]; - } //end if - } //end for - return 0; -} //end of the function BotGapDistance -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotCheckBarrierJump(bot_movestate_t *ms, vec3_t dir, float speed) -{ - vec3_t start, hordir, end; - aas_trace_t trace; - - VectorCopy(ms->origin, end); - end[2] += sv_maxbarrier->value; - //trace right up - trace = AAS_TraceClientBBox(ms->origin, end, PRESENCE_NORMAL, ms->entitynum); - //this shouldn't happen... but we check anyway - if (trace.startsolid) return qfalse; - //if very low ceiling it isn't possible to jump up to a barrier - if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse; - // - hordir[0] = dir[0]; - hordir[1] = dir[1]; - hordir[2] = 0; - VectorNormalize(hordir); - VectorMA(ms->origin, ms->thinktime * speed * 0.5, hordir, end); - VectorCopy(trace.endpos, start); - end[2] = trace.endpos[2]; - //trace from previous trace end pos horizontally in the move direction - trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum); - //again this shouldn't happen - if (trace.startsolid) return qfalse; - // - VectorCopy(trace.endpos, start); - VectorCopy(trace.endpos, end); - end[2] = ms->origin[2]; - //trace down from the previous trace end pos - trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum); - //if solid - if (trace.startsolid) return qfalse; - //if no obstacle at all - if (trace.fraction >= 1.0) return qfalse; - //if less than the maximum step height - if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse; - // - EA_Jump(ms->client); - EA_Move(ms->client, hordir, speed); - ms->moveflags |= MFL_BARRIERJUMP; - //there is a barrier - return qtrue; -} //end of the function BotCheckBarrierJump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotSwimInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type) -{ - vec3_t normdir; - - VectorCopy(dir, normdir); - VectorNormalize(normdir); - EA_Move(ms->client, normdir, speed); - return qtrue; -} //end of the function BotSwimInDirection -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotWalkInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type) -{ - vec3_t hordir, cmdmove, velocity, tmpdir, origin; - int presencetype, maxframes, cmdframes, stopevent; - aas_clientmove_t move; - float dist; - - if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND; - //if the bot is on the ground - if (ms->moveflags & MFL_ONGROUND) - { - //if there is a barrier the bot can jump on - if (BotCheckBarrierJump(ms, dir, speed)) return qtrue; - //remove barrier jump flag - ms->moveflags &= ~MFL_BARRIERJUMP; - //get the presence type for the movement - if ((type & MOVE_CROUCH) && !(type & MOVE_JUMP)) presencetype = PRESENCE_CROUCH; - else presencetype = PRESENCE_NORMAL; - //horizontal direction - hordir[0] = dir[0]; - hordir[1] = dir[1]; - hordir[2] = 0; - VectorNormalize(hordir); - //if the bot is not supposed to jump - if (!(type & MOVE_JUMP)) - { - //if there is a gap, try to jump over it - if (BotGapDistance(ms->origin, hordir, ms->entitynum) > 0) type |= MOVE_JUMP; - } //end if - //get command movement - VectorScale(hordir, speed, cmdmove); - VectorCopy(ms->velocity, velocity); - // - if (type & MOVE_JUMP) - { - //botimport.Print(PRT_MESSAGE, "trying jump\n"); - cmdmove[2] = 400; - maxframes = PREDICTIONTIME_JUMP / 0.1; - cmdframes = 1; - stopevent = SE_HITGROUND|SE_HITGROUNDDAMAGE| - SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA; - } //end if - else - { - maxframes = 2; - cmdframes = 2; - stopevent = SE_HITGROUNDDAMAGE| - SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA; - } //end else - //AAS_ClearShownDebugLines(); - // - VectorCopy(ms->origin, origin); - origin[2] += 0.5; - AAS_PredictClientMovement(&move, ms->entitynum, origin, presencetype, qtrue, - velocity, cmdmove, cmdframes, maxframes, 0.1f, - stopevent, 0, qfalse);//qtrue); - //if prediction time wasn't enough to fully predict the movement - if (move.frames >= maxframes && (type & MOVE_JUMP)) - { - //botimport.Print(PRT_MESSAGE, "client %d: max prediction frames\n", ms->client); - return qfalse; - } //end if - //don't enter slime or lava and don't fall from too high - if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) - { - //botimport.Print(PRT_MESSAGE, "client %d: would be hurt ", ms->client); - //if (move.stopevent & SE_ENTERSLIME) botimport.Print(PRT_MESSAGE, "slime\n"); - //if (move.stopevent & SE_ENTERLAVA) botimport.Print(PRT_MESSAGE, "lava\n"); - //if (move.stopevent & SE_HITGROUNDDAMAGE) botimport.Print(PRT_MESSAGE, "hitground\n"); - return qfalse; - } //end if - //if ground was hit - if (move.stopevent & SE_HITGROUND) - { - //check for nearby gap - VectorNormalize2(move.velocity, tmpdir); - dist = BotGapDistance(move.endpos, tmpdir, ms->entitynum); - if (dist > 0) return qfalse; - // - dist = BotGapDistance(move.endpos, hordir, ms->entitynum); - if (dist > 0) return qfalse; - } //end if - //get horizontal movement - tmpdir[0] = move.endpos[0] - ms->origin[0]; - tmpdir[1] = move.endpos[1] - ms->origin[1]; - tmpdir[2] = 0; - // - //AAS_DrawCross(move.endpos, 4, LINECOLOR_BLUE); - //the bot is blocked by something - if (VectorLength(tmpdir) < speed * ms->thinktime * 0.5) return qfalse; - //perform the movement - if (type & MOVE_JUMP) EA_Jump(ms->client); - if (type & MOVE_CROUCH) EA_Crouch(ms->client); - EA_Move(ms->client, hordir, speed); - //movement was succesfull - return qtrue; - } //end if - else - { - if (ms->moveflags & MFL_BARRIERJUMP) - { - //if near the top or going down - if (ms->velocity[2] < 50) - { - EA_Move(ms->client, dir, speed); - } //end if - } //end if - //FIXME: do air control to avoid hazards - return qtrue; - } //end else -} //end of the function BotWalkInDirection -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type) -{ - bot_movestate_t *ms; - - ms = BotMoveStateFromHandle(movestate); - if (!ms) return qfalse; - //if swimming - if (AAS_Swimming(ms->origin)) - { - return BotSwimInDirection(ms, dir, speed, type); - } //end if - else - { - return BotWalkInDirection(ms, dir, speed, type); - } //end else -} //end of the function BotMoveInDirection -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Intersection(vec2_t p1, vec2_t p2, vec2_t p3, vec2_t p4, vec2_t out) -{ - float x1, dx1, dy1, x2, dx2, dy2, d; - - dx1 = p2[0] - p1[0]; - dy1 = p2[1] - p1[1]; - dx2 = p4[0] - p3[0]; - dy2 = p4[1] - p3[1]; - - d = dy1 * dx2 - dx1 * dy2; - if (d != 0) - { - x1 = p1[1] * dx1 - p1[0] * dy1; - x2 = p3[1] * dx2 - p3[0] * dy2; - out[0] = (int) ((dx1 * x2 - dx2 * x1) / d); - out[1] = (int) ((dy1 * x2 - dy2 * x1) / d); - return qtrue; - } //end if - else - { - return qfalse; - } //end else -} //end of the function Intersection -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotCheckBlocked(bot_movestate_t *ms, vec3_t dir, int checkbottom, bot_moveresult_t *result) -{ - vec3_t mins, maxs, end, up = {0, 0, 1}; - bsp_trace_t trace; - - //test for entities obstructing the bot's path - AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); - // - if (fabs(DotProduct(dir, up)) < 0.7) - { - mins[2] += sv_maxstep->value; //if the bot can step on - maxs[2] -= 10; //a little lower to avoid low ceiling - } //end if - VectorMA(ms->origin, 3, dir, end); - trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY); - //if not started in solid and not hitting the world entity - if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) - { - result->blocked = qtrue; - result->blockentity = trace.ent; -#ifdef DEBUG - //botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client); -#endif //DEBUG - } //end if - //if not in an area with reachability - else if (checkbottom && !AAS_AreaReachability(ms->areanum)) - { - //check if the bot is standing on something - AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); - VectorMA(ms->origin, -3, up, end); - trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); - if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) - { - result->blocked = qtrue; - result->blockentity = trace.ent; - result->flags |= MOVERESULT_ONTOPOFOBSTACLE; -#ifdef DEBUG - //botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client); -#endif //DEBUG - } //end if - } //end else -} //end of the function BotCheckBlocked -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotClearMoveResult(bot_moveresult_t *moveresult) -{ - moveresult->failure = qfalse; - moveresult->type = 0; - moveresult->blocked = qfalse; - moveresult->blockentity = 0; - moveresult->traveltype = 0; - moveresult->flags = 0; -} //end of the function BotClearMoveResult -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach) -{ - float dist, speed; - vec3_t hordir; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //first walk straight to the reachability start - hordir[0] = reach->start[0] - ms->origin[0]; - hordir[1] = reach->start[1] - ms->origin[1]; - hordir[2] = 0; - dist = VectorNormalize(hordir); - // - BotCheckBlocked(ms, hordir, qtrue, &result); - // - if (dist < 10) - { - //walk straight to the reachability end - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - dist = VectorNormalize(hordir); - } //end if - //if going towards a crouch area - if (!(AAS_AreaPresenceType(reach->areanum) & PRESENCE_NORMAL)) - { - //if pretty close to the reachable area - if (dist < 20) EA_Crouch(ms->client); - } //end if - // - dist = BotGapDistance(ms->origin, hordir, ms->entitynum); - // - if (ms->moveflags & MFL_WALK) - { - if (dist > 0) speed = 200 - (180 - 1 * dist); - else speed = 200; - EA_Walk(ms->client); - } //end if - else - { - if (dist > 0) speed = 400 - (360 - 2 * dist); - else speed = 400; - } //end else - //elemantary action move in direction - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotTravel_Walk -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotFinishTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t hordir; - float dist, speed; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //if not on the ground and changed areas... don't walk back!! - //(doesn't seem to help) - /* - ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); - if (ms->areanum == reach->areanum) - { -#ifdef DEBUG - botimport.Print(PRT_MESSAGE, "BotFinishTravel_Walk: already in reach area\n"); -#endif //DEBUG - return result; - } //end if*/ - //go straight to the reachability end - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - dist = VectorNormalize(hordir); - // - if (dist > 100) dist = 100; - speed = 400 - (400 - 3 * dist); - // - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotFinishTravel_Walk -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_Crouch(bot_movestate_t *ms, aas_reachability_t *reach) -{ - float speed; - vec3_t hordir; - bot_moveresult_t result; - - BotClearMoveResult(&result); - // - speed = 400; - //walk straight to reachability end - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - VectorNormalize(hordir); - // - BotCheckBlocked(ms, hordir, qtrue, &result); - //elemantary actions - EA_Crouch(ms->client); - EA_Move(ms->client, hordir, speed); - // - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotTravel_Crouch -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach) -{ - float dist, speed; - vec3_t hordir; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //walk straight to reachability start - hordir[0] = reach->start[0] - ms->origin[0]; - hordir[1] = reach->start[1] - ms->origin[1]; - hordir[2] = 0; - dist = VectorNormalize(hordir); - // - BotCheckBlocked(ms, hordir, qtrue, &result); - //if pretty close to the barrier - if (dist < 9) - { - EA_Jump(ms->client); - } //end if - else - { - if (dist > 60) dist = 60; - speed = 360 - (360 - 6 * dist); - EA_Move(ms->client, hordir, speed); - } //end else - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotTravel_BarrierJump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotFinishTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach) -{ - float dist; - vec3_t hordir; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //if near the top or going down - if (ms->velocity[2] < 250) - { - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - dist = VectorNormalize(hordir); - // - BotCheckBlocked(ms, hordir, qtrue, &result); - // - EA_Move(ms->client, hordir, 400); - VectorCopy(hordir, result.movedir); - } //end if - // - return result; -} //end of the function BotFinishTravel_BarrierJump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_Swim(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t dir; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //swim straight to reachability end - VectorSubtract(reach->start, ms->origin, dir); - VectorNormalize(dir); - // - BotCheckBlocked(ms, dir, qtrue, &result); - //elemantary actions - EA_Move(ms->client, dir, 400); - // - VectorCopy(dir, result.movedir); - Vector2Angles(dir, result.ideal_viewangles); - result.flags |= MOVERESULT_SWIMVIEW; - // - return result; -} //end of the function BotTravel_Swim -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t dir, hordir; - float dist; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //swim straight to reachability end - VectorSubtract(reach->end, ms->origin, dir); - VectorCopy(dir, hordir); - hordir[2] = 0; - dir[2] += 15 + crandom() * 40; - //botimport.Print(PRT_MESSAGE, "BotTravel_WaterJump: dir[2] = %f\n", dir[2]); - VectorNormalize(dir); - dist = VectorNormalize(hordir); - //elemantary actions - //EA_Move(ms->client, dir, 400); - EA_MoveForward(ms->client); - //move up if close to the actual out of water jump spot - if (dist < 40) EA_MoveUp(ms->client); - //set the ideal view angles - Vector2Angles(dir, result.ideal_viewangles); - result.flags |= MOVERESULT_MOVEMENTVIEW; - // - VectorCopy(dir, result.movedir); - // - return result; -} //end of the function BotTravel_WaterJump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotFinishTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t dir, pnt; - float dist; - bot_moveresult_t result; - - //botimport.Print(PRT_MESSAGE, "BotFinishTravel_WaterJump\n"); - BotClearMoveResult(&result); - //if waterjumping there's nothing to do - if (ms->moveflags & MFL_WATERJUMP) return result; - //if not touching any water anymore don't do anything - //otherwise the bot sometimes keeps jumping? - VectorCopy(ms->origin, pnt); - pnt[2] -= 32; //extra for q2dm4 near red armor/mega health - if (!(AAS_PointContents(pnt) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return result; - //swim straight to reachability end - VectorSubtract(reach->end, ms->origin, dir); - dir[0] += crandom() * 10; - dir[1] += crandom() * 10; - dir[2] += 70 + crandom() * 10; - dist = VectorNormalize(dir); - //elemantary actions - EA_Move(ms->client, dir, 400); - //set the ideal view angles - Vector2Angles(dir, result.ideal_viewangles); - result.flags |= MOVERESULT_MOVEMENTVIEW; - // - VectorCopy(dir, result.movedir); - // - return result; -} //end of the function BotFinishTravel_WaterJump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t hordir, dir; - float dist, speed, reachhordist; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //check if the bot is blocked by anything - VectorSubtract(reach->start, ms->origin, dir); - VectorNormalize(dir); - BotCheckBlocked(ms, dir, qtrue, &result); - //if the reachability start and end are practially above each other - VectorSubtract(reach->end, reach->start, dir); - dir[2] = 0; - reachhordist = VectorLength(dir); - //walk straight to the reachability start - hordir[0] = reach->start[0] - ms->origin[0]; - hordir[1] = reach->start[1] - ms->origin[1]; - hordir[2] = 0; - dist = VectorNormalize(hordir); - //if pretty close to the start focus on the reachability end - if (dist < 48) - { - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - VectorNormalize(hordir); - // - if (reachhordist < 20) - { - speed = 100; - } //end if - else if (!AAS_HorizontalVelocityForJump(0, reach->start, reach->end, &speed)) - { - speed = 400; - } //end if - } //end if - else - { - if (reachhordist < 20) - { - if (dist > 64) dist = 64; - speed = 400 - (256 - 4 * dist); - } //end if - else - { - speed = 400; - } //end else - } //end else - // - BotCheckBlocked(ms, hordir, qtrue, &result); - //elemantary action - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotTravel_WalkOffLedge -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotAirControl(vec3_t origin, vec3_t velocity, vec3_t goal, vec3_t dir, float *speed) -{ - vec3_t org, vel; - float dist; - int i; - - VectorCopy(origin, org); - VectorScale(velocity, 0.1, vel); - for (i = 0; i < 50; i++) - { - vel[2] -= sv_gravity->value * 0.01; - //if going down and next position would be below the goal - if (vel[2] < 0 && org[2] + vel[2] < goal[2]) - { - VectorScale(vel, (goal[2] - org[2]) / vel[2], vel); - VectorAdd(org, vel, org); - VectorSubtract(goal, org, dir); - dist = VectorNormalize(dir); - if (dist > 32) dist = 32; - *speed = 400 - (400 - 13 * dist); - return qtrue; - } //end if - else - { - VectorAdd(org, vel, org); - } //end else - } //end for - VectorSet(dir, 0, 0, 0); - *speed = 400; - return qfalse; -} //end of the function BotAirControl -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotFinishTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t dir, hordir, end, v; - float dist, speed; - bot_moveresult_t result; - - BotClearMoveResult(&result); - // - VectorSubtract(reach->end, ms->origin, dir); - BotCheckBlocked(ms, dir, qtrue, &result); - // - VectorSubtract(reach->end, ms->origin, v); - v[2] = 0; - dist = VectorNormalize(v); - if (dist > 16) VectorMA(reach->end, 16, v, end); - else VectorCopy(reach->end, end); - // - if (!BotAirControl(ms->origin, ms->velocity, end, hordir, &speed)) - { - //go straight to the reachability end - VectorCopy(dir, hordir); - hordir[2] = 0; - // - dist = VectorNormalize(hordir); - speed = 400; - } //end if - // - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotFinishTravel_WalkOffLedge -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -/* -bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t hordir; - float dist, gapdist, speed, horspeed, sv_jumpvel; - bot_moveresult_t result; - - BotClearMoveResult(&result); - // - sv_jumpvel = botlibglobals.sv_jumpvel->value; - //walk straight to the reachability start - hordir[0] = reach->start[0] - ms->origin[0]; - hordir[1] = reach->start[1] - ms->origin[1]; - hordir[2] = 0; - dist = VectorNormalize(hordir); - // - speed = 350; - // - gapdist = BotGapDistance(ms, hordir, ms->entitynum); - //if pretty close to the start focus on the reachability end - if (dist < 50 || (gapdist && gapdist < 50)) - { - //NOTE: using max speed (400) works best - //if (AAS_HorizontalVelocityForJump(sv_jumpvel, ms->origin, reach->end, &horspeed)) - //{ - // speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value; - //} //end if - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - VectorNormalize(hordir); - //elemantary action jump - EA_Jump(ms->client); - // - ms->jumpreach = ms->lastreachnum; - speed = 600; - } //end if - else - { - if (AAS_HorizontalVelocityForJump(sv_jumpvel, reach->start, reach->end, &horspeed)) - { - speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value; - } //end if - } //end else - //elemantary action - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotTravel_Jump*/ -/* -bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t hordir, dir1, dir2, mins, maxs, start, end; - float dist1, dist2, speed; - bot_moveresult_t result; - bsp_trace_t trace; - - BotClearMoveResult(&result); - // - hordir[0] = reach->start[0] - reach->end[0]; - hordir[1] = reach->start[1] - reach->end[1]; - hordir[2] = 0; - VectorNormalize(hordir); - // - VectorCopy(reach->start, start); - start[2] += 1; - //minus back the bouding box size plus 16 - VectorMA(reach->start, 80, hordir, end); - // - AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, mins, maxs); - //check for solids - trace = AAS_Trace(start, mins, maxs, end, ms->entitynum, MASK_PLAYERSOLID); - if (trace.startsolid) VectorCopy(start, trace.endpos); - //check for a gap - for (dist1 = 0; dist1 < 80; dist1 += 10) - { - VectorMA(start, dist1+10, hordir, end); - end[2] += 1; - if (AAS_PointAreaNum(end) != ms->reachareanum) break; - } //end for - if (dist1 < 80) VectorMA(reach->start, dist1, hordir, trace.endpos); -// dist1 = BotGapDistance(start, hordir, ms->entitynum); -// if (dist1 && dist1 <= trace.fraction * 80) VectorMA(reach->start, dist1-20, hordir, trace.endpos); - // - VectorSubtract(ms->origin, reach->start, dir1); - dir1[2] = 0; - dist1 = VectorNormalize(dir1); - VectorSubtract(ms->origin, trace.endpos, dir2); - dir2[2] = 0; - dist2 = VectorNormalize(dir2); - //if just before the reachability start - if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5) - { - //botimport.Print(PRT_MESSAGE, "between jump start and run to point\n"); - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - VectorNormalize(hordir); - //elemantary action jump - if (dist1 < 24) EA_Jump(ms->client); - else if (dist1 < 32) EA_DelayedJump(ms->client); - EA_Move(ms->client, hordir, 600); - // - ms->jumpreach = ms->lastreachnum; - } //end if - else - { - //botimport.Print(PRT_MESSAGE, "going towards run to point\n"); - hordir[0] = trace.endpos[0] - ms->origin[0]; - hordir[1] = trace.endpos[1] - ms->origin[1]; - hordir[2] = 0; - VectorNormalize(hordir); - // - if (dist2 > 80) dist2 = 80; - speed = 400 - (400 - 5 * dist2); - EA_Move(ms->client, hordir, speed); - } //end else - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotTravel_Jump*/ -//* -bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t hordir, dir1, dir2, start, end, runstart; -// vec3_t runstart, dir1, dir2, hordir; - float dist1, dist2, speed; - bot_moveresult_t result; - - BotClearMoveResult(&result); - // - AAS_JumpReachRunStart(reach, runstart); - //* - hordir[0] = runstart[0] - reach->start[0]; - hordir[1] = runstart[1] - reach->start[1]; - hordir[2] = 0; - VectorNormalize(hordir); - // - VectorCopy(reach->start, start); - start[2] += 1; - VectorMA(reach->start, 80, hordir, runstart); - //check for a gap - for (dist1 = 0; dist1 < 80; dist1 += 10) - { - VectorMA(start, dist1+10, hordir, end); - end[2] += 1; - if (AAS_PointAreaNum(end) != ms->reachareanum) break; - } //end for - if (dist1 < 80) VectorMA(reach->start, dist1, hordir, runstart); - // - VectorSubtract(ms->origin, reach->start, dir1); - dir1[2] = 0; - dist1 = VectorNormalize(dir1); - VectorSubtract(ms->origin, runstart, dir2); - dir2[2] = 0; - dist2 = VectorNormalize(dir2); - //if just before the reachability start - if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5) - { -// botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - VectorNormalize(hordir); - //elemantary action jump - if (dist1 < 24) EA_Jump(ms->client); - else if (dist1 < 32) EA_DelayedJump(ms->client); - EA_Move(ms->client, hordir, 600); - // - ms->jumpreach = ms->lastreachnum; - } //end if - else - { -// botimport.Print(PRT_MESSAGE, "going towards run start point\n"); - hordir[0] = runstart[0] - ms->origin[0]; - hordir[1] = runstart[1] - ms->origin[1]; - hordir[2] = 0; - VectorNormalize(hordir); - // - if (dist2 > 80) dist2 = 80; - speed = 400 - (400 - 5 * dist2); - EA_Move(ms->client, hordir, speed); - } //end else - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotTravel_Jump*/ -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotFinishTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t hordir, hordir2; - float speed, dist; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //if not jumped yet - if (!ms->jumpreach) return result; - //go straight to the reachability end - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - dist = VectorNormalize(hordir); - // - hordir2[0] = reach->end[0] - reach->start[0]; - hordir2[1] = reach->end[1] - reach->start[1]; - hordir2[2] = 0; - VectorNormalize(hordir2); - // - if (DotProduct(hordir, hordir2) < -0.5 && dist < 24) return result; - //always use max speed when traveling through the air - speed = 800; - // - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotFinishTravel_Jump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_Ladder(bot_movestate_t *ms, aas_reachability_t *reach) -{ - //float dist, speed; - vec3_t dir, viewdir;//, hordir; - vec3_t origin = {0, 0, 0}; -// vec3_t up = {0, 0, 1}; - bot_moveresult_t result; - - BotClearMoveResult(&result); - // -// if ((ms->moveflags & MFL_AGAINSTLADDER)) - //NOTE: not a good idea for ladders starting in water - // || !(ms->moveflags & MFL_ONGROUND)) - { - //botimport.Print(PRT_MESSAGE, "against ladder or not on ground\n"); - VectorSubtract(reach->end, ms->origin, dir); - VectorNormalize(dir); - //set the ideal view angles, facing the ladder up or down - viewdir[0] = dir[0]; - viewdir[1] = dir[1]; - viewdir[2] = 3 * dir[2]; - Vector2Angles(viewdir, result.ideal_viewangles); - //elemantary action - EA_Move(ms->client, origin, 0); - EA_MoveForward(ms->client); - //set movement view flag so the AI can see the view is focussed - result.flags |= MOVERESULT_MOVEMENTVIEW; - } //end if -/* else - { - //botimport.Print(PRT_MESSAGE, "moving towards ladder\n"); - VectorSubtract(reach->end, ms->origin, dir); - //make sure the horizontal movement is large anough - VectorCopy(dir, hordir); - hordir[2] = 0; - dist = VectorNormalize(hordir); - // - dir[0] = hordir[0]; - dir[1] = hordir[1]; - if (dir[2] > 0) dir[2] = 1; - else dir[2] = -1; - if (dist > 50) dist = 50; - speed = 400 - (200 - 4 * dist); - EA_Move(ms->client, dir, speed); - } //end else*/ - //save the movement direction - VectorCopy(dir, result.movedir); - // - return result; -} //end of the function BotTravel_Ladder -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_Teleport(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t hordir; - float dist; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //if the bot is being teleported - if (ms->moveflags & MFL_TELEPORTED) return result; - - //walk straight to center of the teleporter - VectorSubtract(reach->start, ms->origin, hordir); - if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; - dist = VectorNormalize(hordir); - // - BotCheckBlocked(ms, hordir, qtrue, &result); - - if (dist < 30) EA_Move(ms->client, hordir, 200); - else EA_Move(ms->client, hordir, 400); - - if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; - - VectorCopy(hordir, result.movedir); - return result; -} //end of the function BotTravel_Teleport -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t dir, dir1, dir2, hordir, bottomcenter; - float dist, dist1, dist2, speed; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //if standing on the plat - if (BotOnMover(ms->origin, ms->entitynum, reach)) - { -#ifdef DEBUG_ELEVATOR - botimport.Print(PRT_MESSAGE, "bot on elevator\n"); -#endif //DEBUG_ELEVATOR - //if vertically not too far from the end point - if (abs(ms->origin[2] - reach->end[2]) < sv_maxbarrier->value) - { -#ifdef DEBUG_ELEVATOR - botimport.Print(PRT_MESSAGE, "bot moving to end\n"); -#endif //DEBUG_ELEVATOR - //move to the end point - VectorSubtract(reach->end, ms->origin, hordir); - hordir[2] = 0; - VectorNormalize(hordir); - if (!BotCheckBarrierJump(ms, hordir, 100)) - { - EA_Move(ms->client, hordir, 400); - } //end if - VectorCopy(hordir, result.movedir); - } //end else - //if not really close to the center of the elevator - else - { - MoverBottomCenter(reach, bottomcenter); - VectorSubtract(bottomcenter, ms->origin, hordir); - hordir[2] = 0; - dist = VectorNormalize(hordir); - // - if (dist > 10) - { -#ifdef DEBUG_ELEVATOR - botimport.Print(PRT_MESSAGE, "bot moving to center\n"); -#endif //DEBUG_ELEVATOR - //move to the center of the plat - if (dist > 100) dist = 100; - speed = 400 - (400 - 4 * dist); - // - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - } //end if - } //end else - } //end if - else - { -#ifdef DEBUG_ELEVATOR - botimport.Print(PRT_MESSAGE, "bot not on elevator\n"); -#endif //DEBUG_ELEVATOR - //if very near the reachability end - VectorSubtract(reach->end, ms->origin, dir); - dist = VectorLength(dir); - if (dist < 64) - { - if (dist > 60) dist = 60; - speed = 360 - (360 - 6 * dist); - // - if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50)) - { - if (speed > 5) EA_Move(ms->client, dir, speed); - } //end if - VectorCopy(dir, result.movedir); - // - if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; - //stop using this reachability - ms->reachability_time = 0; - return result; - } //end if - //get direction and distance to reachability start - VectorSubtract(reach->start, ms->origin, dir1); - if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0; - dist1 = VectorNormalize(dir1); - //if the elevator isn't down - if (!MoverDown(reach)) - { -#ifdef DEBUG_ELEVATOR - botimport.Print(PRT_MESSAGE, "elevator not down\n"); -#endif //DEBUG_ELEVATOR - dist = dist1; - VectorCopy(dir1, dir); - // - BotCheckBlocked(ms, dir, qfalse, &result); - // - if (dist > 60) dist = 60; - speed = 360 - (360 - 6 * dist); - // - if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) - { - if (speed > 5) EA_Move(ms->client, dir, speed); - } //end if - VectorCopy(dir, result.movedir); - // - if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; - //this isn't a failure... just wait till the elevator comes down - result.type = RESULTTYPE_ELEVATORUP; - result.flags |= MOVERESULT_WAITING; - return result; - } //end if - //get direction and distance to elevator bottom center - MoverBottomCenter(reach, bottomcenter); - VectorSubtract(bottomcenter, ms->origin, dir2); - if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0; - dist2 = VectorNormalize(dir2); - //if very close to the reachability start or - //closer to the elevator center or - //between reachability start and elevator center - if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0) - { -#ifdef DEBUG_ELEVATOR - botimport.Print(PRT_MESSAGE, "bot moving to center\n"); -#endif //DEBUG_ELEVATOR - dist = dist2; - VectorCopy(dir2, dir); - } //end if - else //closer to the reachability start - { -#ifdef DEBUG_ELEVATOR - botimport.Print(PRT_MESSAGE, "bot moving to start\n"); -#endif //DEBUG_ELEVATOR - dist = dist1; - VectorCopy(dir1, dir); - } //end else - // - BotCheckBlocked(ms, dir, qfalse, &result); - // - if (dist > 60) dist = 60; - speed = 400 - (400 - 6 * dist); - // - if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) - { - EA_Move(ms->client, dir, speed); - } //end if - VectorCopy(dir, result.movedir); - // - if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; - } //end else - return result; -} //end of the function BotTravel_Elevator -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotFinishTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t bottomcenter, bottomdir, topdir; - bot_moveresult_t result; - - BotClearMoveResult(&result); - // - MoverBottomCenter(reach, bottomcenter); - VectorSubtract(bottomcenter, ms->origin, bottomdir); - // - VectorSubtract(reach->end, ms->origin, topdir); - // - if (fabs(bottomdir[2]) < fabs(topdir[2])) - { - VectorNormalize(bottomdir); - EA_Move(ms->client, bottomdir, 300); - } //end if - else - { - VectorNormalize(topdir); - EA_Move(ms->client, topdir, 300); - } //end else - return result; -} //end of the function BotFinishTravel_Elevator -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotFuncBobStartEnd(aas_reachability_t *reach, vec3_t start, vec3_t end, vec3_t origin) -{ - int spawnflags, modelnum; - vec3_t mins, maxs, mid, angles = {0, 0, 0}; - int num0, num1; - - modelnum = reach->facenum & 0x0000FFFF; - if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) - { - botimport.Print(PRT_MESSAGE, "BotFuncBobStartEnd: no entity with model %d\n", modelnum); - VectorSet(start, 0, 0, 0); - VectorSet(end, 0, 0, 0); - return; - } //end if - AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); - VectorAdd(mins, maxs, mid); - VectorScale(mid, 0.5, mid); - VectorCopy(mid, start); - VectorCopy(mid, end); - spawnflags = reach->facenum >> 16; - num0 = reach->edgenum >> 16; - if (num0 > 0x00007FFF) num0 |= 0xFFFF0000; - num1 = reach->edgenum & 0x0000FFFF; - if (num1 > 0x00007FFF) num1 |= 0xFFFF0000; - if (spawnflags & 1) - { - start[0] = num0; - end[0] = num1; - // - origin[0] += mid[0]; - origin[1] = mid[1]; - origin[2] = mid[2]; - } //end if - else if (spawnflags & 2) - { - start[1] = num0; - end[1] = num1; - // - origin[0] = mid[0]; - origin[1] += mid[1]; - origin[2] = mid[2]; - } //end else if - else - { - start[2] = num0; - end[2] = num1; - // - origin[0] = mid[0]; - origin[1] = mid[1]; - origin[2] += mid[2]; - } //end else -} //end of the function BotFuncBobStartEnd -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t dir, dir1, dir2, hordir, bottomcenter, bob_start, bob_end, bob_origin; - float dist, dist1, dist2, speed; - bot_moveresult_t result; - - BotClearMoveResult(&result); - // - BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin); - //if standing ontop of the func_bobbing - if (BotOnMover(ms->origin, ms->entitynum, reach)) - { -#ifdef DEBUG_FUNCBOB - botimport.Print(PRT_MESSAGE, "bot on func_bobbing\n"); -#endif - //if near end point of reachability - VectorSubtract(bob_origin, bob_end, dir); - if (VectorLength(dir) < 24) - { -#ifdef DEBUG_FUNCBOB - botimport.Print(PRT_MESSAGE, "bot moving to reachability end\n"); -#endif - //move to the end point - VectorSubtract(reach->end, ms->origin, hordir); - hordir[2] = 0; - VectorNormalize(hordir); - if (!BotCheckBarrierJump(ms, hordir, 100)) - { - EA_Move(ms->client, hordir, 400); - } //end if - VectorCopy(hordir, result.movedir); - } //end else - //if not really close to the center of the elevator - else - { - MoverBottomCenter(reach, bottomcenter); - VectorSubtract(bottomcenter, ms->origin, hordir); - hordir[2] = 0; - dist = VectorNormalize(hordir); - // - if (dist > 10) - { -#ifdef DEBUG_FUNCBOB - botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n"); -#endif - //move to the center of the plat - if (dist > 100) dist = 100; - speed = 400 - (400 - 4 * dist); - // - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - } //end if - } //end else - } //end if - else - { -#ifdef DEBUG_FUNCBOB - botimport.Print(PRT_MESSAGE, "bot not ontop of func_bobbing\n"); -#endif - //if very near the reachability end - VectorSubtract(reach->end, ms->origin, dir); - dist = VectorLength(dir); - if (dist < 64) - { -#ifdef DEBUG_FUNCBOB - botimport.Print(PRT_MESSAGE, "bot moving to end\n"); -#endif - if (dist > 60) dist = 60; - speed = 360 - (360 - 6 * dist); - //if swimming or no barrier jump - if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50)) - { - if (speed > 5) EA_Move(ms->client, dir, speed); - } //end if - VectorCopy(dir, result.movedir); - // - if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; - //stop using this reachability - ms->reachability_time = 0; - return result; - } //end if - //get direction and distance to reachability start - VectorSubtract(reach->start, ms->origin, dir1); - if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0; - dist1 = VectorNormalize(dir1); - //if func_bobbing is Not it's start position - VectorSubtract(bob_origin, bob_start, dir); - if (VectorLength(dir) > 16) - { -#ifdef DEBUG_FUNCBOB - botimport.Print(PRT_MESSAGE, "func_bobbing not at start\n"); -#endif - dist = dist1; - VectorCopy(dir1, dir); - // - BotCheckBlocked(ms, dir, qfalse, &result); - // - if (dist > 60) dist = 60; - speed = 360 - (360 - 6 * dist); - // - if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) - { - if (speed > 5) EA_Move(ms->client, dir, speed); - } //end if - VectorCopy(dir, result.movedir); - // - if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; - //this isn't a failure... just wait till the func_bobbing arrives - result.type = RESULTTYPE_WAITFORFUNCBOBBING; - result.flags |= MOVERESULT_WAITING; - return result; - } //end if - //get direction and distance to func_bob bottom center - MoverBottomCenter(reach, bottomcenter); - VectorSubtract(bottomcenter, ms->origin, dir2); - if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0; - dist2 = VectorNormalize(dir2); - //if very close to the reachability start or - //closer to the elevator center or - //between reachability start and func_bobbing center - if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0) - { -#ifdef DEBUG_FUNCBOB - botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n"); -#endif - dist = dist2; - VectorCopy(dir2, dir); - } //end if - else //closer to the reachability start - { -#ifdef DEBUG_FUNCBOB - botimport.Print(PRT_MESSAGE, "bot moving to reachability start\n"); -#endif - dist = dist1; - VectorCopy(dir1, dir); - } //end else - // - BotCheckBlocked(ms, dir, qfalse, &result); - // - if (dist > 60) dist = 60; - speed = 400 - (400 - 6 * dist); - // - if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) - { - EA_Move(ms->client, dir, speed); - } //end if - VectorCopy(dir, result.movedir); - // - if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; - } //end else - return result; -} //end of the function BotTravel_FuncBobbing -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotFinishTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t bob_origin, bob_start, bob_end, dir, hordir, bottomcenter; - bot_moveresult_t result; - float dist, speed; - - BotClearMoveResult(&result); - // - BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin); - // - VectorSubtract(bob_origin, bob_end, dir); - dist = VectorLength(dir); - //if the func_bobbing is near the end - if (dist < 16) - { - VectorSubtract(reach->end, ms->origin, hordir); - if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; - dist = VectorNormalize(hordir); - // - if (dist > 60) dist = 60; - speed = 360 - (360 - 6 * dist); - // - if (speed > 5) EA_Move(ms->client, dir, speed); - VectorCopy(dir, result.movedir); - // - if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; - } //end if - else - { - MoverBottomCenter(reach, bottomcenter); - VectorSubtract(bottomcenter, ms->origin, hordir); - if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; - dist = VectorNormalize(hordir); - // - if (dist > 5) - { - //move to the center of the plat - if (dist > 100) dist = 100; - speed = 400 - (400 - 4 * dist); - // - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - } //end if - } //end else - return result; -} //end of the function BotFinishTravel_FuncBobbing -//=========================================================================== -// 0 no valid grapple hook visible -// 1 the grapple hook is still flying -// 2 the grapple hooked into a wall -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int GrappleState(bot_movestate_t *ms, aas_reachability_t *reach) -{ - int i; - aas_entityinfo_t entinfo; - - //if the grapple hook is pulling - if (ms->moveflags & MFL_GRAPPLEPULL) - return 2; - //check for a visible grapple missile entity - //or visible grapple entity - for (i = AAS_NextEntity(0); i; i = AAS_NextEntity(i)) - { - if (AAS_EntityType(i) == (int) entitytypemissile->value) - { - AAS_EntityInfo(i, &entinfo); - if (entinfo.weapon == (int) weapindex_grapple->value) - { - return 1; - } //end if - } //end if - } //end for - //no valid grapple at all - return 0; -} //end of the function GrappleState -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotResetGrapple(bot_movestate_t *ms) -{ - aas_reachability_t reach; - - AAS_ReachabilityFromNum(ms->lastreachnum, &reach); - //if not using the grapple hook reachability anymore - if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_GRAPPLEHOOK) - { - if ((ms->moveflags & MFL_ACTIVEGRAPPLE) || ms->grapplevisible_time) - { - if (offhandgrapple->value) - EA_Command(ms->client, cmd_grappleoff->string); - ms->moveflags &= ~MFL_ACTIVEGRAPPLE; - ms->grapplevisible_time = 0; -#ifdef DEBUG_GRAPPLE - botimport.Print(PRT_MESSAGE, "reset grapple\n"); -#endif //DEBUG_GRAPPLE - } //end if - } //end if -} //end of the function BotResetGrapple -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_Grapple(bot_movestate_t *ms, aas_reachability_t *reach) -{ - bot_moveresult_t result; - float dist, speed; - vec3_t dir, viewdir, org; - int state, areanum; - bsp_trace_t trace; - -#ifdef DEBUG_GRAPPLE - static int debugline; - if (!debugline) debugline = botimport.DebugLineCreate(); - botimport.DebugLineShow(debugline, reach->start, reach->end, LINECOLOR_BLUE); -#endif //DEBUG_GRAPPLE - - BotClearMoveResult(&result); - // - if (ms->moveflags & MFL_GRAPPLERESET) - { - if (offhandgrapple->value) - EA_Command(ms->client, cmd_grappleoff->string); - ms->moveflags &= ~MFL_ACTIVEGRAPPLE; - return result; - } //end if - // - if (!(int) offhandgrapple->value) - { - result.weapon = weapindex_grapple->value; - result.flags |= MOVERESULT_MOVEMENTWEAPON; - } //end if - // - if (ms->moveflags & MFL_ACTIVEGRAPPLE) - { -#ifdef DEBUG_GRAPPLE - botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: active grapple\n"); -#endif //DEBUG_GRAPPLE - // - state = GrappleState(ms, reach); - // - VectorSubtract(reach->end, ms->origin, dir); - dir[2] = 0; - dist = VectorLength(dir); - //if very close to the grapple end or the grappled is hooked and - //the bot doesn't get any closer - if (state && dist < 48) - { - if (ms->lastgrappledist - dist < 1) - { -#ifdef DEBUG_GRAPPLE - botimport.Print(PRT_ERROR, "grapple normal end\n"); -#endif //DEBUG_GRAPPLE - if (offhandgrapple->value) - EA_Command(ms->client, cmd_grappleoff->string); - ms->moveflags &= ~MFL_ACTIVEGRAPPLE; - ms->moveflags |= MFL_GRAPPLERESET; - ms->reachability_time = 0; //end the reachability - return result; - } //end if - } //end if - //if no valid grapple at all, or the grapple hooked and the bot - //isn't moving anymore - else if (!state || (state == 2 && dist > ms->lastgrappledist - 2)) - { - if (ms->grapplevisible_time < AAS_Time() - 0.4) - { -#ifdef DEBUG_GRAPPLE - botimport.Print(PRT_ERROR, "grapple not visible\n"); -#endif //DEBUG_GRAPPLE - if (offhandgrapple->value) - EA_Command(ms->client, cmd_grappleoff->string); - ms->moveflags &= ~MFL_ACTIVEGRAPPLE; - ms->moveflags |= MFL_GRAPPLERESET; - ms->reachability_time = 0; //end the reachability - return result; - } //end if - } //end if - else - { - ms->grapplevisible_time = AAS_Time(); - } //end else - // - if (!(int) offhandgrapple->value) - { - EA_Attack(ms->client); - } //end if - //remember the current grapple distance - ms->lastgrappledist = dist; - } //end if - else - { -#ifdef DEBUG_GRAPPLE - botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: inactive grapple\n"); -#endif //DEBUG_GRAPPLE - // - ms->grapplevisible_time = AAS_Time(); - // - VectorSubtract(reach->start, ms->origin, dir); - if (!(ms->moveflags & MFL_SWIMMING)) dir[2] = 0; - VectorAdd(ms->origin, ms->viewoffset, org); - VectorSubtract(reach->end, org, viewdir); - // - dist = VectorNormalize(dir); - Vector2Angles(viewdir, result.ideal_viewangles); - result.flags |= MOVERESULT_MOVEMENTVIEW; - // - if (dist < 5 && - fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 2 && - fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 2) - { -#ifdef DEBUG_GRAPPLE - botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: activating grapple\n"); -#endif //DEBUG_GRAPPLE - //check if the grapple missile path is clear - VectorAdd(ms->origin, ms->viewoffset, org); - trace = AAS_Trace(org, NULL, NULL, reach->end, ms->entitynum, CONTENTS_SOLID); - VectorSubtract(reach->end, trace.endpos, dir); - if (VectorLength(dir) > 16) - { - result.failure = qtrue; - return result; - } //end if - //activate the grapple - if (offhandgrapple->value) - { - EA_Command(ms->client, cmd_grappleon->string); - } //end if - else - { - EA_Attack(ms->client); - } //end else - ms->moveflags |= MFL_ACTIVEGRAPPLE; - ms->lastgrappledist = 999999; - } //end if - else - { - if (dist < 70) speed = 300 - (300 - 4 * dist); - else speed = 400; - // - BotCheckBlocked(ms, dir, qtrue, &result); - //elemantary action move in direction - EA_Move(ms->client, dir, speed); - VectorCopy(dir, result.movedir); - } //end else - //if in another area before actually grappling - areanum = AAS_PointAreaNum(ms->origin); - if (areanum && areanum != ms->reachareanum) ms->reachability_time = 0; - } //end else - return result; -} //end of the function BotTravel_Grapple -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_RocketJump(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t hordir; - float dist, speed; - bot_moveresult_t result; - - //botimport.Print(PRT_MESSAGE, "BotTravel_RocketJump: bah\n"); - BotClearMoveResult(&result); - // - hordir[0] = reach->start[0] - ms->origin[0]; - hordir[1] = reach->start[1] - ms->origin[1]; - hordir[2] = 0; - // - dist = VectorNormalize(hordir); - //look in the movement direction - Vector2Angles(hordir, result.ideal_viewangles); - //look straight down - result.ideal_viewangles[PITCH] = 90; - // - if (dist < 5 && - fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 && - fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5) - { - //botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - VectorNormalize(hordir); - //elemantary action jump - EA_Jump(ms->client); - EA_Attack(ms->client); - EA_Move(ms->client, hordir, 800); - // - ms->jumpreach = ms->lastreachnum; - } //end if - else - { - if (dist > 80) dist = 80; - speed = 400 - (400 - 5 * dist); - EA_Move(ms->client, hordir, speed); - } //end else - //look in the movement direction - Vector2Angles(hordir, result.ideal_viewangles); - //look straight down - result.ideal_viewangles[PITCH] = 90; - //set the view angles directly - EA_View(ms->client, result.ideal_viewangles); - //view is important for the movment - result.flags |= MOVERESULT_MOVEMENTVIEWSET; - //select the rocket launcher - EA_SelectWeapon(ms->client, (int) weapindex_rocketlauncher->value); - //weapon is used for movement - result.weapon = (int) weapindex_rocketlauncher->value; - result.flags |= MOVERESULT_MOVEMENTWEAPON; - // - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotTravel_RocketJump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_BFGJump(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t hordir; - float dist, speed; - bot_moveresult_t result; - - //botimport.Print(PRT_MESSAGE, "BotTravel_BFGJump: bah\n"); - BotClearMoveResult(&result); - // - hordir[0] = reach->start[0] - ms->origin[0]; - hordir[1] = reach->start[1] - ms->origin[1]; - hordir[2] = 0; - // - dist = VectorNormalize(hordir); - // - if (dist < 5 && - fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 && - fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5) - { - //botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - VectorNormalize(hordir); - //elemantary action jump - EA_Jump(ms->client); - EA_Attack(ms->client); - EA_Move(ms->client, hordir, 800); - // - ms->jumpreach = ms->lastreachnum; - } //end if - else - { - if (dist > 80) dist = 80; - speed = 400 - (400 - 5 * dist); - EA_Move(ms->client, hordir, speed); - } //end else - //look in the movement direction - Vector2Angles(hordir, result.ideal_viewangles); - //look straight down - result.ideal_viewangles[PITCH] = 90; - //set the view angles directly - EA_View(ms->client, result.ideal_viewangles); - //view is important for the movment - result.flags |= MOVERESULT_MOVEMENTVIEWSET; - //select the rocket launcher - EA_SelectWeapon(ms->client, (int) weapindex_bfg10k->value); - //weapon is used for movement - result.weapon = (int) weapindex_bfg10k->value; - result.flags |= MOVERESULT_MOVEMENTWEAPON; - // - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotTravel_BFGJump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotFinishTravel_WeaponJump(bot_movestate_t *ms, aas_reachability_t *reach) -{ - vec3_t hordir; - float speed; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //if not jumped yet - if (!ms->jumpreach) return result; - /* - //go straight to the reachability end - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - VectorNormalize(hordir); - //always use max speed when traveling through the air - EA_Move(ms->client, hordir, 800); - VectorCopy(hordir, result.movedir); - */ - // - if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed)) - { - //go straight to the reachability end - VectorSubtract(reach->end, ms->origin, hordir); - hordir[2] = 0; - VectorNormalize(hordir); - speed = 400; - } //end if - // - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotFinishTravel_WeaponJump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach) -{ - float dist, speed; - vec3_t hordir; - bot_moveresult_t result; - - BotClearMoveResult(&result); - //first walk straight to the reachability start - hordir[0] = reach->start[0] - ms->origin[0]; - hordir[1] = reach->start[1] - ms->origin[1]; - hordir[2] = 0; - dist = VectorNormalize(hordir); - // - BotCheckBlocked(ms, hordir, qtrue, &result); - speed = 400; - //elemantary action move in direction - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotTravel_JumpPad -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotFinishTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach) -{ - float speed; - vec3_t hordir; - bot_moveresult_t result; - - BotClearMoveResult(&result); - if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed)) - { - hordir[0] = reach->end[0] - ms->origin[0]; - hordir[1] = reach->end[1] - ms->origin[1]; - hordir[2] = 0; - VectorNormalize(hordir); - speed = 400; - } //end if - BotCheckBlocked(ms, hordir, qtrue, &result); - //elemantary action move in direction - EA_Move(ms->client, hordir, speed); - VectorCopy(hordir, result.movedir); - // - return result; -} //end of the function BotFinishTravel_JumpPad -//=========================================================================== -// time before the reachability times out -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotReachabilityTime(aas_reachability_t *reach) -{ - switch(reach->traveltype & TRAVELTYPE_MASK) - { - case TRAVEL_WALK: return 5; - case TRAVEL_CROUCH: return 5; - case TRAVEL_BARRIERJUMP: return 5; - case TRAVEL_LADDER: return 6; - case TRAVEL_WALKOFFLEDGE: return 5; - case TRAVEL_JUMP: return 5; - case TRAVEL_SWIM: return 5; - case TRAVEL_WATERJUMP: return 5; - case TRAVEL_TELEPORT: return 5; - case TRAVEL_ELEVATOR: return 10; - case TRAVEL_GRAPPLEHOOK: return 8; - case TRAVEL_ROCKETJUMP: return 6; - case TRAVEL_BFGJUMP: return 6; - case TRAVEL_JUMPPAD: return 10; - case TRAVEL_FUNCBOB: return 10; - default: - { - botimport.Print(PRT_ERROR, "travel type %d not implemented yet\n", reach->traveltype); - return 8; - } //end case - } //end switch -} //end of the function BotReachabilityTime -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bot_moveresult_t BotMoveInGoalArea(bot_movestate_t *ms, bot_goal_t *goal) -{ - bot_moveresult_t result; - vec3_t dir; - float dist, speed; - -#ifdef DEBUG - //botimport.Print(PRT_MESSAGE, "%s: moving straight to goal\n", ClientName(ms->entitynum-1)); - //AAS_ClearShownDebugLines(); - //AAS_DebugLine(ms->origin, goal->origin, LINECOLOR_RED); -#endif //DEBUG - BotClearMoveResult(&result); - //walk straight to the goal origin - dir[0] = goal->origin[0] - ms->origin[0]; - dir[1] = goal->origin[1] - ms->origin[1]; - if (ms->moveflags & MFL_SWIMMING) - { - dir[2] = goal->origin[2] - ms->origin[2]; - result.traveltype = TRAVEL_SWIM; - } //end if - else - { - dir[2] = 0; - result.traveltype = TRAVEL_WALK; - } //endif - // - dist = VectorNormalize(dir); - if (dist > 100) dist = 100; - speed = 400 - (400 - 4 * dist); - if (speed < 10) speed = 0; - // - BotCheckBlocked(ms, dir, qtrue, &result); - //elemantary action move in direction - EA_Move(ms->client, dir, speed); - VectorCopy(dir, result.movedir); - // - if (ms->moveflags & MFL_SWIMMING) - { - Vector2Angles(dir, result.ideal_viewangles); - result.flags |= MOVERESULT_SWIMVIEW; - } //end if - //if (!debugline) debugline = botimport.DebugLineCreate(); - //botimport.DebugLineShow(debugline, ms->origin, goal->origin, LINECOLOR_BLUE); - // - ms->lastreachnum = 0; - ms->lastareanum = 0; - ms->lastgoalareanum = goal->areanum; - VectorCopy(ms->origin, ms->lastorigin); - // - return result; -} //end of the function BotMoveInGoalArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags) -{ - int reachnum, lastreachnum, foundjumppad, ent, resultflags; - aas_reachability_t reach, lastreach; - bot_movestate_t *ms; - //vec3_t mins, maxs, up = {0, 0, 1}; - //bsp_trace_t trace; - //static int debugline; - - - BotClearMoveResult(result); - // - ms = BotMoveStateFromHandle(movestate); - if (!ms) return; - //reset the grapple before testing if the bot has a valid goal - //because the bot could loose all it's goals when stuck to a wall - BotResetGrapple(ms); - // - if (!goal) - { -#ifdef DEBUG - botimport.Print(PRT_MESSAGE, "client %d: movetogoal -> no goal\n", ms->client); -#endif //DEBUG - result->failure = qtrue; - return; - } //end if - //botimport.Print(PRT_MESSAGE, "numavoidreach = %d\n", ms->numavoidreach); - //remove some of the move flags - ms->moveflags &= ~(MFL_SWIMMING|MFL_AGAINSTLADDER); - //set some of the move flags - //NOTE: the MFL_ONGROUND flag is also set in the higher AI - if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND; - // - if (ms->moveflags & MFL_ONGROUND) - { - int modeltype, modelnum; - - ent = BotOnTopOfEntity(ms); - - if (ent != -1) - { - modelnum = AAS_EntityModelindex(ent); - if (modelnum >= 0 && modelnum < MAX_MODELS) - { - modeltype = modeltypes[modelnum]; - - if (modeltype == MODELTYPE_FUNC_PLAT) - { - AAS_ReachabilityFromNum(ms->lastreachnum, &reach); - //if the bot is Not using the elevator - if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR || - //NOTE: the face number is the plat model number - (reach.facenum & 0x0000FFFF) != modelnum) - { - reachnum = AAS_NextModelReachability(0, modelnum); - if (reachnum) - { - //botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_plat\n", ms->client); - AAS_ReachabilityFromNum(reachnum, &reach); - ms->lastreachnum = reachnum; - ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); - } //end if - else - { - if (bot_developer) - { - botimport.Print(PRT_MESSAGE, "client %d: on func_plat without reachability\n", ms->client); - } //end if - result->blocked = qtrue; - result->blockentity = ent; - result->flags |= MOVERESULT_ONTOPOFOBSTACLE; - return; - } //end else - } //end if - result->flags |= MOVERESULT_ONTOPOF_ELEVATOR; - } //end if - else if (modeltype == MODELTYPE_FUNC_BOB) - { - AAS_ReachabilityFromNum(ms->lastreachnum, &reach); - //if the bot is Not using the func bobbing - if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB || - //NOTE: the face number is the func_bobbing model number - (reach.facenum & 0x0000FFFF) != modelnum) - { - reachnum = AAS_NextModelReachability(0, modelnum); - if (reachnum) - { - //botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_bobbing\n", ms->client); - AAS_ReachabilityFromNum(reachnum, &reach); - ms->lastreachnum = reachnum; - ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); - } //end if - else - { - if (bot_developer) - { - botimport.Print(PRT_MESSAGE, "client %d: on func_bobbing without reachability\n", ms->client); - } //end if - result->blocked = qtrue; - result->blockentity = ent; - result->flags |= MOVERESULT_ONTOPOFOBSTACLE; - return; - } //end else - } //end if - result->flags |= MOVERESULT_ONTOPOF_FUNCBOB; - } //end if - else if (modeltype == MODELTYPE_FUNC_STATIC || modeltype == MODELTYPE_FUNC_DOOR) - { - // check if ontop of a door bridge ? - ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); - // if not in a reachability area - if (!AAS_AreaReachability(ms->areanum)) - { - result->blocked = qtrue; - result->blockentity = ent; - result->flags |= MOVERESULT_ONTOPOFOBSTACLE; - return; - } //end if - } //end else if - else - { - result->blocked = qtrue; - result->blockentity = ent; - result->flags |= MOVERESULT_ONTOPOFOBSTACLE; - return; - } //end else - } //end if - } //end if - } //end if - //if swimming - if (AAS_Swimming(ms->origin)) ms->moveflags |= MFL_SWIMMING; - //if against a ladder - if (AAS_AgainstLadder(ms->origin)) ms->moveflags |= MFL_AGAINSTLADDER; - //if the bot is on the ground, swimming or against a ladder - if (ms->moveflags & (MFL_ONGROUND|MFL_SWIMMING|MFL_AGAINSTLADDER)) - { - //botimport.Print(PRT_MESSAGE, "%s: onground, swimming or against ladder\n", ClientName(ms->entitynum-1)); - // - AAS_ReachabilityFromNum(ms->lastreachnum, &lastreach); - //reachability area the bot is in - //ms->areanum = BotReachabilityArea(ms->origin, ((lastreach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR)); - ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); - // - if ( !ms->areanum ) - { - result->failure = qtrue; - result->blocked = qtrue; - result->blockentity = 0; - result->type = RESULTTYPE_INSOLIDAREA; - return; - } //end if - //if the bot is in the goal area - if (ms->areanum == goal->areanum) - { - *result = BotMoveInGoalArea(ms, goal); - return; - } //end if - //assume we can use the reachability from the last frame - reachnum = ms->lastreachnum; - //if there is a last reachability - if (reachnum) - { - AAS_ReachabilityFromNum(reachnum, &reach); - //check if the reachability is still valid - if (!(AAS_TravelFlagForType(reach.traveltype) & travelflags)) - { - reachnum = 0; - } //end if - //special grapple hook case - else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_GRAPPLEHOOK) - { - if (ms->reachability_time < AAS_Time() || - (ms->moveflags & MFL_GRAPPLERESET)) - { - reachnum = 0; - } //end if - } //end if - //special elevator case - else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR || - (reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) - { - if ((result->flags & MOVERESULT_ONTOPOF_FUNCBOB) || - (result->flags & MOVERESULT_ONTOPOF_FUNCBOB)) - { - ms->reachability_time = AAS_Time() + 5; - } //end if - //if the bot was going for an elevator and reached the reachability area - if (ms->areanum == reach.areanum || - ms->reachability_time < AAS_Time()) - { - reachnum = 0; - } //end if - } //end if - else - { -#ifdef DEBUG - if (bot_developer) - { - if (ms->reachability_time < AAS_Time()) - { - botimport.Print(PRT_MESSAGE, "client %d: reachability timeout in ", ms->client); - AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); - botimport.Print(PRT_MESSAGE, "\n"); - } //end if - /* - if (ms->lastareanum != ms->areanum) - { - botimport.Print(PRT_MESSAGE, "changed from area %d to %d\n", ms->lastareanum, ms->areanum); - } //end if*/ - } //end if -#endif //DEBUG - //if the goal area changed or the reachability timed out - //or the area changed - if (ms->lastgoalareanum != goal->areanum || - ms->reachability_time < AAS_Time() || - ms->lastareanum != ms->areanum) - { - reachnum = 0; - //botimport.Print(PRT_MESSAGE, "area change or timeout\n"); - } //end else if - } //end else - } //end if - resultflags = 0; - //if the bot needs a new reachability - if (!reachnum) - { - //if the area has no reachability links - if (!AAS_AreaReachability(ms->areanum)) - { -#ifdef DEBUG - if (bot_developer) - { - botimport.Print(PRT_MESSAGE, "area %d no reachability\n", ms->areanum); - } //end if -#endif //DEBUG - } //end if - //get a new reachability leading towards the goal - reachnum = BotGetReachabilityToGoal(ms->origin, ms->areanum, - ms->lastgoalareanum, ms->lastareanum, - ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, - goal, travelflags, travelflags, - ms->avoidspots, ms->numavoidspots, &resultflags); - //the area number the reachability starts in - ms->reachareanum = ms->areanum; - //reset some state variables - ms->jumpreach = 0; //for TRAVEL_JUMP - ms->moveflags &= ~MFL_GRAPPLERESET; //for TRAVEL_GRAPPLEHOOK - //if there is a reachability to the goal - if (reachnum) - { - AAS_ReachabilityFromNum(reachnum, &reach); - //set a timeout for this reachability - ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); - // -#ifdef AVOIDREACH - //add the reachability to the reachabilities to avoid for a while - BotAddToAvoidReach(ms, reachnum, AVOIDREACH_TIME); -#endif //AVOIDREACH - } //end if -#ifdef DEBUG - - else if (bot_developer) - { - botimport.Print(PRT_MESSAGE, "goal not reachable\n"); - Com_Memset(&reach, 0, sizeof(aas_reachability_t)); //make compiler happy - } //end else - if (bot_developer) - { - //if still going for the same goal - if (ms->lastgoalareanum == goal->areanum) - { - if (ms->lastareanum == reach.areanum) - { - botimport.Print(PRT_MESSAGE, "same goal, going back to previous area\n"); - } //end if - } //end if - } //end if -#endif //DEBUG - } //end else - // - ms->lastreachnum = reachnum; - ms->lastgoalareanum = goal->areanum; - ms->lastareanum = ms->areanum; - //if the bot has a reachability - if (reachnum) - { - //get the reachability from the number - AAS_ReachabilityFromNum(reachnum, &reach); - result->traveltype = reach.traveltype; - // -#ifdef DEBUG_AI_MOVE - AAS_ClearShownDebugLines(); - AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); - AAS_ShowReachability(&reach); -#endif //DEBUG_AI_MOVE - // -#ifdef DEBUG - //botimport.Print(PRT_MESSAGE, "client %d: ", ms->client); - //AAS_PrintTravelType(reach.traveltype); - //botimport.Print(PRT_MESSAGE, "\n"); -#endif //DEBUG - switch(reach.traveltype & TRAVELTYPE_MASK) - { - case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break; - case TRAVEL_CROUCH: *result = BotTravel_Crouch(ms, &reach); break; - case TRAVEL_BARRIERJUMP: *result = BotTravel_BarrierJump(ms, &reach); break; - case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break; - case TRAVEL_WALKOFFLEDGE: *result = BotTravel_WalkOffLedge(ms, &reach); break; - case TRAVEL_JUMP: *result = BotTravel_Jump(ms, &reach); break; - case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break; - case TRAVEL_WATERJUMP: *result = BotTravel_WaterJump(ms, &reach); break; - case TRAVEL_TELEPORT: *result = BotTravel_Teleport(ms, &reach); break; - case TRAVEL_ELEVATOR: *result = BotTravel_Elevator(ms, &reach); break; - case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break; - case TRAVEL_ROCKETJUMP: *result = BotTravel_RocketJump(ms, &reach); break; - case TRAVEL_BFGJUMP: *result = BotTravel_BFGJump(ms, &reach); break; - case TRAVEL_JUMPPAD: *result = BotTravel_JumpPad(ms, &reach); break; - case TRAVEL_FUNCBOB: *result = BotTravel_FuncBobbing(ms, &reach); break; - default: - { - botimport.Print(PRT_FATAL, "travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK)); - break; - } //end case - } //end switch - result->traveltype = reach.traveltype; - result->flags |= resultflags; - } //end if - else - { - result->failure = qtrue; - result->flags |= resultflags; - Com_Memset(&reach, 0, sizeof(aas_reachability_t)); - } //end else -#ifdef DEBUG - if (bot_developer) - { - if (result->failure) - { - botimport.Print(PRT_MESSAGE, "client %d: movement failure in ", ms->client); - AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); - botimport.Print(PRT_MESSAGE, "\n"); - } //end if - } //end if -#endif //DEBUG - } //end if - else - { - int i, numareas, areas[16]; - vec3_t end; - - //special handling of jump pads when the bot uses a jump pad without knowing it - foundjumppad = qfalse; - VectorMA(ms->origin, -2 * ms->thinktime, ms->velocity, end); - numareas = AAS_TraceAreas(ms->origin, end, areas, NULL, 16); - for (i = numareas-1; i >= 0; i--) - { - if (AAS_AreaJumpPad(areas[i])) - { - //botimport.Print(PRT_MESSAGE, "client %d used a jumppad without knowing, area %d\n", ms->client, areas[i]); - foundjumppad = qtrue; - lastreachnum = BotGetReachabilityToGoal(end, areas[i], - ms->lastgoalareanum, ms->lastareanum, - ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, - goal, travelflags, TFL_JUMPPAD, ms->avoidspots, ms->numavoidspots, NULL); - if (lastreachnum) - { - ms->lastreachnum = lastreachnum; - ms->lastareanum = areas[i]; - //botimport.Print(PRT_MESSAGE, "found jumppad reachability\n"); - break; - } //end if - else - { - for (lastreachnum = AAS_NextAreaReachability(areas[i], 0); lastreachnum; - lastreachnum = AAS_NextAreaReachability(areas[i], lastreachnum)) - { - //get the reachability from the number - AAS_ReachabilityFromNum(lastreachnum, &reach); - if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) - { - ms->lastreachnum = lastreachnum; - ms->lastareanum = areas[i]; - //botimport.Print(PRT_MESSAGE, "found jumppad reachability hard!!\n"); - break; - } //end if - } //end for - if (lastreachnum) break; - } //end else - } //end if - } //end for - if (bot_developer) - { - //if a jumppad is found with the trace but no reachability is found - if (foundjumppad && !ms->lastreachnum) - { - botimport.Print(PRT_MESSAGE, "client %d didn't find jumppad reachability\n", ms->client); - } //end if - } //end if - // - if (ms->lastreachnum) - { - //botimport.Print(PRT_MESSAGE, "%s: NOT onground, swimming or against ladder\n", ClientName(ms->entitynum-1)); - AAS_ReachabilityFromNum(ms->lastreachnum, &reach); - result->traveltype = reach.traveltype; -#ifdef DEBUG - //botimport.Print(PRT_MESSAGE, "client %d finish: ", ms->client); - //AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); - //botimport.Print(PRT_MESSAGE, "\n"); -#endif //DEBUG - // - switch(reach.traveltype & TRAVELTYPE_MASK) - { - case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;//BotFinishTravel_Walk(ms, &reach); break; - case TRAVEL_CROUCH: /*do nothing*/ break; - case TRAVEL_BARRIERJUMP: *result = BotFinishTravel_BarrierJump(ms, &reach); break; - case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break; - case TRAVEL_WALKOFFLEDGE: *result = BotFinishTravel_WalkOffLedge(ms, &reach); break; - case TRAVEL_JUMP: *result = BotFinishTravel_Jump(ms, &reach); break; - case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break; - case TRAVEL_WATERJUMP: *result = BotFinishTravel_WaterJump(ms, &reach); break; - case TRAVEL_TELEPORT: /*do nothing*/ break; - case TRAVEL_ELEVATOR: *result = BotFinishTravel_Elevator(ms, &reach); break; - case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break; - case TRAVEL_ROCKETJUMP: - case TRAVEL_BFGJUMP: *result = BotFinishTravel_WeaponJump(ms, &reach); break; - case TRAVEL_JUMPPAD: *result = BotFinishTravel_JumpPad(ms, &reach); break; - case TRAVEL_FUNCBOB: *result = BotFinishTravel_FuncBobbing(ms, &reach); break; - default: - { - botimport.Print(PRT_FATAL, "(last) travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK)); - break; - } //end case - } //end switch - result->traveltype = reach.traveltype; -#ifdef DEBUG - if (bot_developer) - { - if (result->failure) - { - botimport.Print(PRT_MESSAGE, "client %d: movement failure in finish ", ms->client); - AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); - botimport.Print(PRT_MESSAGE, "\n"); - } //end if - } //end if -#endif //DEBUG - } //end if - } //end else - //FIXME: is it right to do this here? - if (result->blocked) ms->reachability_time -= 10 * ms->thinktime; - //copy the last origin - VectorCopy(ms->origin, ms->lastorigin); - //return the movement result - return; -} //end of the function BotMoveToGoal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotResetAvoidReach(int movestate) -{ - bot_movestate_t *ms; - - ms = BotMoveStateFromHandle(movestate); - if (!ms) return; - Com_Memset(ms->avoidreach, 0, MAX_AVOIDREACH * sizeof(int)); - Com_Memset(ms->avoidreachtimes, 0, MAX_AVOIDREACH * sizeof(float)); - Com_Memset(ms->avoidreachtries, 0, MAX_AVOIDREACH * sizeof(int)); -} //end of the function BotResetAvoidReach -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotResetLastAvoidReach(int movestate) -{ - int i, latest; - float latesttime; - bot_movestate_t *ms; - - ms = BotMoveStateFromHandle(movestate); - if (!ms) return; - latesttime = 0; - latest = 0; - for (i = 0; i < MAX_AVOIDREACH; i++) - { - if (ms->avoidreachtimes[i] > latesttime) - { - latesttime = ms->avoidreachtimes[i]; - latest = i; - } //end if - } //end for - if (latesttime) - { - ms->avoidreachtimes[latest] = 0; - if (ms->avoidreachtries[i] > 0) ms->avoidreachtries[latest]--; - } //end if -} //end of the function BotResetLastAvoidReach -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotResetMoveState(int movestate) -{ - bot_movestate_t *ms; - - ms = BotMoveStateFromHandle(movestate); - if (!ms) return; - Com_Memset(ms, 0, sizeof(bot_movestate_t)); -} //end of the function BotResetMoveState -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotSetupMoveAI(void) -{ - BotSetBrushModelTypes(); - sv_maxstep = LibVar("sv_step", "18"); - sv_maxbarrier = LibVar("sv_maxbarrier", "32"); - sv_gravity = LibVar("sv_gravity", "800"); - weapindex_rocketlauncher = LibVar("weapindex_rocketlauncher", "5"); - weapindex_bfg10k = LibVar("weapindex_bfg10k", "9"); - weapindex_grapple = LibVar("weapindex_grapple", "10"); - entitytypemissile = LibVar("entitytypemissile", "3"); - offhandgrapple = LibVar("offhandgrapple", "0"); - cmd_grappleon = LibVar("cmd_grappleon", "grappleon"); - cmd_grappleoff = LibVar("cmd_grappleoff", "grappleoff"); - return BLERR_NOERROR; -} //end of the function BotSetupMoveAI -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotShutdownMoveAI(void) -{ - int i; - - for (i = 1; i <= MAX_CLIENTS; i++) - { - if (botmovestates[i]) - { - FreeMemory(botmovestates[i]); - botmovestates[i] = NULL; - } //end if - } //end for -} //end of the function BotShutdownMoveAI - - diff --git a/tools/quake3/bspc/deps/botlib/be_ai_move.h b/tools/quake3/bspc/deps/botlib/be_ai_move.h deleted file mode 100644 index a32d9397..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_move.h +++ /dev/null @@ -1,142 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// - -/***************************************************************************** - * name: be_ai_move.h - * - * desc: movement AI - * - * $Archive: /source/code/botlib/be_ai_move.h $ - * - *****************************************************************************/ - -//movement types -#define MOVE_WALK 1 -#define MOVE_CROUCH 2 -#define MOVE_JUMP 4 -#define MOVE_GRAPPLE 8 -#define MOVE_ROCKETJUMP 16 -#define MOVE_BFGJUMP 32 -//move flags -#define MFL_BARRIERJUMP 1 //bot is performing a barrier jump -#define MFL_ONGROUND 2 //bot is in the ground -#define MFL_SWIMMING 4 //bot is swimming -#define MFL_AGAINSTLADDER 8 //bot is against a ladder -#define MFL_WATERJUMP 16 //bot is waterjumping -#define MFL_TELEPORTED 32 //bot is being teleported -#define MFL_GRAPPLEPULL 64 //bot is being pulled by the grapple -#define MFL_ACTIVEGRAPPLE 128 //bot is using the grapple hook -#define MFL_GRAPPLERESET 256 //bot has reset the grapple -#define MFL_WALK 512 //bot should walk slowly -// move result flags -#define MOVERESULT_MOVEMENTVIEW 1 //bot uses view for movement -#define MOVERESULT_SWIMVIEW 2 //bot uses view for swimming -#define MOVERESULT_WAITING 4 //bot is waiting for something -#define MOVERESULT_MOVEMENTVIEWSET 8 //bot has set the view in movement code -#define MOVERESULT_MOVEMENTWEAPON 16 //bot uses weapon for movement -#define MOVERESULT_ONTOPOFOBSTACLE 32 //bot is ontop of obstacle -#define MOVERESULT_ONTOPOF_FUNCBOB 64 //bot is ontop of a func_bobbing -#define MOVERESULT_ONTOPOF_ELEVATOR 128 //bot is ontop of an elevator (func_plat) -#define MOVERESULT_BLOCKEDBYAVOIDSPOT 256 //bot is blocked by an avoid spot -// -#define MAX_AVOIDREACH 1 -#define MAX_AVOIDSPOTS 32 -// avoid spot types -#define AVOID_CLEAR 0 //clear all avoid spots -#define AVOID_ALWAYS 1 //avoid always -#define AVOID_DONTBLOCK 2 //never totally block -// restult types -#define RESULTTYPE_ELEVATORUP 1 //elevator is up -#define RESULTTYPE_WAITFORFUNCBOBBING 2 //waiting for func bobbing to arrive -#define RESULTTYPE_BADGRAPPLEPATH 4 //grapple path is obstructed -#define RESULTTYPE_INSOLIDAREA 8 //stuck in solid area, this is bad - -//structure used to initialize the movement state -//the or_moveflags MFL_ONGROUND, MFL_TELEPORTED and MFL_WATERJUMP come from the playerstate -typedef struct bot_initmove_s -{ - vec3_t origin; //origin of the bot - vec3_t velocity; //velocity of the bot - vec3_t viewoffset; //view offset - int entitynum; //entity number of the bot - int client; //client number of the bot - float thinktime; //time the bot thinks - int presencetype; //presencetype of the bot - vec3_t viewangles; //view angles of the bot - int or_moveflags; //values ored to the movement flags -} bot_initmove_t; - -//NOTE: the ideal_viewangles are only valid if MFL_MOVEMENTVIEW is set -typedef struct bot_moveresult_s -{ - int failure; //true if movement failed all together - int type; //failure or blocked type - int blocked; //true if blocked by an entity - int blockentity; //entity blocking the bot - int traveltype; //last executed travel type - int flags; //result flags - int weapon; //weapon used for movement - vec3_t movedir; //movement direction - vec3_t ideal_viewangles; //ideal viewangles for the movement -} bot_moveresult_t; - -#define bot_moveresult_t_cleared(x) bot_moveresult_t (x) = {0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0}} - -typedef struct bot_avoidspot_s -{ - vec3_t origin; - float radius; - int type; -} bot_avoidspot_t; - -//resets the whole move state -void BotResetMoveState(int movestate); -//moves the bot to the given goal -void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags); -//moves the bot in the specified direction using the specified type of movement -int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type); -//reset avoid reachability -void BotResetAvoidReach(int movestate); -//resets the last avoid reachability -void BotResetLastAvoidReach(int movestate); -//returns a reachability area if the origin is in one -int BotReachabilityArea(vec3_t origin, int client); -//view target based on movement -int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target); -//predict the position of a player based on movement towards a goal -int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target); -//returns the handle of a newly allocated movestate -int BotAllocMoveState(void); -//frees the movestate with the given handle -void BotFreeMoveState(int handle); -//initialize movement state before performing any movement -void BotInitMoveState(int handle, bot_initmove_t *initmove); -//add a spot to avoid (if type == AVOID_CLEAR all spots are removed) -void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type); -//must be called every map change -void BotSetBrushModelTypes(void); -//setup movement AI -int BotSetupMoveAI(void); -//shutdown movement AI -void BotShutdownMoveAI(void); - diff --git a/tools/quake3/bspc/deps/botlib/be_ai_weap.c b/tools/quake3/bspc/deps/botlib/be_ai_weap.c deleted file mode 100644 index 84639fdc..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_weap.c +++ /dev/null @@ -1,543 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_ai_weap.c - * - * desc: weapon AI - * - * $Archive: /MissionPack/code/botlib/be_ai_weap.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_libvar.h" -#include "l_log.h" -#include "l_memory.h" -#include "l_utils.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_ai_weight.h" //fuzzy weights -#include "be_ai_weap.h" - -//#define DEBUG_AI_WEAP - -//structure field offsets -#define WEAPON_OFS(x) (int)&(((weaponinfo_t *)0)->x) -#define PROJECTILE_OFS(x) (int)&(((projectileinfo_t *)0)->x) - -//weapon definition // bk001212 - static -static fielddef_t weaponinfo_fields[] = -{ -{"number", WEAPON_OFS(number), FT_INT}, //weapon number -{"name", WEAPON_OFS(name), FT_STRING}, //name of the weapon -{"level", WEAPON_OFS(level), FT_INT}, -{"model", WEAPON_OFS(model), FT_STRING}, //model of the weapon -{"weaponindex", WEAPON_OFS(weaponindex), FT_INT}, //index of weapon in inventory -{"flags", WEAPON_OFS(flags), FT_INT}, //special flags -{"projectile", WEAPON_OFS(projectile), FT_STRING}, //projectile used by the weapon -{"numprojectiles", WEAPON_OFS(numprojectiles), FT_INT}, //number of projectiles -{"hspread", WEAPON_OFS(hspread), FT_FLOAT}, //horizontal spread of projectiles (degrees from middle) -{"vspread", WEAPON_OFS(vspread), FT_FLOAT}, //vertical spread of projectiles (degrees from middle) -{"speed", WEAPON_OFS(speed), FT_FLOAT}, //speed of the projectile (0 = instant hit) -{"acceleration", WEAPON_OFS(acceleration), FT_FLOAT}, //"acceleration" * time (in seconds) + "speed" = projectile speed -{"recoil", WEAPON_OFS(recoil), FT_FLOAT|FT_ARRAY, 3}, //amount of recoil the player gets from the weapon -{"offset", WEAPON_OFS(offset), FT_FLOAT|FT_ARRAY, 3}, //projectile start offset relative to eye and view angles -{"angleoffset", WEAPON_OFS(angleoffset), FT_FLOAT|FT_ARRAY, 3},//offset of the shoot angles relative to the view angles -{"extrazvelocity", WEAPON_OFS(extrazvelocity), FT_FLOAT},//extra z velocity the projectile gets -{"ammoamount", WEAPON_OFS(ammoamount), FT_INT}, //ammo amount used per shot -{"ammoindex", WEAPON_OFS(ammoindex), FT_INT}, //index of ammo in inventory -{"activate", WEAPON_OFS(activate), FT_FLOAT}, //time it takes to select the weapon -{"reload", WEAPON_OFS(reload), FT_FLOAT}, //time it takes to reload the weapon -{"spinup", WEAPON_OFS(spinup), FT_FLOAT}, //time it takes before first shot -{"spindown", WEAPON_OFS(spindown), FT_FLOAT}, //time it takes before weapon stops firing -{NULL, 0, 0, 0} -}; - -//projectile definition -static fielddef_t projectileinfo_fields[] = -{ -{"name", PROJECTILE_OFS(name), FT_STRING}, //name of the projectile -{"model", WEAPON_OFS(model), FT_STRING}, //model of the projectile -{"flags", PROJECTILE_OFS(flags), FT_INT}, //special flags -{"gravity", PROJECTILE_OFS(gravity), FT_FLOAT}, //amount of gravity applied to the projectile [0,1] -{"damage", PROJECTILE_OFS(damage), FT_INT}, //damage of the projectile -{"radius", PROJECTILE_OFS(radius), FT_FLOAT}, //radius of damage -{"visdamage", PROJECTILE_OFS(visdamage), FT_INT}, //damage of the projectile to visible entities -{"damagetype", PROJECTILE_OFS(damagetype), FT_INT}, //type of damage (combination of the DAMAGETYPE_? flags) -{"healthinc", PROJECTILE_OFS(healthinc), FT_INT}, //health increase the owner gets -{"push", PROJECTILE_OFS(push), FT_FLOAT}, //amount a player is pushed away from the projectile impact -{"detonation", PROJECTILE_OFS(detonation), FT_FLOAT}, //time before projectile explodes after fire pressed -{"bounce", PROJECTILE_OFS(bounce), FT_FLOAT}, //amount the projectile bounces -{"bouncefric", PROJECTILE_OFS(bouncefric), FT_FLOAT}, //amount the bounce decreases per bounce -{"bouncestop", PROJECTILE_OFS(bouncestop), FT_FLOAT}, //minimum bounce value before bouncing stops -//recurive projectile definition?? -{NULL, 0, 0, 0} -}; - -static structdef_t weaponinfo_struct = -{ - sizeof(weaponinfo_t), weaponinfo_fields -}; -static structdef_t projectileinfo_struct = -{ - sizeof(projectileinfo_t), projectileinfo_fields -}; - -//weapon configuration: set of weapons with projectiles -typedef struct weaponconfig_s -{ - int numweapons; - int numprojectiles; - projectileinfo_t *projectileinfo; - weaponinfo_t *weaponinfo; -} weaponconfig_t; - -//the bot weapon state -typedef struct bot_weaponstate_s -{ - struct weightconfig_s *weaponweightconfig; //weapon weight configuration - int *weaponweightindex; //weapon weight index -} bot_weaponstate_t; - -static bot_weaponstate_t *botweaponstates[MAX_CLIENTS+1]; -static weaponconfig_t *weaponconfig; - -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -int BotValidWeaponNumber(int weaponnum) -{ - if (weaponnum <= 0 || weaponnum > weaponconfig->numweapons) - { - botimport.Print(PRT_ERROR, "weapon number out of range\n"); - return qfalse; - } //end if - return qtrue; -} //end of the function BotValidWeaponNumber -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -bot_weaponstate_t *BotWeaponStateFromHandle(int handle) -{ - if (handle <= 0 || handle > MAX_CLIENTS) - { - botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); - return NULL; - } //end if - if (!botweaponstates[handle]) - { - botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); - return NULL; - } //end if - return botweaponstates[handle]; -} //end of the function BotWeaponStateFromHandle -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef DEBUG_AI_WEAP -void DumpWeaponConfig(weaponconfig_t *wc) -{ - FILE *fp; - int i; - - fp = Log_FileStruct(); - if (!fp) return; - for (i = 0; i < wc->numprojectiles; i++) - { - WriteStructure(fp, &projectileinfo_struct, (char *) &wc->projectileinfo[i]); - Log_Flush(); - } //end for - for (i = 0; i < wc->numweapons; i++) - { - WriteStructure(fp, &weaponinfo_struct, (char *) &wc->weaponinfo[i]); - Log_Flush(); - } //end for -} //end of the function DumpWeaponConfig -#endif //DEBUG_AI_WEAP -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -weaponconfig_t *LoadWeaponConfig(char *filename) -{ - int max_weaponinfo, max_projectileinfo; - token_t token; - char path[MAX_PATH]; - int i, j; - source_t *source; - weaponconfig_t *wc; - weaponinfo_t weaponinfo; - - max_weaponinfo = (int) LibVarValue("max_weaponinfo", "32"); - if (max_weaponinfo < 0) - { - botimport.Print(PRT_ERROR, "max_weaponinfo = %d\n", max_weaponinfo); - max_weaponinfo = 32; - LibVarSet("max_weaponinfo", "32"); - } //end if - max_projectileinfo = (int) LibVarValue("max_projectileinfo", "32"); - if (max_projectileinfo < 0) - { - botimport.Print(PRT_ERROR, "max_projectileinfo = %d\n", max_projectileinfo); - max_projectileinfo = 32; - LibVarSet("max_projectileinfo", "32"); - } //end if - strncpy(path, filename, MAX_PATH); - PC_SetBaseFolder(BOTFILESBASEFOLDER); - source = LoadSourceFile(path); - if (!source) - { - botimport.Print(PRT_ERROR, "counldn't load %s\n", path); - return NULL; - } //end if - //initialize weapon config - wc = (weaponconfig_t *) GetClearedHunkMemory(sizeof(weaponconfig_t) + - max_weaponinfo * sizeof(weaponinfo_t) + - max_projectileinfo * sizeof(projectileinfo_t)); - wc->weaponinfo = (weaponinfo_t *) ((char *) wc + sizeof(weaponconfig_t)); - wc->projectileinfo = (projectileinfo_t *) ((char *) wc->weaponinfo + - max_weaponinfo * sizeof(weaponinfo_t)); - wc->numweapons = max_weaponinfo; - wc->numprojectiles = 0; - //parse the source file - while(PC_ReadToken(source, &token)) - { - if (!strcmp(token.string, "weaponinfo")) - { - Com_Memset(&weaponinfo, 0, sizeof(weaponinfo_t)); - if (!ReadStructure(source, &weaponinfo_struct, (char *) &weaponinfo)) - { - FreeMemory(wc); - FreeSource(source); - return NULL; - } //end if - if (weaponinfo.number < 0 || weaponinfo.number >= max_weaponinfo) - { - botimport.Print(PRT_ERROR, "weapon info number %d out of range in %s\n", weaponinfo.number, path); - FreeMemory(wc); - FreeSource(source); - return NULL; - } //end if - Com_Memcpy(&wc->weaponinfo[weaponinfo.number], &weaponinfo, sizeof(weaponinfo_t)); - wc->weaponinfo[weaponinfo.number].valid = qtrue; - } //end if - else if (!strcmp(token.string, "projectileinfo")) - { - if (wc->numprojectiles >= max_projectileinfo) - { - botimport.Print(PRT_ERROR, "more than %d projectiles defined in %s\n", max_projectileinfo, path); - FreeMemory(wc); - FreeSource(source); - return NULL; - } //end if - Com_Memset(&wc->projectileinfo[wc->numprojectiles], 0, sizeof(projectileinfo_t)); - if (!ReadStructure(source, &projectileinfo_struct, (char *) &wc->projectileinfo[wc->numprojectiles])) - { - FreeMemory(wc); - FreeSource(source); - return NULL; - } //end if - wc->numprojectiles++; - } //end if - else - { - botimport.Print(PRT_ERROR, "unknown definition %s in %s\n", token.string, path); - FreeMemory(wc); - FreeSource(source); - return NULL; - } //end else - } //end while - FreeSource(source); - //fix up weapons - for (i = 0; i < wc->numweapons; i++) - { - if (!wc->weaponinfo[i].valid) continue; - if (!wc->weaponinfo[i].name[0]) - { - botimport.Print(PRT_ERROR, "weapon %d has no name in %s\n", i, path); - FreeMemory(wc); - return NULL; - } //end if - if (!wc->weaponinfo[i].projectile[0]) - { - botimport.Print(PRT_ERROR, "weapon %s has no projectile in %s\n", wc->weaponinfo[i].name, path); - FreeMemory(wc); - return NULL; - } //end if - //find the projectile info and copy it to the weapon info - for (j = 0; j < wc->numprojectiles; j++) - { - if (!strcmp(wc->projectileinfo[j].name, wc->weaponinfo[i].projectile)) - { - Com_Memcpy(&wc->weaponinfo[i].proj, &wc->projectileinfo[j], sizeof(projectileinfo_t)); - break; - } //end if - } //end for - if (j == wc->numprojectiles) - { - botimport.Print(PRT_ERROR, "weapon %s uses undefined projectile in %s\n", wc->weaponinfo[i].name, path); - FreeMemory(wc); - return NULL; - } //end if - } //end for - if (!wc->numweapons) botimport.Print(PRT_WARNING, "no weapon info loaded\n"); - botimport.Print(PRT_MESSAGE, "loaded %s\n", path); - return wc; -} //end of the function LoadWeaponConfig -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int *WeaponWeightIndex(weightconfig_t *wwc, weaponconfig_t *wc) -{ - int *index, i; - - //initialize item weight index - index = (int *) GetClearedMemory(sizeof(int) * wc->numweapons); - - for (i = 0; i < wc->numweapons; i++) - { - index[i] = FindFuzzyWeight(wwc, wc->weaponinfo[i].name); - } //end for - return index; -} //end of the function WeaponWeightIndex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotFreeWeaponWeights(int weaponstate) -{ - bot_weaponstate_t *ws; - - ws = BotWeaponStateFromHandle(weaponstate); - if (!ws) return; - if (ws->weaponweightconfig) FreeWeightConfig(ws->weaponweightconfig); - if (ws->weaponweightindex) FreeMemory(ws->weaponweightindex); -} //end of the function BotFreeWeaponWeights -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotLoadWeaponWeights(int weaponstate, char *filename) -{ - bot_weaponstate_t *ws; - - ws = BotWeaponStateFromHandle(weaponstate); - if (!ws) return BLERR_CANNOTLOADWEAPONWEIGHTS; - BotFreeWeaponWeights(weaponstate); - // - ws->weaponweightconfig = ReadWeightConfig(filename); - if (!ws->weaponweightconfig) - { - botimport.Print(PRT_FATAL, "couldn't load weapon config %s\n", filename); - return BLERR_CANNOTLOADWEAPONWEIGHTS; - } //end if - if (!weaponconfig) return BLERR_CANNOTLOADWEAPONCONFIG; - ws->weaponweightindex = WeaponWeightIndex(ws->weaponweightconfig, weaponconfig); - return BLERR_NOERROR; -} //end of the function BotLoadWeaponWeights -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo) -{ - bot_weaponstate_t *ws; - - if (!BotValidWeaponNumber(weapon)) return; - ws = BotWeaponStateFromHandle(weaponstate); - if (!ws) return; - if (!weaponconfig) return; - Com_Memcpy(weaponinfo, &weaponconfig->weaponinfo[weapon], sizeof(weaponinfo_t)); -} //end of the function BotGetWeaponInfo -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotChooseBestFightWeapon(int weaponstate, int *inventory) -{ - int i, index, bestweapon; - float weight, bestweight; - weaponconfig_t *wc; - bot_weaponstate_t *ws; - - ws = BotWeaponStateFromHandle(weaponstate); - if (!ws) return 0; - wc = weaponconfig; - if (!weaponconfig) return 0; - - //if the bot has no weapon weight configuration - if (!ws->weaponweightconfig) return 0; - - bestweight = 0; - bestweapon = 0; - for (i = 0; i < wc->numweapons; i++) - { - if (!wc->weaponinfo[i].valid) continue; - index = ws->weaponweightindex[i]; - if (index < 0) continue; - weight = FuzzyWeight(inventory, ws->weaponweightconfig, index); - if (weight > bestweight) - { - bestweight = weight; - bestweapon = i; - } //end if - } //end for - return bestweapon; -} //end of the function BotChooseBestFightWeapon -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotResetWeaponState(int weaponstate) -{ - struct weightconfig_s *weaponweightconfig; - int *weaponweightindex; - bot_weaponstate_t *ws; - - ws = BotWeaponStateFromHandle(weaponstate); - if (!ws) return; - weaponweightconfig = ws->weaponweightconfig; - weaponweightindex = ws->weaponweightindex; - - //Com_Memset(ws, 0, sizeof(bot_weaponstate_t)); - ws->weaponweightconfig = weaponweightconfig; - ws->weaponweightindex = weaponweightindex; -} //end of the function BotResetWeaponState -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -int BotAllocWeaponState(void) -{ - int i; - - for (i = 1; i <= MAX_CLIENTS; i++) - { - if (!botweaponstates[i]) - { - botweaponstates[i] = GetClearedMemory(sizeof(bot_weaponstate_t)); - return i; - } //end if - } //end for - return 0; -} //end of the function BotAllocWeaponState -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -void BotFreeWeaponState(int handle) -{ - if (handle <= 0 || handle > MAX_CLIENTS) - { - botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); - return; - } //end if - if (!botweaponstates[handle]) - { - botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); - return; - } //end if - BotFreeWeaponWeights(handle); - FreeMemory(botweaponstates[handle]); - botweaponstates[handle] = NULL; -} //end of the function BotFreeWeaponState -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BotSetupWeaponAI(void) -{ - char *file; - - file = LibVarString("weaponconfig", "weapons.c"); - weaponconfig = LoadWeaponConfig(file); - if (!weaponconfig) - { - botimport.Print(PRT_FATAL, "couldn't load the weapon config\n"); - return BLERR_CANNOTLOADWEAPONCONFIG; - } //end if - -#ifdef DEBUG_AI_WEAP - DumpWeaponConfig(weaponconfig); -#endif //DEBUG_AI_WEAP - // - return BLERR_NOERROR; -} //end of the function BotSetupWeaponAI -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotShutdownWeaponAI(void) -{ - int i; - - if (weaponconfig) FreeMemory(weaponconfig); - weaponconfig = NULL; - - for (i = 1; i <= MAX_CLIENTS; i++) - { - if (botweaponstates[i]) - { - BotFreeWeaponState(i); - } //end if - } //end for -} //end of the function BotShutdownWeaponAI - diff --git a/tools/quake3/bspc/deps/botlib/be_ai_weap.h b/tools/quake3/bspc/deps/botlib/be_ai_weap.h deleted file mode 100644 index 59067fb0..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_weap.h +++ /dev/null @@ -1,104 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// - -/***************************************************************************** - * name: be_ai_weap.h - * - * desc: weapon AI - * - * $Archive: /source/code/botlib/be_ai_weap.h $ - * - *****************************************************************************/ - -//projectile flags -#define PFL_WINDOWDAMAGE 1 //projectile damages through window -#define PFL_RETURN 2 //set when projectile returns to owner -//weapon flags -#define WFL_FIRERELEASED 1 //set when projectile is fired with key-up event -//damage types -#define DAMAGETYPE_IMPACT 1 //damage on impact -#define DAMAGETYPE_RADIAL 2 //radial damage -#define DAMAGETYPE_VISIBLE 4 //damage to all entities visible to the projectile - -typedef struct projectileinfo_s -{ - char name[MAX_STRINGFIELD]; - char model[MAX_STRINGFIELD]; - int flags; - float gravity; - int damage; - float radius; - int visdamage; - int damagetype; - int healthinc; - float push; - float detonation; - float bounce; - float bouncefric; - float bouncestop; -} projectileinfo_t; - -typedef struct weaponinfo_s -{ - int valid; //true if the weapon info is valid - int number; //number of the weapon - char name[MAX_STRINGFIELD]; - char model[MAX_STRINGFIELD]; - int level; - int weaponindex; - int flags; - char projectile[MAX_STRINGFIELD]; - int numprojectiles; - float hspread; - float vspread; - float speed; - float acceleration; - vec3_t recoil; - vec3_t offset; - vec3_t angleoffset; - float extrazvelocity; - int ammoamount; - int ammoindex; - float activate; - float reload; - float spinup; - float spindown; - projectileinfo_t proj; //pointer to the used projectile -} weaponinfo_t; - -//setup the weapon AI -int BotSetupWeaponAI(void); -//shut down the weapon AI -void BotShutdownWeaponAI(void); -//returns the best weapon to fight with -int BotChooseBestFightWeapon(int weaponstate, int *inventory); -//returns the information of the current weapon -void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo); -//loads the weapon weights -int BotLoadWeaponWeights(int weaponstate, char *filename); -//returns a handle to a newly allocated weapon state -int BotAllocWeaponState(void); -//frees the weapon state -void BotFreeWeaponState(int weaponstate); -//resets the whole weapon state -void BotResetWeaponState(int weaponstate); diff --git a/tools/quake3/bspc/deps/botlib/be_ai_weight.c b/tools/quake3/bspc/deps/botlib/be_ai_weight.c deleted file mode 100644 index 75cecd58..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_weight.c +++ /dev/null @@ -1,912 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_ai_weight.c - * - * desc: fuzzy logic - * - * $Archive: /MissionPack/code/botlib/be_ai_weight.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_log.h" -#include "l_utils.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "l_libvar.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_interface.h" -#include "be_ai_weight.h" - -#define MAX_INVENTORYVALUE 999999 -#define EVALUATERECURSIVELY - -#define MAX_WEIGHT_FILES 128 -weightconfig_t *weightFileList[MAX_WEIGHT_FILES]; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int ReadValue(source_t *source, float *value) -{ - token_t token; - - if (!PC_ExpectAnyToken(source, &token)) return qfalse; - if (!strcmp(token.string, "-")) - { - SourceWarning(source, "negative value set to zero\n"); - if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) return qfalse; - } //end if - if (token.type != TT_NUMBER) - { - SourceError(source, "invalid return value %s\n", token.string); - return qfalse; - } //end if - *value = token.floatvalue; - return qtrue; -} //end of the function ReadValue -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int ReadFuzzyWeight(source_t *source, fuzzyseperator_t *fs) -{ - if (PC_CheckTokenString(source, "balance")) - { - fs->type = WT_BALANCE; - if (!PC_ExpectTokenString(source, "(")) return qfalse; - if (!ReadValue(source, &fs->weight)) return qfalse; - if (!PC_ExpectTokenString(source, ",")) return qfalse; - if (!ReadValue(source, &fs->minweight)) return qfalse; - if (!PC_ExpectTokenString(source, ",")) return qfalse; - if (!ReadValue(source, &fs->maxweight)) return qfalse; - if (!PC_ExpectTokenString(source, ")")) return qfalse; - } //end if - else - { - fs->type = 0; - if (!ReadValue(source, &fs->weight)) return qfalse; - fs->minweight = fs->weight; - fs->maxweight = fs->weight; - } //end if - if (!PC_ExpectTokenString(source, ";")) return qfalse; - return qtrue; -} //end of the function ReadFuzzyWeight -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeFuzzySeperators_r(fuzzyseperator_t *fs) -{ - if (!fs) return; - if (fs->child) FreeFuzzySeperators_r(fs->child); - if (fs->next) FreeFuzzySeperators_r(fs->next); - FreeMemory(fs); -} //end of the function FreeFuzzySeperators -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeWeightConfig2(weightconfig_t *config) -{ - int i; - - for (i = 0; i < config->numweights; i++) - { - FreeFuzzySeperators_r(config->weights[i].firstseperator); - if (config->weights[i].name) FreeMemory(config->weights[i].name); - } //end for - FreeMemory(config); -} //end of the function FreeWeightConfig2 -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeWeightConfig(weightconfig_t *config) -{ - if (!LibVarGetValue("bot_reloadcharacters")) return; - FreeWeightConfig2(config); -} //end of the function FreeWeightConfig -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -fuzzyseperator_t *ReadFuzzySeperators_r(source_t *source) -{ - int newindent, index, def, founddefault; - token_t token; - fuzzyseperator_t *fs, *lastfs, *firstfs; - - founddefault = qfalse; - firstfs = NULL; - lastfs = NULL; - if (!PC_ExpectTokenString(source, "(")) return NULL; - if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) return NULL; - index = token.intvalue; - if (!PC_ExpectTokenString(source, ")")) return NULL; - if (!PC_ExpectTokenString(source, "{")) return NULL; - if (!PC_ExpectAnyToken(source, &token)) return NULL; - do - { - def = !strcmp(token.string, "default"); - if (def || !strcmp(token.string, "case")) - { - fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); - fs->index = index; - if (lastfs) lastfs->next = fs; - else firstfs = fs; - lastfs = fs; - if (def) - { - if (founddefault) - { - SourceError(source, "switch already has a default\n"); - FreeFuzzySeperators_r(firstfs); - return NULL; - } //end if - fs->value = MAX_INVENTORYVALUE; - founddefault = qtrue; - } //end if - else - { - if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) - { - FreeFuzzySeperators_r(firstfs); - return NULL; - } //end if - fs->value = token.intvalue; - } //end else - if (!PC_ExpectTokenString(source, ":") || !PC_ExpectAnyToken(source, &token)) - { - FreeFuzzySeperators_r(firstfs); - return NULL; - } //end if - newindent = qfalse; - if (!strcmp(token.string, "{")) - { - newindent = qtrue; - if (!PC_ExpectAnyToken(source, &token)) - { - FreeFuzzySeperators_r(firstfs); - return NULL; - } //end if - } //end if - if (!strcmp(token.string, "return")) - { - if (!ReadFuzzyWeight(source, fs)) - { - FreeFuzzySeperators_r(firstfs); - return NULL; - } //end if - } //end if - else if (!strcmp(token.string, "switch")) - { - fs->child = ReadFuzzySeperators_r(source); - if (!fs->child) - { - FreeFuzzySeperators_r(firstfs); - return NULL; - } //end if - } //end else if - else - { - SourceError(source, "invalid name %s\n", token.string); - return NULL; - } //end else - if (newindent) - { - if (!PC_ExpectTokenString(source, "}")) - { - FreeFuzzySeperators_r(firstfs); - return NULL; - } //end if - } //end if - } //end if - else - { - FreeFuzzySeperators_r(firstfs); - SourceError(source, "invalid name %s\n", token.string); - return NULL; - } //end else - if (!PC_ExpectAnyToken(source, &token)) - { - FreeFuzzySeperators_r(firstfs); - return NULL; - } //end if - } while(strcmp(token.string, "}")); - // - if (!founddefault) - { - SourceWarning(source, "switch without default\n"); - fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); - fs->index = index; - fs->value = MAX_INVENTORYVALUE; - fs->weight = 0; - fs->next = NULL; - fs->child = NULL; - if (lastfs) lastfs->next = fs; - else firstfs = fs; - lastfs = fs; - } //end if - // - return firstfs; -} //end of the function ReadFuzzySeperators_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -weightconfig_t *ReadWeightConfig(char *filename) -{ - int newindent, avail = 0, n; - token_t token; - source_t *source; - fuzzyseperator_t *fs; - weightconfig_t *config = NULL; -#ifdef DEBUG - int starttime; - - starttime = Sys_MilliSeconds(); -#endif //DEBUG - - if (!LibVarGetValue("bot_reloadcharacters")) - { - avail = -1; - for( n = 0; n < MAX_WEIGHT_FILES; n++ ) - { - config = weightFileList[n]; - if( !config ) - { - if( avail == -1 ) - { - avail = n; - } //end if - continue; - } //end if - if( strcmp( filename, config->filename ) == 0 ) - { - //botimport.Print( PRT_MESSAGE, "retained %s\n", filename ); - return config; - } //end if - } //end for - - if( avail == -1 ) - { - botimport.Print( PRT_ERROR, "weightFileList was full trying to load %s\n", filename ); - return NULL; - } //end if - } //end if - - PC_SetBaseFolder(BOTFILESBASEFOLDER); - source = LoadSourceFile(filename); - if (!source) - { - botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); - return NULL; - } //end if - // - config = (weightconfig_t *) GetClearedMemory(sizeof(weightconfig_t)); - config->numweights = 0; - Q_strncpyz( config->filename, filename, sizeof(config->filename) ); - //parse the item config file - while(PC_ReadToken(source, &token)) - { - if (!strcmp(token.string, "weight")) - { - if (config->numweights >= MAX_WEIGHTS) - { - SourceWarning(source, "too many fuzzy weights\n"); - break; - } //end if - if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) - { - FreeWeightConfig(config); - FreeSource(source); - return NULL; - } //end if - StripDoubleQuotes(token.string); - config->weights[config->numweights].name = (char *) GetClearedMemory(strlen(token.string) + 1); - strcpy(config->weights[config->numweights].name, token.string); - if (!PC_ExpectAnyToken(source, &token)) - { - FreeWeightConfig(config); - FreeSource(source); - return NULL; - } //end if - newindent = qfalse; - if (!strcmp(token.string, "{")) - { - newindent = qtrue; - if (!PC_ExpectAnyToken(source, &token)) - { - FreeWeightConfig(config); - FreeSource(source); - return NULL; - } //end if - } //end if - if (!strcmp(token.string, "switch")) - { - fs = ReadFuzzySeperators_r(source); - if (!fs) - { - FreeWeightConfig(config); - FreeSource(source); - return NULL; - } //end if - config->weights[config->numweights].firstseperator = fs; - } //end if - else if (!strcmp(token.string, "return")) - { - fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); - fs->index = 0; - fs->value = MAX_INVENTORYVALUE; - fs->next = NULL; - fs->child = NULL; - if (!ReadFuzzyWeight(source, fs)) - { - FreeMemory(fs); - FreeWeightConfig(config); - FreeSource(source); - return NULL; - } //end if - config->weights[config->numweights].firstseperator = fs; - } //end else if - else - { - SourceError(source, "invalid name %s\n", token.string); - FreeWeightConfig(config); - FreeSource(source); - return NULL; - } //end else - if (newindent) - { - if (!PC_ExpectTokenString(source, "}")) - { - FreeWeightConfig(config); - FreeSource(source); - return NULL; - } //end if - } //end if - config->numweights++; - } //end if - else - { - SourceError(source, "invalid name %s\n", token.string); - FreeWeightConfig(config); - FreeSource(source); - return NULL; - } //end else - } //end while - //free the source at the end of a pass - FreeSource(source); - //if the file was located in a pak file - botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); -#ifdef DEBUG - if (bot_developer) - { - botimport.Print(PRT_MESSAGE, "weights loaded in %d msec\n", Sys_MilliSeconds() - starttime); - } //end if -#endif //DEBUG - // - if (!LibVarGetValue("bot_reloadcharacters")) - { - weightFileList[avail] = config; - } //end if - // - return config; -} //end of the function ReadWeightConfig -#if 0 -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WriteFuzzyWeight(FILE *fp, fuzzyseperator_t *fs) -{ - if (fs->type == WT_BALANCE) - { - if (fprintf(fp, " return balance(") < 0) return qfalse; - if (!WriteFloat(fp, fs->weight)) return qfalse; - if (fprintf(fp, ",") < 0) return qfalse; - if (!WriteFloat(fp, fs->minweight)) return qfalse; - if (fprintf(fp, ",") < 0) return qfalse; - if (!WriteFloat(fp, fs->maxweight)) return qfalse; - if (fprintf(fp, ");\n") < 0) return qfalse; - } //end if - else - { - if (fprintf(fp, " return ") < 0) return qfalse; - if (!WriteFloat(fp, fs->weight)) return qfalse; - if (fprintf(fp, ";\n") < 0) return qfalse; - } //end else - return qtrue; -} //end of the function WriteFuzzyWeight -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WriteFuzzySeperators_r(FILE *fp, fuzzyseperator_t *fs, int indent) -{ - if (!WriteIndent(fp, indent)) return qfalse; - if (fprintf(fp, "switch(%d)\n", fs->index) < 0) return qfalse; - if (!WriteIndent(fp, indent)) return qfalse; - if (fprintf(fp, "{\n") < 0) return qfalse; - indent++; - do - { - if (!WriteIndent(fp, indent)) return qfalse; - if (fs->next) - { - if (fprintf(fp, "case %d:", fs->value) < 0) return qfalse; - } //end if - else - { - if (fprintf(fp, "default:") < 0) return qfalse; - } //end else - if (fs->child) - { - if (fprintf(fp, "\n") < 0) return qfalse; - if (!WriteIndent(fp, indent)) return qfalse; - if (fprintf(fp, "{\n") < 0) return qfalse; - if (!WriteFuzzySeperators_r(fp, fs->child, indent + 1)) return qfalse; - if (!WriteIndent(fp, indent)) return qfalse; - if (fs->next) - { - if (fprintf(fp, "} //end case\n") < 0) return qfalse; - } //end if - else - { - if (fprintf(fp, "} //end default\n") < 0) return qfalse; - } //end else - } //end if - else - { - if (!WriteFuzzyWeight(fp, fs)) return qfalse; - } //end else - fs = fs->next; - } while(fs); - indent--; - if (!WriteIndent(fp, indent)) return qfalse; - if (fprintf(fp, "} //end switch\n") < 0) return qfalse; - return qtrue; -} //end of the function WriteItemFuzzyWeights_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WriteWeightConfig(char *filename, weightconfig_t *config) -{ - int i; - FILE *fp; - weight_t *ifw; - - fp = fopen(filename, "wb"); - if (!fp) return qfalse; - - for (i = 0; i < config->numweights; i++) - { - ifw = &config->weights[i]; - if (fprintf(fp, "\nweight \"%s\"\n", ifw->name) < 0) return qfalse; - if (fprintf(fp, "{\n") < 0) return qfalse; - if (ifw->firstseperator->index > 0) - { - if (!WriteFuzzySeperators_r(fp, ifw->firstseperator, 1)) return qfalse; - } //end if - else - { - if (!WriteIndent(fp, 1)) return qfalse; - if (!WriteFuzzyWeight(fp, ifw->firstseperator)) return qfalse; - } //end else - if (fprintf(fp, "} //end weight\n") < 0) return qfalse; - } //end for - fclose(fp); - return qtrue; -} //end of the function WriteWeightConfig -#endif -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int FindFuzzyWeight(weightconfig_t *wc, char *name) -{ - int i; - - for (i = 0; i < wc->numweights; i++) - { - if (!strcmp(wc->weights[i].name, name)) - { - return i; - } //end if - } //end if - return -1; -} //end of the function FindFuzzyWeight -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float FuzzyWeight_r(int *inventory, fuzzyseperator_t *fs) -{ - float scale, w1, w2; - - if (inventory[fs->index] < fs->value) - { - if (fs->child) return FuzzyWeight_r(inventory, fs->child); - else return fs->weight; - } //end if - else if (fs->next) - { - if (inventory[fs->index] < fs->next->value) - { - //first weight - if (fs->child) w1 = FuzzyWeight_r(inventory, fs->child); - else w1 = fs->weight; - //second weight - if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child); - else w2 = fs->next->weight; - //the scale factor - scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value); - //scale between the two weights - return scale * w1 + (1 - scale) * w2; - } //end if - return FuzzyWeight_r(inventory, fs->next); - } //end else if - return fs->weight; -} //end of the function FuzzyWeight_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float FuzzyWeightUndecided_r(int *inventory, fuzzyseperator_t *fs) -{ - float scale, w1, w2; - - if (inventory[fs->index] < fs->value) - { - if (fs->child) return FuzzyWeightUndecided_r(inventory, fs->child); - else return fs->minweight + random() * (fs->maxweight - fs->minweight); - } //end if - else if (fs->next) - { - if (inventory[fs->index] < fs->next->value) - { - //first weight - if (fs->child) w1 = FuzzyWeightUndecided_r(inventory, fs->child); - else w1 = fs->minweight + random() * (fs->maxweight - fs->minweight); - //second weight - if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child); - else w2 = fs->next->minweight + random() * (fs->next->maxweight - fs->next->minweight); - //the scale factor - scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value); - //scale between the two weights - return scale * w1 + (1 - scale) * w2; - } //end if - return FuzzyWeightUndecided_r(inventory, fs->next); - } //end else if - return fs->weight; -} //end of the function FuzzyWeightUndecided_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum) -{ -#ifdef EVALUATERECURSIVELY - return FuzzyWeight_r(inventory, wc->weights[weightnum].firstseperator); -#else - fuzzyseperator_t *s; - - s = wc->weights[weightnum].firstseperator; - if (!s) return 0; - while(1) - { - if (inventory[s->index] < s->value) - { - if (s->child) s = s->child; - else return s->weight; - } //end if - else - { - if (s->next) s = s->next; - else return s->weight; - } //end else - } //end if - return 0; -#endif -} //end of the function FuzzyWeight -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum) -{ -#ifdef EVALUATERECURSIVELY - return FuzzyWeightUndecided_r(inventory, wc->weights[weightnum].firstseperator); -#else - fuzzyseperator_t *s; - - s = wc->weights[weightnum].firstseperator; - if (!s) return 0; - while(1) - { - if (inventory[s->index] < s->value) - { - if (s->child) s = s->child; - else return s->minweight + random() * (s->maxweight - s->minweight); - } //end if - else - { - if (s->next) s = s->next; - else return s->minweight + random() * (s->maxweight - s->minweight); - } //end else - } //end if - return 0; -#endif -} //end of the function FuzzyWeightUndecided -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EvolveFuzzySeperator_r(fuzzyseperator_t *fs) -{ - if (fs->child) - { - EvolveFuzzySeperator_r(fs->child); - } //end if - else if (fs->type == WT_BALANCE) - { - //every once in a while an evolution leap occurs, mutation - if (random() < 0.01) fs->weight += crandom() * (fs->maxweight - fs->minweight); - else fs->weight += crandom() * (fs->maxweight - fs->minweight) * 0.5; - //modify bounds if necesary because of mutation - if (fs->weight < fs->minweight) fs->minweight = fs->weight; - else if (fs->weight > fs->maxweight) fs->maxweight = fs->weight; - } //end else if - if (fs->next) EvolveFuzzySeperator_r(fs->next); -} //end of the function EvolveFuzzySeperator_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EvolveWeightConfig(weightconfig_t *config) -{ - int i; - - for (i = 0; i < config->numweights; i++) - { - EvolveFuzzySeperator_r(config->weights[i].firstseperator); - } //end for -} //end of the function EvolveWeightConfig -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ScaleFuzzySeperator_r(fuzzyseperator_t *fs, float scale) -{ - if (fs->child) - { - ScaleFuzzySeperator_r(fs->child, scale); - } //end if - else if (fs->type == WT_BALANCE) - { - // - fs->weight = (fs->maxweight + fs->minweight) * scale; - //get the weight between bounds - if (fs->weight < fs->minweight) fs->weight = fs->minweight; - else if (fs->weight > fs->maxweight) fs->weight = fs->maxweight; - } //end else if - if (fs->next) ScaleFuzzySeperator_r(fs->next, scale); -} //end of the function ScaleFuzzySeperator_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ScaleWeight(weightconfig_t *config, char *name, float scale) -{ - int i; - - if (scale < 0) scale = 0; - else if (scale > 1) scale = 1; - for (i = 0; i < config->numweights; i++) - { - if (!strcmp(name, config->weights[i].name)) - { - ScaleFuzzySeperator_r(config->weights[i].firstseperator, scale); - break; - } //end if - } //end for -} //end of the function ScaleWeight -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t *fs, float scale) -{ - if (fs->child) - { - ScaleFuzzySeperatorBalanceRange_r(fs->child, scale); - } //end if - else if (fs->type == WT_BALANCE) - { - float mid = (fs->minweight + fs->maxweight) * 0.5; - //get the weight between bounds - fs->maxweight = mid + (fs->maxweight - mid) * scale; - fs->minweight = mid + (fs->minweight - mid) * scale; - if (fs->maxweight < fs->minweight) - { - fs->maxweight = fs->minweight; - } //end if - } //end else if - if (fs->next) ScaleFuzzySeperatorBalanceRange_r(fs->next, scale); -} //end of the function ScaleFuzzySeperatorBalanceRange_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ScaleFuzzyBalanceRange(weightconfig_t *config, float scale) -{ - int i; - - if (scale < 0) scale = 0; - else if (scale > 100) scale = 100; - for (i = 0; i < config->numweights; i++) - { - ScaleFuzzySeperatorBalanceRange_r(config->weights[i].firstseperator, scale); - } //end for -} //end of the function ScaleFuzzyBalanceRange -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int InterbreedFuzzySeperator_r(fuzzyseperator_t *fs1, fuzzyseperator_t *fs2, - fuzzyseperator_t *fsout) -{ - if (fs1->child) - { - if (!fs2->child || !fsout->child) - { - botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal child\n"); - return qfalse; - } //end if - if (!InterbreedFuzzySeperator_r(fs2->child, fs2->child, fsout->child)) - { - return qfalse; - } //end if - } //end if - else if (fs1->type == WT_BALANCE) - { - if (fs2->type != WT_BALANCE || fsout->type != WT_BALANCE) - { - botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal balance\n"); - return qfalse; - } //end if - fsout->weight = (fs1->weight + fs2->weight) / 2; - if (fsout->weight > fsout->maxweight) fsout->maxweight = fsout->weight; - if (fsout->weight > fsout->minweight) fsout->minweight = fsout->weight; - } //end else if - if (fs1->next) - { - if (!fs2->next || !fsout->next) - { - botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal next\n"); - return qfalse; - } //end if - if (!InterbreedFuzzySeperator_r(fs1->next, fs2->next, fsout->next)) - { - return qfalse; - } //end if - } //end if - return qtrue; -} //end of the function InterbreedFuzzySeperator_r -//=========================================================================== -// config1 and config2 are interbreeded and stored in configout -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2, - weightconfig_t *configout) -{ - int i; - - if (config1->numweights != config2->numweights || - config1->numweights != configout->numweights) - { - botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal numweights\n"); - return; - } //end if - for (i = 0; i < config1->numweights; i++) - { - InterbreedFuzzySeperator_r(config1->weights[i].firstseperator, - config2->weights[i].firstseperator, - configout->weights[i].firstseperator); - } //end for -} //end of the function InterbreedWeightConfigs -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BotShutdownWeights(void) -{ - int i; - - for( i = 0; i < MAX_WEIGHT_FILES; i++ ) - { - if (weightFileList[i]) - { - FreeWeightConfig2(weightFileList[i]); - weightFileList[i] = NULL; - } //end if - } //end for -} //end of the function BotShutdownWeights diff --git a/tools/quake3/bspc/deps/botlib/be_ai_weight.h b/tools/quake3/bspc/deps/botlib/be_ai_weight.h deleted file mode 100644 index fb1c8853..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ai_weight.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_ai_weight.h - * - * desc: fuzzy weights - * - * $Archive: /source/code/botlib/be_ai_weight.h $ - * - *****************************************************************************/ - -#define WT_BALANCE 1 -#define MAX_WEIGHTS 128 - -//fuzzy seperator -typedef struct fuzzyseperator_s -{ - int index; - int value; - int type; - float weight; - float minweight; - float maxweight; - struct fuzzyseperator_s *child; - struct fuzzyseperator_s *next; -} fuzzyseperator_t; - -//fuzzy weight -typedef struct weight_s -{ - char *name; - struct fuzzyseperator_s *firstseperator; -} weight_t; - -//weight configuration -typedef struct weightconfig_s -{ - int numweights; - weight_t weights[MAX_WEIGHTS]; - char filename[MAX_QPATH]; -} weightconfig_t; - -//reads a weight configuration -weightconfig_t *ReadWeightConfig(char *filename); -//free a weight configuration -void FreeWeightConfig(weightconfig_t *config); -//writes a weight configuration, returns true if successfull -qboolean WriteWeightConfig(char *filename, weightconfig_t *config); -//find the fuzzy weight with the given name -int FindFuzzyWeight(weightconfig_t *wc, char *name); -//returns the fuzzy weight for the given inventory and weight -float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum); -float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum); -//scales the weight with the given name -void ScaleWeight(weightconfig_t *config, char *name, float scale); -//scale the balance range -void ScaleBalanceRange(weightconfig_t *config, float scale); -//evolves the weight configuration -void EvolveWeightConfig(weightconfig_t *config); -//interbreed the weight configurations and stores the interbreeded one in configout -void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2, weightconfig_t *configout); -//frees cached weight configurations -void BotShutdownWeights(void); diff --git a/tools/quake3/bspc/deps/botlib/be_ea.c b/tools/quake3/bspc/deps/botlib/be_ea.c deleted file mode 100644 index b6ddc3f3..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ea.c +++ /dev/null @@ -1,508 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_ea.c - * - * desc: elementary actions - * - * $Archive: /MissionPack/code/botlib/be_ea.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "botlib.h" -#include "be_interface.h" - -#define MAX_USERMOVE 400 -#define MAX_COMMANDARGUMENTS 10 -#define ACTION_JUMPEDLASTFRAME 128 - -bot_input_t *botinputs; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Say(int client, char *str) -{ - botimport.BotClientCommand(client, va("say %s", str) ); -} //end of the function EA_Say -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_SayTeam(int client, char *str) -{ - botimport.BotClientCommand(client, va("say_team %s", str)); -} //end of the function EA_SayTeam -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Tell(int client, int clientto, char *str) -{ - botimport.BotClientCommand(client, va("tell %d, %s", clientto, str)); -} //end of the function EA_SayTeam -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_UseItem(int client, char *it) -{ - botimport.BotClientCommand(client, va("use %s", it)); -} //end of the function EA_UseItem -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_DropItem(int client, char *it) -{ - botimport.BotClientCommand(client, va("drop %s", it)); -} //end of the function EA_DropItem -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_UseInv(int client, char *inv) -{ - botimport.BotClientCommand(client, va("invuse %s", inv)); -} //end of the function EA_UseInv -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_DropInv(int client, char *inv) -{ - botimport.BotClientCommand(client, va("invdrop %s", inv)); -} //end of the function EA_DropInv -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Gesture(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_GESTURE; -} //end of the function EA_Gesture -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Command(int client, char *command) -{ - botimport.BotClientCommand(client, command); -} //end of the function EA_Command -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_SelectWeapon(int client, int weapon) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->weapon = weapon; -} //end of the function EA_SelectWeapon -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Attack(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_ATTACK; -} //end of the function EA_Attack -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Talk(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_TALK; -} //end of the function EA_Talk -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Use(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_USE; -} //end of the function EA_Use -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Respawn(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_RESPAWN; -} //end of the function EA_Respawn -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Jump(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - if (bi->actionflags & ACTION_JUMPEDLASTFRAME) - { - bi->actionflags &= ~ACTION_JUMP; - } //end if - else - { - bi->actionflags |= ACTION_JUMP; - } //end if -} //end of the function EA_Jump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_DelayedJump(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - if (bi->actionflags & ACTION_JUMPEDLASTFRAME) - { - bi->actionflags &= ~ACTION_DELAYEDJUMP; - } //end if - else - { - bi->actionflags |= ACTION_DELAYEDJUMP; - } //end if -} //end of the function EA_DelayedJump -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Crouch(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_CROUCH; -} //end of the function EA_Crouch -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Walk(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_WALK; -} //end of the function EA_Walk -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Action(int client, int action) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= action; -} //end of function EA_Action -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_MoveUp(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_MOVEUP; -} //end of the function EA_MoveUp -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_MoveDown(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_MOVEDOWN; -} //end of the function EA_MoveDown -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_MoveForward(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_MOVEFORWARD; -} //end of the function EA_MoveForward -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_MoveBack(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_MOVEBACK; -} //end of the function EA_MoveBack -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_MoveLeft(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_MOVELEFT; -} //end of the function EA_MoveLeft -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_MoveRight(int client) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - bi->actionflags |= ACTION_MOVERIGHT; -} //end of the function EA_MoveRight -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Move(int client, vec3_t dir, float speed) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - VectorCopy(dir, bi->dir); - //cap speed - if (speed > MAX_USERMOVE) speed = MAX_USERMOVE; - else if (speed < -MAX_USERMOVE) speed = -MAX_USERMOVE; - bi->speed = speed; -} //end of the function EA_Move -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_View(int client, vec3_t viewangles) -{ - bot_input_t *bi; - - bi = &botinputs[client]; - - VectorCopy(viewangles, bi->viewangles); -} //end of the function EA_View -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_EndRegular(int client, float thinktime) -{ -/* - bot_input_t *bi; - int jumped = qfalse; - - bi = &botinputs[client]; - - bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; - - bi->thinktime = thinktime; - botimport.BotInput(client, bi); - - bi->thinktime = 0; - VectorClear(bi->dir); - bi->speed = 0; - jumped = bi->actionflags & ACTION_JUMP; - bi->actionflags = 0; - if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; -*/ -} //end of the function EA_EndRegular -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_GetInput(int client, float thinktime, bot_input_t *input) -{ - bot_input_t *bi; -// int jumped = qfalse; - - bi = &botinputs[client]; - -// bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; - - bi->thinktime = thinktime; - Com_Memcpy(input, bi, sizeof(bot_input_t)); - - /* - bi->thinktime = 0; - VectorClear(bi->dir); - bi->speed = 0; - jumped = bi->actionflags & ACTION_JUMP; - bi->actionflags = 0; - if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; - */ -} //end of the function EA_GetInput -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_ResetInput(int client) -{ - bot_input_t *bi; - int jumped = qfalse; - - bi = &botinputs[client]; - bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; - - bi->thinktime = 0; - VectorClear(bi->dir); - bi->speed = 0; - jumped = bi->actionflags & ACTION_JUMP; - bi->actionflags = 0; - if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; -} //end of the function EA_ResetInput -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int EA_Setup(void) -{ - //initialize the bot inputs - botinputs = (bot_input_t *) GetClearedHunkMemory( - botlibglobals.maxclients * sizeof(bot_input_t)); - return BLERR_NOERROR; -} //end of the function EA_Setup -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void EA_Shutdown(void) -{ - FreeMemory(botinputs); - botinputs = NULL; -} //end of the function EA_Shutdown diff --git a/tools/quake3/bspc/deps/botlib/be_ea.h b/tools/quake3/bspc/deps/botlib/be_ea.h deleted file mode 100644 index 4fb37046..00000000 --- a/tools/quake3/bspc/deps/botlib/be_ea.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// - -/***************************************************************************** - * name: be_ea.h - * - * desc: elementary actions - * - * $Archive: /source/code/botlib/be_ea.h $ - * - *****************************************************************************/ - -//ClientCommand elementary actions -void EA_Say(int client, char *str); -void EA_SayTeam(int client, char *str); -void EA_Command(int client, char *command ); - -void EA_Action(int client, int action); -void EA_Crouch(int client); -void EA_Walk(int client); -void EA_MoveUp(int client); -void EA_MoveDown(int client); -void EA_MoveForward(int client); -void EA_MoveBack(int client); -void EA_MoveLeft(int client); -void EA_MoveRight(int client); -void EA_Attack(int client); -void EA_Respawn(int client); -void EA_Talk(int client); -void EA_Gesture(int client); -void EA_Use(int client); - -//regular elementary actions -void EA_SelectWeapon(int client, int weapon); -void EA_Jump(int client); -void EA_DelayedJump(int client); -void EA_Move(int client, vec3_t dir, float speed); -void EA_View(int client, vec3_t viewangles); - -//send regular input to the server -void EA_EndRegular(int client, float thinktime); -void EA_GetInput(int client, float thinktime, bot_input_t *input); -void EA_ResetInput(int client); -//setup and shutdown routines -int EA_Setup(void); -void EA_Shutdown(void); diff --git a/tools/quake3/bspc/deps/botlib/be_interface.c b/tools/quake3/bspc/deps/botlib/be_interface.c deleted file mode 100644 index c15c8590..00000000 --- a/tools/quake3/bspc/deps/botlib/be_interface.c +++ /dev/null @@ -1,881 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_interface.c // bk010221 - FIXME - DEAD code elimination - * - * desc: bot library interface - * - * $Archive: /MissionPack/code/botlib/be_interface.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_log.h" -#include "l_libvar.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "aasfile.h" -#include "botlib.h" -#include "be_aas.h" -#include "be_aas_funcs.h" -#include "be_aas_def.h" -#include "be_interface.h" - -#include "be_ea.h" -#include "be_ai_weight.h" -#include "be_ai_goal.h" -#include "be_ai_move.h" -#include "be_ai_weap.h" -#include "be_ai_chat.h" -#include "be_ai_char.h" -#include "be_ai_gen.h" - -//library globals in a structure -botlib_globals_t botlibglobals; - -botlib_export_t be_botlib_export; -botlib_import_t botimport; -// -int bot_developer; -//qtrue if the library is setup -int botlibsetup = qfalse; - -//=========================================================================== -// -// several functions used by the exported functions -// -//=========================================================================== - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Sys_MilliSeconds(void) -{ - return clock() * 1000 / CLOCKS_PER_SEC; -} //end of the function Sys_MilliSeconds -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean ValidClientNumber(int num, char *str) -{ - if (num < 0 || num > botlibglobals.maxclients) - { - //weird: the disabled stuff results in a crash - botimport.Print(PRT_ERROR, "%s: invalid client number %d, [0, %d]\n", - str, num, botlibglobals.maxclients); - return qfalse; - } //end if - return qtrue; -} //end of the function BotValidateClientNumber -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean ValidEntityNumber(int num, char *str) -{ - if (num < 0 || num > botlibglobals.maxentities) - { - botimport.Print(PRT_ERROR, "%s: invalid entity number %d, [0, %d]\n", - str, num, botlibglobals.maxentities); - return qfalse; - } //end if - return qtrue; -} //end of the function BotValidateClientNumber -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean BotLibSetup(char *str) -{ - if (!botlibglobals.botlibsetup) - { - botimport.Print(PRT_ERROR, "%s: bot library used before being setup\n", str); - return qfalse; - } //end if - return qtrue; -} //end of the function BotLibSetup - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Export_BotLibSetup(void) -{ - int errnum; - - bot_developer = LibVarGetValue("bot_developer"); - memset( &botlibglobals, 0, sizeof(botlibglobals) ); // bk001207 - init - //initialize byte swapping (litte endian etc.) -// Swap_Init(); - Log_Open("botlib.log"); - // - botimport.Print(PRT_MESSAGE, "------- BotLib Initialization -------\n"); - // - botlibglobals.maxclients = (int) LibVarValue("maxclients", "128"); - botlibglobals.maxentities = (int) LibVarValue("maxentities", "1024"); - - errnum = AAS_Setup(); //be_aas_main.c - if (errnum != BLERR_NOERROR) return errnum; - errnum = EA_Setup(); //be_ea.c - if (errnum != BLERR_NOERROR) return errnum; - errnum = BotSetupWeaponAI(); //be_ai_weap.c - if (errnum != BLERR_NOERROR)return errnum; - errnum = BotSetupGoalAI(); //be_ai_goal.c - if (errnum != BLERR_NOERROR) return errnum; - errnum = BotSetupChatAI(); //be_ai_chat.c - if (errnum != BLERR_NOERROR) return errnum; - errnum = BotSetupMoveAI(); //be_ai_move.c - if (errnum != BLERR_NOERROR) return errnum; - - botlibsetup = qtrue; - botlibglobals.botlibsetup = qtrue; - - return BLERR_NOERROR; -} //end of the function Export_BotLibSetup -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Export_BotLibShutdown(void) -{ - if (!BotLibSetup("BotLibShutdown")) return BLERR_LIBRARYNOTSETUP; -#ifndef DEMO - //DumpFileCRCs(); -#endif //DEMO - // - BotShutdownChatAI(); //be_ai_chat.c - BotShutdownMoveAI(); //be_ai_move.c - BotShutdownGoalAI(); //be_ai_goal.c - BotShutdownWeaponAI(); //be_ai_weap.c - BotShutdownWeights(); //be_ai_weight.c - BotShutdownCharacters(); //be_ai_char.c - //shud down aas - AAS_Shutdown(); - //shut down bot elemantary actions - EA_Shutdown(); - //free all libvars - LibVarDeAllocAll(); - //remove all global defines from the pre compiler - PC_RemoveAllGlobalDefines(); - - //dump all allocated memory -// DumpMemory(); -#ifdef DEBUG - PrintMemoryLabels(); -#endif - //shut down library log file - Log_Shutdown(); - // - botlibsetup = qfalse; - botlibglobals.botlibsetup = qfalse; - // print any files still open - PC_CheckOpenSourceHandles(); - // - return BLERR_NOERROR; -} //end of the function Export_BotLibShutdown -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Export_BotLibVarSet(char *var_name, char *value) -{ - LibVarSet(var_name, value); - return BLERR_NOERROR; -} //end of the function Export_BotLibVarSet -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Export_BotLibVarGet(char *var_name, char *value, int size) -{ - char *varvalue; - - varvalue = LibVarGetString(var_name); - strncpy(value, varvalue, size-1); - value[size-1] = '\0'; - return BLERR_NOERROR; -} //end of the function Export_BotLibVarGet -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Export_BotLibStartFrame(float time) -{ - if (!BotLibSetup("BotStartFrame")) return BLERR_LIBRARYNOTSETUP; - return AAS_StartFrame(time); -} //end of the function Export_BotLibStartFrame -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Export_BotLibLoadMap(const char *mapname) -{ -#ifdef DEBUG - int starttime = Sys_MilliSeconds(); -#endif - int errnum; - - if (!BotLibSetup("BotLoadMap")) return BLERR_LIBRARYNOTSETUP; - // - botimport.Print(PRT_MESSAGE, "------------ Map Loading ------------\n"); - //startup AAS for the current map, model and sound index - errnum = AAS_LoadMap(mapname); - if (errnum != BLERR_NOERROR) return errnum; - //initialize the items in the level - BotInitLevelItems(); //be_ai_goal.h - BotSetBrushModelTypes(); //be_ai_move.h - // - botimport.Print(PRT_MESSAGE, "-------------------------------------\n"); -#ifdef DEBUG - botimport.Print(PRT_MESSAGE, "map loaded in %d msec\n", Sys_MilliSeconds() - starttime); -#endif - // - return BLERR_NOERROR; -} //end of the function Export_BotLibLoadMap -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Export_BotLibUpdateEntity(int ent, bot_entitystate_t *state) -{ - if (!BotLibSetup("BotUpdateEntity")) return BLERR_LIBRARYNOTSETUP; - if (!ValidEntityNumber(ent, "BotUpdateEntity")) return BLERR_INVALIDENTITYNUMBER; - - return AAS_UpdateEntity(ent, state); -} //end of the function Export_BotLibUpdateEntity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir); -void ElevatorBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter); -int BotGetReachabilityToGoal(vec3_t origin, int areanum, - int lastgoalareanum, int lastareanum, - int *avoidreach, float *avoidreachtimes, int *avoidreachtries, - bot_goal_t *goal, int travelflags, int movetravelflags, - struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags); - -int AAS_PointLight(vec3_t origin, int *red, int *green, int *blue); - -int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); - -int AAS_Reachability_WeaponJump(int area1num, int area2num); - -int BotFuzzyPointReachabilityArea(vec3_t origin); - -float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum); - -void AAS_FloodAreas(vec3_t origin); - -int BotExportTest(int parm0, char *parm1, vec3_t parm2, vec3_t parm3) -{ - -// return AAS_PointLight(parm2, NULL, NULL, NULL); - -#ifdef DEBUG - static int area = -1; - static int line[2]; - int newarea, i, highlightarea, flood; -// int reachnum; - vec3_t eye, forward, right, end, origin; -// vec3_t bottomcenter; -// aas_trace_t trace; -// aas_face_t *face; -// aas_entity_t *ent; -// bsp_trace_t bsptrace; -// aas_reachability_t reach; -// bot_goal_t goal; - - // clock_t start_time, end_time; - vec3_t mins = {-16, -16, -24}; - vec3_t maxs = {16, 16, 32}; - -// int areas[10], numareas; - - - //return 0; - - if (!aasworld.loaded) return 0; - - /* - if (parm0 & 1) - { - AAS_ClearShownPolygons(); - AAS_FloodAreas(parm2); - } //end if - return 0; - */ - for (i = 0; i < 2; i++) if (!line[i]) line[i] = botimport.DebugLineCreate(); - -// AAS_ClearShownDebugLines(); - - //if (AAS_AgainstLadder(parm2)) botimport.Print(PRT_MESSAGE, "against ladder\n"); - //BotOnGround(parm2, PRESENCE_NORMAL, 1, &newarea, &newarea); - //botimport.Print(PRT_MESSAGE, "%f %f %f\n", parm2[0], parm2[1], parm2[2]); - //* - highlightarea = LibVarGetValue("bot_highlightarea"); - if (highlightarea > 0) - { - newarea = highlightarea; - } //end if - else - { - VectorCopy(parm2, origin); - origin[2] += 0.5; - //newarea = AAS_PointAreaNum(origin); - newarea = BotFuzzyPointReachabilityArea(origin); - } //end else - - botimport.Print(PRT_MESSAGE, "\rtravel time to goal (%d) = %d ", botlibglobals.goalareanum, - AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT)); - //newarea = BotReachabilityArea(origin, qtrue); - if (newarea != area) - { - botimport.Print(PRT_MESSAGE, "origin = %f, %f, %f\n", origin[0], origin[1], origin[2]); - area = newarea; - botimport.Print(PRT_MESSAGE, "new area %d, cluster %d, presence type %d\n", - area, AAS_AreaCluster(area), AAS_PointPresenceType(origin)); - botimport.Print(PRT_MESSAGE, "area contents: "); - if (aasworld.areasettings[area].contents & AREACONTENTS_WATER) - { - botimport.Print(PRT_MESSAGE, "water &"); - } //end if - if (aasworld.areasettings[area].contents & AREACONTENTS_LAVA) - { - botimport.Print(PRT_MESSAGE, "lava &"); - } //end if - if (aasworld.areasettings[area].contents & AREACONTENTS_SLIME) - { - botimport.Print(PRT_MESSAGE, "slime &"); - } //end if - if (aasworld.areasettings[area].contents & AREACONTENTS_JUMPPAD) - { - botimport.Print(PRT_MESSAGE, "jump pad &"); - } //end if - if (aasworld.areasettings[area].contents & AREACONTENTS_CLUSTERPORTAL) - { - botimport.Print(PRT_MESSAGE, "cluster portal &"); - } //end if - if (aasworld.areasettings[area].contents & AREACONTENTS_VIEWPORTAL) - { - botimport.Print(PRT_MESSAGE, "view portal &"); - } //end if - if (aasworld.areasettings[area].contents & AREACONTENTS_DONOTENTER) - { - botimport.Print(PRT_MESSAGE, "do not enter &"); - } //end if - if (aasworld.areasettings[area].contents & AREACONTENTS_MOVER) - { - botimport.Print(PRT_MESSAGE, "mover &"); - } //end if - if (!aasworld.areasettings[area].contents) - { - botimport.Print(PRT_MESSAGE, "empty"); - } //end if - botimport.Print(PRT_MESSAGE, "\n"); - botimport.Print(PRT_MESSAGE, "travel time to goal (%d) = %d\n", botlibglobals.goalareanum, - AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT|TFL_ROCKETJUMP)); - /* - VectorCopy(origin, end); - end[2] += 5; - numareas = AAS_TraceAreas(origin, end, areas, NULL, 10); - AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1); - botimport.Print(PRT_MESSAGE, "num areas = %d, area = %d\n", numareas, areas[0]); - */ - /* - botlibglobals.goalareanum = newarea; - VectorCopy(parm2, botlibglobals.goalorigin); - botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n", - origin[0], origin[1], origin[2], newarea); - */ - } //end if - //* - flood = LibVarGetValue("bot_flood"); - if (parm0 & 1) - { - if (flood) - { - AAS_ClearShownPolygons(); - AAS_ClearShownDebugLines(); - AAS_FloodAreas(parm2); - } - else - { - botlibglobals.goalareanum = newarea; - VectorCopy(parm2, botlibglobals.goalorigin); - botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n", - origin[0], origin[1], origin[2], newarea); - } - } //end if*/ - if (flood) - return 0; -// if (parm0 & BUTTON_USE) -// { -// botlibglobals.runai = !botlibglobals.runai; -// if (botlibglobals.runai) botimport.Print(PRT_MESSAGE, "started AI\n"); -// else botimport.Print(PRT_MESSAGE, "stopped AI\n"); - //* / - /* - goal.areanum = botlibglobals.goalareanum; - reachnum = BotGetReachabilityToGoal(parm2, newarea, 1, - ms.avoidreach, ms.avoidreachtimes, - &goal, TFL_DEFAULT); - if (!reachnum) - { - botimport.Print(PRT_MESSAGE, "goal not reachable\n"); - } //end if - else - { - AAS_ReachabilityFromNum(reachnum, &reach); - AAS_ClearShownDebugLines(); - AAS_ShowArea(area, qtrue); - AAS_ShowArea(reach.areanum, qtrue); - AAS_DrawCross(reach.start, 6, LINECOLOR_BLUE); - AAS_DrawCross(reach.end, 6, LINECOLOR_RED); - // - if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) - { - ElevatorBottomCenter(&reach, bottomcenter); - AAS_DrawCross(bottomcenter, 10, LINECOLOR_GREEN); - } //end if - } //end else*/ -// botimport.Print(PRT_MESSAGE, "travel time to goal = %d\n", -// AAS_AreaTravelTimeToGoalArea(area, origin, botlibglobals.goalareanum, TFL_DEFAULT)); -// botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n"); -// AAS_Reachability_WeaponJump(703, 716); -// } //end if*/ - -/* face = AAS_AreaGroundFace(newarea, parm2); - if (face) - { - AAS_ShowFace(face - aasworld.faces); - } //end if*/ - /* - AAS_ClearShownDebugLines(); - AAS_ShowArea(newarea, parm0 & BUTTON_USE); - AAS_ShowReachableAreas(area); - */ - AAS_ClearShownPolygons(); - AAS_ClearShownDebugLines(); - AAS_ShowAreaPolygons(newarea, 1, parm0 & 4); - if (parm0 & 2) AAS_ShowReachableAreas(area); - else - { - static int lastgoalareanum, lastareanum; - static int avoidreach[MAX_AVOIDREACH]; - static float avoidreachtimes[MAX_AVOIDREACH]; - static int avoidreachtries[MAX_AVOIDREACH]; - int reachnum, resultFlags; - bot_goal_t goal; - aas_reachability_t reach; - - /* - goal.areanum = botlibglobals.goalareanum; - VectorCopy(botlibglobals.goalorigin, goal.origin); - reachnum = BotGetReachabilityToGoal(origin, newarea, - lastgoalareanum, lastareanum, - avoidreach, avoidreachtimes, avoidreachtries, - &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, - NULL, 0, &resultFlags); - AAS_ReachabilityFromNum(reachnum, &reach); - AAS_ShowReachability(&reach); - */ - int curarea; - vec3_t curorigin; - - goal.areanum = botlibglobals.goalareanum; - VectorCopy(botlibglobals.goalorigin, goal.origin); - VectorCopy(origin, curorigin); - curarea = newarea; - for ( i = 0; i < 100; i++ ) { - if ( curarea == goal.areanum ) { - break; - } - reachnum = BotGetReachabilityToGoal(curorigin, curarea, - lastgoalareanum, lastareanum, - avoidreach, avoidreachtimes, avoidreachtries, - &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, - NULL, 0, &resultFlags); - AAS_ReachabilityFromNum(reachnum, &reach); - AAS_ShowReachability(&reach); - VectorCopy(reach.end, origin); - lastareanum = curarea; - curarea = reach.areanum; - } - } //end else - VectorClear(forward); - //BotGapDistance(origin, forward, 0); - /* - if (parm0 & BUTTON_USE) - { - botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n"); - AAS_Reachability_WeaponJump(703, 716); - } //end if*/ - - AngleVectors(parm3, forward, right, NULL); - //get the eye 16 units to the right of the origin - VectorMA(parm2, 8, right, eye); - //get the eye 24 units up - eye[2] += 24; - //get the end point for the line to be traced - VectorMA(eye, 800, forward, end); - -// AAS_TestMovementPrediction(1, parm2, forward); -/* - //trace the line to find the hit point - trace = AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1); - if (!line[0]) line[0] = botimport.DebugLineCreate(); - botimport.DebugLineShow(line[0], eye, trace.endpos, LINECOLOR_BLUE); - // - AAS_ClearShownDebugLines(); - if (trace.ent) - { - ent = &aasworld.entities[trace.ent]; - AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); - } //end if -*/ - -/* - start_time = clock(); - for (i = 0; i < 2000; i++) - { - AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); -// AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1); - } //end for - end_time = clock(); - botimport.Print(PRT_MESSAGE, "me %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC); - start_time = clock(); - for (i = 0; i < 2000; i++) - { - AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); - } //end for - end_time = clock(); - botimport.Print(PRT_MESSAGE, "id %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC); -*/ - - // TTimo: nested comments are BAD for gcc -Werror, use #if 0 instead.. -#if 0 - AAS_ClearShownDebugLines(); - //bsptrace = AAS_Trace(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID); - bsptrace = AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); - if (!line[0]) line[0] = botimport.DebugLineCreate(); - botimport.DebugLineShow(line[0], eye, bsptrace.endpos, LINECOLOR_YELLOW); - if (bsptrace.fraction < 1.0) - { - face = AAS_TraceEndFace(&trace); - if (face) - { - AAS_ShowFace(face - aasworld.faces); - } //end if - - AAS_DrawPlaneCross(bsptrace.endpos, - bsptrace.plane.normal, - bsptrace.plane.dist + bsptrace.exp_dist, - bsptrace.plane.type, LINECOLOR_GREEN); - if (trace.ent) - { - ent = &aasworld.entities[trace.ent]; - AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); - } //end if - } //end if - //bsptrace = AAS_Trace2(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID); - bsptrace = AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); - botimport.DebugLineShow(line[1], eye, bsptrace.endpos, LINECOLOR_BLUE); - if (bsptrace.fraction < 1.0) - { - AAS_DrawPlaneCross(bsptrace.endpos, - bsptrace.plane.normal, - bsptrace.plane.dist,// + bsptrace.exp_dist, - bsptrace.plane.type, LINECOLOR_RED); - if (bsptrace.ent) - { - ent = &aasworld.entities[bsptrace.ent]; - AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); - } //end if - } //end if -#endif -#endif - return 0; -} //end of the function BotExportTest - - -/* -============ -Init_AAS_Export -============ -*/ -static void Init_AAS_Export( aas_export_t *aas ) { - //-------------------------------------------- - // be_aas_entity.c - //-------------------------------------------- - aas->AAS_EntityInfo = AAS_EntityInfo; - //-------------------------------------------- - // be_aas_main.c - //-------------------------------------------- - aas->AAS_Initialized = AAS_Initialized; - aas->AAS_PresenceTypeBoundingBox = AAS_PresenceTypeBoundingBox; - aas->AAS_Time = AAS_Time; - //-------------------------------------------- - // be_aas_sample.c - //-------------------------------------------- - aas->AAS_PointAreaNum = AAS_PointAreaNum; - aas->AAS_PointReachabilityAreaIndex = AAS_PointReachabilityAreaIndex; - aas->AAS_TraceAreas = AAS_TraceAreas; - aas->AAS_BBoxAreas = AAS_BBoxAreas; - aas->AAS_AreaInfo = AAS_AreaInfo; - //-------------------------------------------- - // be_aas_bspq3.c - //-------------------------------------------- - aas->AAS_PointContents = AAS_PointContents; - aas->AAS_NextBSPEntity = AAS_NextBSPEntity; - aas->AAS_ValueForBSPEpairKey = AAS_ValueForBSPEpairKey; - aas->AAS_VectorForBSPEpairKey = AAS_VectorForBSPEpairKey; - aas->AAS_FloatForBSPEpairKey = AAS_FloatForBSPEpairKey; - aas->AAS_IntForBSPEpairKey = AAS_IntForBSPEpairKey; - //-------------------------------------------- - // be_aas_reach.c - //-------------------------------------------- - aas->AAS_AreaReachability = AAS_AreaReachability; - //-------------------------------------------- - // be_aas_route.c - //-------------------------------------------- - aas->AAS_AreaTravelTimeToGoalArea = AAS_AreaTravelTimeToGoalArea; - aas->AAS_EnableRoutingArea = AAS_EnableRoutingArea; - aas->AAS_PredictRoute = AAS_PredictRoute; - //-------------------------------------------- - // be_aas_altroute.c - //-------------------------------------------- - aas->AAS_AlternativeRouteGoals = AAS_AlternativeRouteGoals; - //-------------------------------------------- - // be_aas_move.c - //-------------------------------------------- - aas->AAS_Swimming = AAS_Swimming; - aas->AAS_PredictClientMovement = AAS_PredictClientMovement; -} - - -/* -============ -Init_EA_Export -============ -*/ -static void Init_EA_Export( ea_export_t *ea ) { - //ClientCommand elementary actions - ea->EA_Command = EA_Command; - ea->EA_Say = EA_Say; - ea->EA_SayTeam = EA_SayTeam; - - ea->EA_Action = EA_Action; - ea->EA_Gesture = EA_Gesture; - ea->EA_Talk = EA_Talk; - ea->EA_Attack = EA_Attack; - ea->EA_Use = EA_Use; - ea->EA_Respawn = EA_Respawn; - ea->EA_Crouch = EA_Crouch; - ea->EA_MoveUp = EA_MoveUp; - ea->EA_MoveDown = EA_MoveDown; - ea->EA_MoveForward = EA_MoveForward; - ea->EA_MoveBack = EA_MoveBack; - ea->EA_MoveLeft = EA_MoveLeft; - ea->EA_MoveRight = EA_MoveRight; - - ea->EA_SelectWeapon = EA_SelectWeapon; - ea->EA_Jump = EA_Jump; - ea->EA_DelayedJump = EA_DelayedJump; - ea->EA_Move = EA_Move; - ea->EA_View = EA_View; - ea->EA_GetInput = EA_GetInput; - ea->EA_EndRegular = EA_EndRegular; - ea->EA_ResetInput = EA_ResetInput; -} - - -/* -============ -Init_AI_Export -============ -*/ -static void Init_AI_Export( ai_export_t *ai ) { - //----------------------------------- - // be_ai_char.h - //----------------------------------- - ai->BotLoadCharacter = BotLoadCharacter; - ai->BotFreeCharacter = BotFreeCharacter; - ai->Characteristic_Float = Characteristic_Float; - ai->Characteristic_BFloat = Characteristic_BFloat; - ai->Characteristic_Integer = Characteristic_Integer; - ai->Characteristic_BInteger = Characteristic_BInteger; - ai->Characteristic_String = Characteristic_String; - //----------------------------------- - // be_ai_chat.h - //----------------------------------- - ai->BotAllocChatState = BotAllocChatState; - ai->BotFreeChatState = BotFreeChatState; - ai->BotQueueConsoleMessage = BotQueueConsoleMessage; - ai->BotRemoveConsoleMessage = BotRemoveConsoleMessage; - ai->BotNextConsoleMessage = BotNextConsoleMessage; - ai->BotNumConsoleMessages = BotNumConsoleMessages; - ai->BotInitialChat = BotInitialChat; - ai->BotNumInitialChats = BotNumInitialChats; - ai->BotReplyChat = BotReplyChat; - ai->BotChatLength = BotChatLength; - ai->BotEnterChat = BotEnterChat; - ai->BotGetChatMessage = BotGetChatMessage; - ai->StringContains = StringContains; - ai->BotFindMatch = BotFindMatch; - ai->BotMatchVariable = BotMatchVariable; - ai->UnifyWhiteSpaces = UnifyWhiteSpaces; - ai->BotReplaceSynonyms = BotReplaceSynonyms; - ai->BotLoadChatFile = BotLoadChatFile; - ai->BotSetChatGender = BotSetChatGender; - ai->BotSetChatName = BotSetChatName; - //----------------------------------- - // be_ai_goal.h - //----------------------------------- - ai->BotResetGoalState = BotResetGoalState; - ai->BotResetAvoidGoals = BotResetAvoidGoals; - ai->BotRemoveFromAvoidGoals = BotRemoveFromAvoidGoals; - ai->BotPushGoal = BotPushGoal; - ai->BotPopGoal = BotPopGoal; - ai->BotEmptyGoalStack = BotEmptyGoalStack; - ai->BotDumpAvoidGoals = BotDumpAvoidGoals; - ai->BotDumpGoalStack = BotDumpGoalStack; - ai->BotGoalName = BotGoalName; - ai->BotGetTopGoal = BotGetTopGoal; - ai->BotGetSecondGoal = BotGetSecondGoal; - ai->BotChooseLTGItem = BotChooseLTGItem; - ai->BotChooseNBGItem = BotChooseNBGItem; - ai->BotTouchingGoal = BotTouchingGoal; - ai->BotItemGoalInVisButNotVisible = BotItemGoalInVisButNotVisible; - ai->BotGetLevelItemGoal = BotGetLevelItemGoal; - ai->BotGetNextCampSpotGoal = BotGetNextCampSpotGoal; - ai->BotGetMapLocationGoal = BotGetMapLocationGoal; - ai->BotAvoidGoalTime = BotAvoidGoalTime; - ai->BotSetAvoidGoalTime = BotSetAvoidGoalTime; - ai->BotInitLevelItems = BotInitLevelItems; - ai->BotUpdateEntityItems = BotUpdateEntityItems; - ai->BotLoadItemWeights = BotLoadItemWeights; - ai->BotFreeItemWeights = BotFreeItemWeights; - ai->BotInterbreedGoalFuzzyLogic = BotInterbreedGoalFuzzyLogic; - ai->BotSaveGoalFuzzyLogic = BotSaveGoalFuzzyLogic; - ai->BotMutateGoalFuzzyLogic = BotMutateGoalFuzzyLogic; - ai->BotAllocGoalState = BotAllocGoalState; - ai->BotFreeGoalState = BotFreeGoalState; - //----------------------------------- - // be_ai_move.h - //----------------------------------- - ai->BotResetMoveState = BotResetMoveState; - ai->BotMoveToGoal = BotMoveToGoal; - ai->BotMoveInDirection = BotMoveInDirection; - ai->BotResetAvoidReach = BotResetAvoidReach; - ai->BotResetLastAvoidReach = BotResetLastAvoidReach; - ai->BotReachabilityArea = BotReachabilityArea; - ai->BotMovementViewTarget = BotMovementViewTarget; - ai->BotPredictVisiblePosition = BotPredictVisiblePosition; - ai->BotAllocMoveState = BotAllocMoveState; - ai->BotFreeMoveState = BotFreeMoveState; - ai->BotInitMoveState = BotInitMoveState; - ai->BotAddAvoidSpot = BotAddAvoidSpot; - //----------------------------------- - // be_ai_weap.h - //----------------------------------- - ai->BotChooseBestFightWeapon = BotChooseBestFightWeapon; - ai->BotGetWeaponInfo = BotGetWeaponInfo; - ai->BotLoadWeaponWeights = BotLoadWeaponWeights; - ai->BotAllocWeaponState = BotAllocWeaponState; - ai->BotFreeWeaponState = BotFreeWeaponState; - ai->BotResetWeaponState = BotResetWeaponState; - //----------------------------------- - // be_ai_gen.h - //----------------------------------- - ai->GeneticParentsAndChildSelection = GeneticParentsAndChildSelection; -} - - -/* -============ -GetBotLibAPI -============ -*/ -botlib_export_t *GetBotLibAPI(int apiVersion, botlib_import_t *import) { - assert(import); // bk001129 - this wasn't set for baseq3/ - botimport = *import; - assert(botimport.Print); // bk001129 - pars pro toto - - Com_Memset( &be_botlib_export, 0, sizeof( be_botlib_export ) ); - - if ( apiVersion != BOTLIB_API_VERSION ) { - botimport.Print( PRT_ERROR, "Mismatched BOTLIB_API_VERSION: expected %i, got %i\n", BOTLIB_API_VERSION, apiVersion ); - return NULL; - } - - Init_AAS_Export(&be_botlib_export.aas); - Init_EA_Export(&be_botlib_export.ea); - Init_AI_Export(&be_botlib_export.ai); - - be_botlib_export.BotLibSetup = Export_BotLibSetup; - be_botlib_export.BotLibShutdown = Export_BotLibShutdown; - be_botlib_export.BotLibVarSet = Export_BotLibVarSet; - be_botlib_export.BotLibVarGet = Export_BotLibVarGet; - - be_botlib_export.PC_AddGlobalDefine = PC_AddGlobalDefine; - be_botlib_export.PC_LoadSourceHandle = PC_LoadSourceHandle; - be_botlib_export.PC_FreeSourceHandle = PC_FreeSourceHandle; - be_botlib_export.PC_ReadTokenHandle = PC_ReadTokenHandle; - be_botlib_export.PC_SourceFileAndLine = PC_SourceFileAndLine; - - be_botlib_export.BotLibStartFrame = Export_BotLibStartFrame; - be_botlib_export.BotLibLoadMap = Export_BotLibLoadMap; - be_botlib_export.BotLibUpdateEntity = Export_BotLibUpdateEntity; - be_botlib_export.Test = BotExportTest; - - return &be_botlib_export; -} diff --git a/tools/quake3/bspc/deps/botlib/be_interface.h b/tools/quake3/bspc/deps/botlib/be_interface.h deleted file mode 100644 index c3cdf085..00000000 --- a/tools/quake3/bspc/deps/botlib/be_interface.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: be_interface.h - * - * desc: botlib interface - * - * $Archive: /source/code/botlib/be_interface.h $ - * - *****************************************************************************/ - -//#define DEBUG //debug code -#define RANDOMIZE //randomize bot behaviour - -//FIXME: get rid of this global structure -typedef struct botlib_globals_s -{ - int botlibsetup; //true when the bot library has been setup - int maxentities; //maximum number of entities - int maxclients; //maximum number of clients - float time; //the global time -#ifdef DEBUG - qboolean debug; //true if debug is on - int goalareanum; - vec3_t goalorigin; - int runai; -#endif -} botlib_globals_t; - - -extern botlib_globals_t botlibglobals; -extern botlib_import_t botimport; -extern int botDeveloper; //true if developer is on - -// -int Sys_MilliSeconds(void); - diff --git a/tools/quake3/bspc/deps/botlib/botlib.h b/tools/quake3/bspc/deps/botlib/botlib.h deleted file mode 100644 index 5fb18554..00000000 --- a/tools/quake3/bspc/deps/botlib/botlib.h +++ /dev/null @@ -1,516 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// -/***************************************************************************** - * name: botlib.h - * - * desc: bot AI library - * - * $Archive: /source/code/game/botai.h $ - * - *****************************************************************************/ - -#define BOTLIB_API_VERSION 2 - -struct aas_clientmove_s; -struct aas_entityinfo_s; -struct aas_areainfo_s; -struct aas_altroutegoal_s; -struct aas_predictroute_s; -struct bot_consolemessage_s; -struct bot_match_s; -struct bot_goal_s; -struct bot_moveresult_s; -struct bot_initmove_s; -struct weaponinfo_s; - -#define BOTFILESBASEFOLDER "botfiles" -//debug line colors -#define LINECOLOR_NONE -1 -#define LINECOLOR_RED 1//0xf2f2f0f0L -#define LINECOLOR_GREEN 2//0xd0d1d2d3L -#define LINECOLOR_BLUE 3//0xf3f3f1f1L -#define LINECOLOR_YELLOW 4//0xdcdddedfL -#define LINECOLOR_ORANGE 5//0xe0e1e2e3L - -//Print types -#define PRT_MESSAGE 1 -#define PRT_WARNING 2 -#define PRT_ERROR 3 -#define PRT_FATAL 4 -#define PRT_EXIT 5 - -//console message types -#define CMS_NORMAL 0 -#define CMS_CHAT 1 - -//botlib error codes -#define BLERR_NOERROR 0 //no error -#define BLERR_LIBRARYNOTSETUP 1 //library not setup -#define BLERR_INVALIDENTITYNUMBER 2 //invalid entity number -#define BLERR_NOAASFILE 3 //no AAS file available -#define BLERR_CANNOTOPENAASFILE 4 //cannot open AAS file -#define BLERR_WRONGAASFILEID 5 //incorrect AAS file id -#define BLERR_WRONGAASFILEVERSION 6 //incorrect AAS file version -#define BLERR_CANNOTREADAASLUMP 7 //cannot read AAS file lump -#define BLERR_CANNOTLOADICHAT 8 //cannot load initial chats -#define BLERR_CANNOTLOADITEMWEIGHTS 9 //cannot load item weights -#define BLERR_CANNOTLOADITEMCONFIG 10 //cannot load item config -#define BLERR_CANNOTLOADWEAPONWEIGHTS 11 //cannot load weapon weights -#define BLERR_CANNOTLOADWEAPONCONFIG 12 //cannot load weapon config - -//action flags -#define ACTION_ATTACK 0x0000001 -#define ACTION_USE 0x0000002 -#define ACTION_RESPAWN 0x0000008 -#define ACTION_JUMP 0x0000010 -#define ACTION_MOVEUP 0x0000020 -#define ACTION_CROUCH 0x0000080 -#define ACTION_MOVEDOWN 0x0000100 -#define ACTION_MOVEFORWARD 0x0000200 -#define ACTION_MOVEBACK 0x0000800 -#define ACTION_MOVELEFT 0x0001000 -#define ACTION_MOVERIGHT 0x0002000 -#define ACTION_DELAYEDJUMP 0x0008000 -#define ACTION_TALK 0x0010000 -#define ACTION_GESTURE 0x0020000 -#define ACTION_WALK 0x0080000 -#define ACTION_AFFIRMATIVE 0x0100000 -#define ACTION_NEGATIVE 0x0200000 -#define ACTION_GETFLAG 0x0800000 -#define ACTION_GUARDBASE 0x1000000 -#define ACTION_PATROL 0x2000000 -#define ACTION_FOLLOWME 0x8000000 - -//the bot input, will be converted to an usercmd_t -typedef struct bot_input_s -{ - float thinktime; //time since last output (in seconds) - vec3_t dir; //movement direction - float speed; //speed in the range [0, 400] - vec3_t viewangles; //the view angles - int actionflags; //one of the ACTION_? flags - int weapon; //weapon to use -} bot_input_t; - -#ifndef BSPTRACE - -#define BSPTRACE - -//bsp_trace_t hit surface -typedef struct bsp_surface_s -{ - char name[16]; - int flags; - int value; -} bsp_surface_t; - -//remove the bsp_trace_s structure definition l8r on -//a trace is returned when a box is swept through the world -typedef struct bsp_trace_s -{ - qboolean allsolid; // if true, plane is not valid - qboolean startsolid; // if true, the initial point was in a solid area - float fraction; // time completed, 1.0 = didn't hit anything - vec3_t endpos; // final position - cplane_t plane; // surface normal at impact - float exp_dist; // expanded plane distance - int sidenum; // number of the brush side hit - bsp_surface_t surface; // the hit point surface - int contents; // contents on other side of surface hit - int ent; // number of entity hit -} bsp_trace_t; - -#endif // BSPTRACE - -//entity state -typedef struct bot_entitystate_s -{ - int type; // entity type - int flags; // entity flags - vec3_t origin; // origin of the entity - vec3_t angles; // angles of the model - vec3_t old_origin; // for lerping - vec3_t mins; // bounding box minimums - vec3_t maxs; // bounding box maximums - int groundent; // ground entity - int solid; // solid type - int modelindex; // model used - int modelindex2; // weapons, CTF flags, etc - int frame; // model frame number - int event; // impulse events -- muzzle flashes, footsteps, etc - int eventParm; // even parameter - int powerups; // bit flags - int weapon; // determines weapon and flash model, etc - int legsAnim; // mask off ANIM_TOGGLEBIT - int torsoAnim; // mask off ANIM_TOGGLEBIT -} bot_entitystate_t; - -//bot AI library exported functions -typedef struct botlib_import_s -{ - //print messages from the bot library - void (QDECL *Print)(int type, char *fmt, ...); - //trace a bbox through the world - void (*Trace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask); - //trace a bbox against a specific entity - void (*EntityTrace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask); - //retrieve the contents at the given point - int (*PointContents)(vec3_t point); - //check if the point is in potential visible sight - int (*inPVS)(vec3_t p1, vec3_t p2); - //retrieve the BSP entity data lump - char *(*BSPEntityData)(void); - // - void (*BSPModelMinsMaxsOrigin)(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin); - //send a bot client command - void (*BotClientCommand)(int client, char *command); - //memory allocation - void *(*GetMemory)(int size); // allocate from Zone - void (*FreeMemory)(void *ptr); // free memory from Zone - int (*AvailableMemory)(void); // available Zone memory - void *(*HunkAlloc)(int size); // allocate from hunk - //file system access - int (*FS_FOpenFile)( const char *qpath, fileHandle_t *file, fsMode_t mode ); - int (*FS_Read)( void *buffer, int len, fileHandle_t f ); - int (*FS_Write)( const void *buffer, int len, fileHandle_t f ); - void (*FS_FCloseFile)( fileHandle_t f ); - int (*FS_Seek)( fileHandle_t f, long offset, int origin ); - //debug visualisation stuff - int (*DebugLineCreate)(void); - void (*DebugLineDelete)(int line); - void (*DebugLineShow)(int line, vec3_t start, vec3_t end, int color); - // - int (*DebugPolygonCreate)(int color, int numPoints, vec3_t *points); - void (*DebugPolygonDelete)(int id); -} botlib_import_t; - -typedef struct aas_export_s -{ - //----------------------------------- - // be_aas_entity.h - //----------------------------------- - void (*AAS_EntityInfo)(int entnum, struct aas_entityinfo_s *info); - //----------------------------------- - // be_aas_main.h - //----------------------------------- - int (*AAS_Initialized)(void); - void (*AAS_PresenceTypeBoundingBox)(int presencetype, vec3_t mins, vec3_t maxs); - float (*AAS_Time)(void); - //-------------------------------------------- - // be_aas_sample.c - //-------------------------------------------- - int (*AAS_PointAreaNum)(vec3_t point); - int (*AAS_PointReachabilityAreaIndex)( vec3_t point ); - int (*AAS_TraceAreas)(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); - int (*AAS_BBoxAreas)(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas); - int (*AAS_AreaInfo)( int areanum, struct aas_areainfo_s *info ); - //-------------------------------------------- - // be_aas_bspq3.c - //-------------------------------------------- - int (*AAS_PointContents)(vec3_t point); - int (*AAS_NextBSPEntity)(int ent); - int (*AAS_ValueForBSPEpairKey)(int ent, char *key, char *value, int size); - int (*AAS_VectorForBSPEpairKey)(int ent, char *key, vec3_t v); - int (*AAS_FloatForBSPEpairKey)(int ent, char *key, float *value); - int (*AAS_IntForBSPEpairKey)(int ent, char *key, int *value); - //-------------------------------------------- - // be_aas_reach.c - //-------------------------------------------- - int (*AAS_AreaReachability)(int areanum); - //-------------------------------------------- - // be_aas_route.c - //-------------------------------------------- - int (*AAS_AreaTravelTimeToGoalArea)(int areanum, vec3_t origin, int goalareanum, int travelflags); - int (*AAS_EnableRoutingArea)(int areanum, int enable); - int (*AAS_PredictRoute)(struct aas_predictroute_s *route, int areanum, vec3_t origin, - int goalareanum, int travelflags, int maxareas, int maxtime, - int stopevent, int stopcontents, int stoptfl, int stopareanum); - //-------------------------------------------- - // be_aas_altroute.c - //-------------------------------------------- - int (*AAS_AlternativeRouteGoals)(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, - struct aas_altroutegoal_s *altroutegoals, int maxaltroutegoals, - int type); - //-------------------------------------------- - // be_aas_move.c - //-------------------------------------------- - int (*AAS_Swimming)(vec3_t origin); - int (*AAS_PredictClientMovement)(struct aas_clientmove_s *move, - int entnum, vec3_t origin, - int presencetype, int onground, - vec3_t velocity, vec3_t cmdmove, - int cmdframes, - int maxframes, float frametime, - int stopevent, int stopareanum, int visualize); -} aas_export_t; - -typedef struct ea_export_s -{ - //ClientCommand elementary actions - void (*EA_Command)(int client, char *command ); - void (*EA_Say)(int client, char *str); - void (*EA_SayTeam)(int client, char *str); - // - void (*EA_Action)(int client, int action); - void (*EA_Gesture)(int client); - void (*EA_Talk)(int client); - void (*EA_Attack)(int client); - void (*EA_Use)(int client); - void (*EA_Respawn)(int client); - void (*EA_MoveUp)(int client); - void (*EA_MoveDown)(int client); - void (*EA_MoveForward)(int client); - void (*EA_MoveBack)(int client); - void (*EA_MoveLeft)(int client); - void (*EA_MoveRight)(int client); - void (*EA_Crouch)(int client); - - void (*EA_SelectWeapon)(int client, int weapon); - void (*EA_Jump)(int client); - void (*EA_DelayedJump)(int client); - void (*EA_Move)(int client, vec3_t dir, float speed); - void (*EA_View)(int client, vec3_t viewangles); - //send regular input to the server - void (*EA_EndRegular)(int client, float thinktime); - void (*EA_GetInput)(int client, float thinktime, bot_input_t *input); - void (*EA_ResetInput)(int client); -} ea_export_t; - -typedef struct ai_export_s -{ - //----------------------------------- - // be_ai_char.h - //----------------------------------- - int (*BotLoadCharacter)(char *charfile, float skill); - void (*BotFreeCharacter)(int character); - float (*Characteristic_Float)(int character, int index); - float (*Characteristic_BFloat)(int character, int index, float min, float max); - int (*Characteristic_Integer)(int character, int index); - int (*Characteristic_BInteger)(int character, int index, int min, int max); - void (*Characteristic_String)(int character, int index, char *buf, int size); - //----------------------------------- - // be_ai_chat.h - //----------------------------------- - int (*BotAllocChatState)(void); - void (*BotFreeChatState)(int handle); - void (*BotQueueConsoleMessage)(int chatstate, int type, char *message); - void (*BotRemoveConsoleMessage)(int chatstate, int handle); - int (*BotNextConsoleMessage)(int chatstate, struct bot_consolemessage_s *cm); - int (*BotNumConsoleMessages)(int chatstate); - void (*BotInitialChat)(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); - int (*BotNumInitialChats)(int chatstate, char *type); - int (*BotReplyChat)(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); - int (*BotChatLength)(int chatstate); - void (*BotEnterChat)(int chatstate, int client, int sendto); - void (*BotGetChatMessage)(int chatstate, char *buf, int size); - int (*StringContains)(char *str1, char *str2, int casesensitive); - int (*BotFindMatch)(char *str, struct bot_match_s *match, unsigned long int context); - void (*BotMatchVariable)(struct bot_match_s *match, int variable, char *buf, int size); - void (*UnifyWhiteSpaces)(char *string); - void (*BotReplaceSynonyms)(char *string, unsigned long int context); - int (*BotLoadChatFile)(int chatstate, char *chatfile, char *chatname); - void (*BotSetChatGender)(int chatstate, int gender); - void (*BotSetChatName)(int chatstate, char *name, int client); - //----------------------------------- - // be_ai_goal.h - //----------------------------------- - void (*BotResetGoalState)(int goalstate); - void (*BotResetAvoidGoals)(int goalstate); - void (*BotRemoveFromAvoidGoals)(int goalstate, int number); - void (*BotPushGoal)(int goalstate, struct bot_goal_s *goal); - void (*BotPopGoal)(int goalstate); - void (*BotEmptyGoalStack)(int goalstate); - void (*BotDumpAvoidGoals)(int goalstate); - void (*BotDumpGoalStack)(int goalstate); - void (*BotGoalName)(int number, char *name, int size); - int (*BotGetTopGoal)(int goalstate, struct bot_goal_s *goal); - int (*BotGetSecondGoal)(int goalstate, struct bot_goal_s *goal); - int (*BotChooseLTGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags); - int (*BotChooseNBGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags, - struct bot_goal_s *ltg, float maxtime); - int (*BotTouchingGoal)(vec3_t origin, struct bot_goal_s *goal); - int (*BotItemGoalInVisButNotVisible)(int viewer, vec3_t eye, vec3_t viewangles, struct bot_goal_s *goal); - int (*BotGetLevelItemGoal)(int index, char *classname, struct bot_goal_s *goal); - int (*BotGetNextCampSpotGoal)(int num, struct bot_goal_s *goal); - int (*BotGetMapLocationGoal)(char *name, struct bot_goal_s *goal); - float (*BotAvoidGoalTime)(int goalstate, int number); - void (*BotSetAvoidGoalTime)(int goalstate, int number, float avoidtime); - void (*BotInitLevelItems)(void); - void (*BotUpdateEntityItems)(void); - int (*BotLoadItemWeights)(int goalstate, char *filename); - void (*BotFreeItemWeights)(int goalstate); - void (*BotInterbreedGoalFuzzyLogic)(int parent1, int parent2, int child); - void (*BotSaveGoalFuzzyLogic)(int goalstate, char *filename); - void (*BotMutateGoalFuzzyLogic)(int goalstate, float range); - int (*BotAllocGoalState)(int client); - void (*BotFreeGoalState)(int handle); - //----------------------------------- - // be_ai_move.h - //----------------------------------- - void (*BotResetMoveState)(int movestate); - void (*BotMoveToGoal)(struct bot_moveresult_s *result, int movestate, struct bot_goal_s *goal, int travelflags); - int (*BotMoveInDirection)(int movestate, vec3_t dir, float speed, int type); - void (*BotResetAvoidReach)(int movestate); - void (*BotResetLastAvoidReach)(int movestate); - int (*BotReachabilityArea)(vec3_t origin, int testground); - int (*BotMovementViewTarget)(int movestate, struct bot_goal_s *goal, int travelflags, float lookahead, vec3_t target); - int (*BotPredictVisiblePosition)(vec3_t origin, int areanum, struct bot_goal_s *goal, int travelflags, vec3_t target); - int (*BotAllocMoveState)(void); - void (*BotFreeMoveState)(int handle); - void (*BotInitMoveState)(int handle, struct bot_initmove_s *initmove); - void (*BotAddAvoidSpot)(int movestate, vec3_t origin, float radius, int type); - //----------------------------------- - // be_ai_weap.h - //----------------------------------- - int (*BotChooseBestFightWeapon)(int weaponstate, int *inventory); - void (*BotGetWeaponInfo)(int weaponstate, int weapon, struct weaponinfo_s *weaponinfo); - int (*BotLoadWeaponWeights)(int weaponstate, char *filename); - int (*BotAllocWeaponState)(void); - void (*BotFreeWeaponState)(int weaponstate); - void (*BotResetWeaponState)(int weaponstate); - //----------------------------------- - // be_ai_gen.h - //----------------------------------- - int (*GeneticParentsAndChildSelection)(int numranks, float *ranks, int *parent1, int *parent2, int *child); -} ai_export_t; - -//bot AI library imported functions -typedef struct botlib_export_s -{ - //Area Awareness System functions - aas_export_t aas; - //Elementary Action functions - ea_export_t ea; - //AI functions - ai_export_t ai; - //setup the bot library, returns BLERR_ - int (*BotLibSetup)(void); - //shutdown the bot library, returns BLERR_ - int (*BotLibShutdown)(void); - //sets a library variable returns BLERR_ - int (*BotLibVarSet)(char *var_name, char *value); - //gets a library variable returns BLERR_ - int (*BotLibVarGet)(char *var_name, char *value, int size); - - //sets a C-like define returns BLERR_ - int (*PC_AddGlobalDefine)(char *string); - int (*PC_LoadSourceHandle)(const char *filename); - int (*PC_FreeSourceHandle)(int handle); - int (*PC_ReadTokenHandle)(int handle, pc_token_t *pc_token); - int (*PC_SourceFileAndLine)(int handle, char *filename, int *line); - - //start a frame in the bot library - int (*BotLibStartFrame)(float time); - //load a new map in the bot library - int (*BotLibLoadMap)(const char *mapname); - //entity updates - int (*BotLibUpdateEntity)(int ent, bot_entitystate_t *state); - //just for testing - int (*Test)(int parm0, char *parm1, vec3_t parm2, vec3_t parm3); -} botlib_export_t; - -//linking of bot library -botlib_export_t *GetBotLibAPI( int apiVersion, botlib_import_t *import ); - -/* Library variables: - -name: default: module(s): description: - -"basedir" "" l_utils.c base directory -"gamedir" "" l_utils.c game directory -"cddir" "" l_utils.c CD directory - -"log" "0" l_log.c enable/disable creating a log file -"maxclients" "4" be_interface.c maximum number of clients -"maxentities" "1024" be_interface.c maximum number of entities -"bot_developer" "0" be_interface.c bot developer mode (it's "botDeveloper" in C to prevent symbol clash). - -"phys_friction" "6" be_aas_move.c ground friction -"phys_stopspeed" "100" be_aas_move.c stop speed -"phys_gravity" "800" be_aas_move.c gravity value -"phys_waterfriction" "1" be_aas_move.c water friction -"phys_watergravity" "400" be_aas_move.c gravity in water -"phys_maxvelocity" "320" be_aas_move.c maximum velocity -"phys_maxwalkvelocity" "320" be_aas_move.c maximum walk velocity -"phys_maxcrouchvelocity" "100" be_aas_move.c maximum crouch velocity -"phys_maxswimvelocity" "150" be_aas_move.c maximum swim velocity -"phys_walkaccelerate" "10" be_aas_move.c walk acceleration -"phys_airaccelerate" "1" be_aas_move.c air acceleration -"phys_swimaccelerate" "4" be_aas_move.c swim acceleration -"phys_maxstep" "18" be_aas_move.c maximum step height -"phys_maxsteepness" "0.7" be_aas_move.c maximum floor steepness -"phys_maxbarrier" "32" be_aas_move.c maximum barrier height -"phys_maxwaterjump" "19" be_aas_move.c maximum waterjump height -"phys_jumpvel" "270" be_aas_move.c jump z velocity -"phys_falldelta5" "40" be_aas_move.c -"phys_falldelta10" "60" be_aas_move.c -"rs_waterjump" "400" be_aas_move.c -"rs_teleport" "50" be_aas_move.c -"rs_barrierjump" "100" be_aas_move.c -"rs_startcrouch" "300" be_aas_move.c -"rs_startgrapple" "500" be_aas_move.c -"rs_startwalkoffledge" "70" be_aas_move.c -"rs_startjump" "300" be_aas_move.c -"rs_rocketjump" "500" be_aas_move.c -"rs_bfgjump" "500" be_aas_move.c -"rs_jumppad" "250" be_aas_move.c -"rs_aircontrolledjumppad" "300" be_aas_move.c -"rs_funcbob" "300" be_aas_move.c -"rs_startelevator" "50" be_aas_move.c -"rs_falldamage5" "300" be_aas_move.c -"rs_falldamage10" "500" be_aas_move.c -"rs_maxjumpfallheight" "450" be_aas_move.c - -"max_aaslinks" "4096" be_aas_sample.c maximum links in the AAS -"max_routingcache" "4096" be_aas_route.c maximum routing cache size in KB -"forceclustering" "0" be_aas_main.c force recalculation of clusters -"forcereachability" "0" be_aas_main.c force recalculation of reachabilities -"forcewrite" "0" be_aas_main.c force writing of aas file -"aasoptimize" "0" be_aas_main.c enable aas optimization -"sv_mapChecksum" "0" be_aas_main.c BSP file checksum -"bot_visualizejumppads" "0" be_aas_reach.c visualize jump pads - -"bot_reloadcharacters" "0" - reload bot character files -"ai_gametype" "0" be_ai_goal.c game type -"droppedweight" "1000" be_ai_goal.c additional dropped item weight -"weapindex_rocketlauncher" "5" be_ai_move.c rl weapon index for rocket jumping -"weapindex_bfg10k" "9" be_ai_move.c bfg weapon index for bfg jumping -"weapindex_grapple" "10" be_ai_move.c grapple weapon index for grappling -"entitytypemissile" "3" be_ai_move.c ET_MISSILE -"offhandgrapple" "0" be_ai_move.c enable off hand grapple hook -"cmd_grappleon" "grappleon" be_ai_move.c command to activate off hand grapple -"cmd_grappleoff" "grappleoff" be_ai_move.c command to deactivate off hand grapple -"itemconfig" "items.c" be_ai_goal.c item configuration file -"weaponconfig" "weapons.c" be_ai_weap.c weapon configuration file -"synfile" "syn.c" be_ai_chat.c file with synonyms -"rndfile" "rnd.c" be_ai_chat.c file with random strings -"matchfile" "match.c" be_ai_chat.c file with match strings -"nochat" "0" be_ai_chat.c disable chats -"max_messages" "1024" be_ai_chat.c console message heap size -"max_weaponinfo" "32" be_ai_weap.c maximum number of weapon info -"max_projectileinfo" "32" be_ai_weap.c maximum number of projectile info -"max_iteminfo" "256" be_ai_goal.c maximum number of item info -"max_levelitems" "256" be_ai_goal.c maximum number of level items - -*/ - diff --git a/tools/quake3/bspc/deps/botlib/l_crc.c b/tools/quake3/bspc/deps/botlib/l_crc.c deleted file mode 100644 index 837c6196..00000000 --- a/tools/quake3/bspc/deps/botlib/l_crc.c +++ /dev/null @@ -1,151 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_crc.c - * - * desc: CRC calculation - * - * $Archive: /MissionPack/CODE/botlib/l_crc.c $ - * - *****************************************************************************/ - -#include -#include -#include - -#include "qcommon/q_shared.h" -#include "botlib.h" -#include "be_interface.h" //for botimport.Print - - -// FIXME: byte swap? - -// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 -// and the initial and final xor values shown below... in other words, the -// CCITT standard CRC used by XMODEM - -#define CRC_INIT_VALUE 0xffff -#define CRC_XOR_VALUE 0x0000 - -unsigned short crctable[257] = -{ - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 -}; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CRC_Init(unsigned short *crcvalue) -{ - *crcvalue = CRC_INIT_VALUE; -} //end of the function CRC_Init -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CRC_ProcessByte(unsigned short *crcvalue, byte data) -{ - *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; -} //end of the function CRC_ProcessByte -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -unsigned short CRC_Value(unsigned short crcvalue) -{ - return crcvalue ^ CRC_XOR_VALUE; -} //end of the function CRC_Value -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -unsigned short CRC_ProcessString(unsigned char *data, int length) -{ - unsigned short crcvalue; - int i, ind; - - CRC_Init(&crcvalue); - - for (i = 0; i < length; i++) - { - ind = (crcvalue >> 8) ^ data[i]; - if (ind < 0 || ind > 256) ind = 0; - crcvalue = (crcvalue << 8) ^ crctable[ind]; - } //end for - return CRC_Value(crcvalue); -} //end of the function CRC_ProcessString -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CRC_ContinueProcessString(unsigned short *crc, char *data, int length) -{ - int i; - - for (i = 0; i < length; i++) - { - *crc = (*crc << 8) ^ crctable[(*crc >> 8) ^ data[i]]; - } //end for -} //end of the function CRC_ProcessString diff --git a/tools/quake3/bspc/deps/botlib/l_crc.h b/tools/quake3/bspc/deps/botlib/l_crc.h deleted file mode 100644 index f9c7e379..00000000 --- a/tools/quake3/bspc/deps/botlib/l_crc.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -typedef unsigned short crc_t; - -void CRC_Init(unsigned short *crcvalue); -void CRC_ProcessByte(unsigned short *crcvalue, byte data); -unsigned short CRC_Value(unsigned short crcvalue); -unsigned short CRC_ProcessString(unsigned char *data, int length); -void CRC_ContinueProcessString(unsigned short *crc, char *data, int length); diff --git a/tools/quake3/bspc/deps/botlib/l_libvar.c b/tools/quake3/bspc/deps/botlib/l_libvar.c deleted file mode 100644 index 4fccdba2..00000000 --- a/tools/quake3/bspc/deps/botlib/l_libvar.c +++ /dev/null @@ -1,294 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_libvar.c - * - * desc: bot library variables - * - * $Archive: /MissionPack/code/botlib/l_libvar.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "l_memory.h" -#include "l_libvar.h" - -//list with library variables -libvar_t *libvarlist; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float LibVarStringValue(char *string) -{ - int dotfound = 0; - float value = 0; - - while(*string) - { - if (*string < '0' || *string > '9') - { - if (dotfound || *string != '.') - { - return 0; - } //end if - else - { - dotfound = 10; - string++; - } //end if - } //end if - if (dotfound) - { - value = value + (float) (*string - '0') / (float) dotfound; - dotfound *= 10; - } //end if - else - { - value = value * 10.0 + (float) (*string - '0'); - } //end else - string++; - } //end while - return value; -} //end of the function LibVarStringValue -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -libvar_t *LibVarAlloc(char *var_name) -{ - libvar_t *v; - - v = (libvar_t *) GetMemory(sizeof(libvar_t) + strlen(var_name) + 1); - Com_Memset(v, 0, sizeof(libvar_t)); - v->name = (char *) v + sizeof(libvar_t); - strcpy(v->name, var_name); - //add the variable in the list - v->next = libvarlist; - libvarlist = v; - return v; -} //end of the function LibVarAlloc -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void LibVarDeAlloc(libvar_t *v) -{ - if (v->string) FreeMemory(v->string); - FreeMemory(v); -} //end of the function LibVarDeAlloc -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void LibVarDeAllocAll(void) -{ - libvar_t *v; - - for (v = libvarlist; v; v = libvarlist) - { - libvarlist = libvarlist->next; - LibVarDeAlloc(v); - } //end for - libvarlist = NULL; -} //end of the function LibVarDeAllocAll -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -libvar_t *LibVarGet(char *var_name) -{ - libvar_t *v; - - for (v = libvarlist; v; v = v->next) - { - if (!Q_stricmp(v->name, var_name)) - { - return v; - } //end if - } //end for - return NULL; -} //end of the function LibVarGet -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *LibVarGetString(char *var_name) -{ - libvar_t *v; - - v = LibVarGet(var_name); - if (v) - { - return v->string; - } //end if - else - { - return ""; - } //end else -} //end of the function LibVarGetString -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float LibVarGetValue(char *var_name) -{ - libvar_t *v; - - v = LibVarGet(var_name); - if (v) - { - return v->value; - } //end if - else - { - return 0; - } //end else -} //end of the function LibVarGetValue -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -libvar_t *LibVar(char *var_name, char *value) -{ - libvar_t *v; - v = LibVarGet(var_name); - if (v) return v; - //create new variable - v = LibVarAlloc(var_name); - //variable string - v->string = (char *) GetMemory(strlen(value) + 1); - strcpy(v->string, value); - //the value - v->value = LibVarStringValue(v->string); - //variable is modified - v->modified = qtrue; - // - return v; -} //end of the function LibVar -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *LibVarString(char *var_name, char *value) -{ - libvar_t *v; - - v = LibVar(var_name, value); - return v->string; -} //end of the function LibVarString -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float LibVarValue(char *var_name, char *value) -{ - libvar_t *v; - - v = LibVar(var_name, value); - return v->value; -} //end of the function LibVarValue -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void LibVarSet(char *var_name, char *value) -{ - libvar_t *v; - - v = LibVarGet(var_name); - if (v) - { - FreeMemory(v->string); - } //end if - else - { - v = LibVarAlloc(var_name); - } //end else - //variable string - v->string = (char *) GetMemory(strlen(value) + 1); - strcpy(v->string, value); - //the value - v->value = LibVarStringValue(v->string); - //variable is modified - v->modified = qtrue; -} //end of the function LibVarSet -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean LibVarChanged(char *var_name) -{ - libvar_t *v; - - v = LibVarGet(var_name); - if (v) - { - return v->modified; - } //end if - else - { - return qfalse; - } //end else -} //end of the function LibVarChanged -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void LibVarSetNotModified(char *var_name) -{ - libvar_t *v; - - v = LibVarGet(var_name); - if (v) - { - v->modified = qfalse; - } //end if -} //end of the function LibVarSetNotModified diff --git a/tools/quake3/bspc/deps/botlib/l_libvar.h b/tools/quake3/bspc/deps/botlib/l_libvar.h deleted file mode 100644 index d96685f4..00000000 --- a/tools/quake3/bspc/deps/botlib/l_libvar.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_libvar.h - * - * desc: botlib vars - * - * $Archive: /source/code/botlib/l_libvar.h $ - * - *****************************************************************************/ - -//library variable -typedef struct libvar_s -{ - char *name; - char *string; - int flags; - qboolean modified; // set each time the cvar is changed - float value; - struct libvar_s *next; -} libvar_t; - -//removes all library variables -void LibVarDeAllocAll(void); -//gets the library variable with the given name -libvar_t *LibVarGet(char *var_name); -//gets the string of the library variable with the given name -char *LibVarGetString(char *var_name); -//gets the value of the library variable with the given name -float LibVarGetValue(char *var_name); -//creates the library variable if not existing already and returns it -libvar_t *LibVar(char *var_name, char *value); -//creates the library variable if not existing already and returns the value -float LibVarValue(char *var_name, char *value); -//creates the library variable if not existing already and returns the value string -char *LibVarString(char *var_name, char *value); -//sets the library variable -void LibVarSet(char *var_name, char *value); -//returns true if the library variable has been modified -qboolean LibVarChanged(char *var_name); -//sets the library variable to unmodified -void LibVarSetNotModified(char *var_name); - diff --git a/tools/quake3/bspc/deps/botlib/l_log.c b/tools/quake3/bspc/deps/botlib/l_log.c deleted file mode 100644 index a2ca3c04..00000000 --- a/tools/quake3/bspc/deps/botlib/l_log.c +++ /dev/null @@ -1,169 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_log.c - * - * desc: log file - * - * $Archive: /MissionPack/CODE/botlib/l_log.c $ - * - *****************************************************************************/ - -#include -#include -#include - -#include "qcommon/q_shared.h" -#include "botlib.h" -#include "be_interface.h" //for botimport.Print -#include "l_libvar.h" - -#define MAX_LOGFILENAMESIZE 1024 - -typedef struct logfile_s -{ - char filename[MAX_LOGFILENAMESIZE]; - FILE *fp; - int numwrites; -} logfile_t; - -static logfile_t logfile; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_Open(char *filename) -{ - if (!LibVarValue("log", "0")) return; - if (!filename || !strlen(filename)) - { - botimport.Print(PRT_MESSAGE, "openlog \n"); - return; - } //end if - if (logfile.fp) - { - botimport.Print(PRT_ERROR, "log file %s is already opened\n", logfile.filename); - return; - } //end if - logfile.fp = fopen(filename, "wb"); - if (!logfile.fp) - { - botimport.Print(PRT_ERROR, "can't open the log file %s\n", filename); - return; - } //end if - strncpy(logfile.filename, filename, MAX_LOGFILENAMESIZE); - botimport.Print(PRT_MESSAGE, "Opened log %s\n", logfile.filename); -} //end of the function Log_Create -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_Close(void) -{ - if (!logfile.fp) return; - if (fclose(logfile.fp)) - { - botimport.Print(PRT_ERROR, "can't close log file %s\n", logfile.filename); - return; - } //end if - logfile.fp = NULL; - botimport.Print(PRT_MESSAGE, "Closed log %s\n", logfile.filename); -} //end of the function Log_Close -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_Shutdown(void) -{ - if (logfile.fp) Log_Close(); -} //end of the function Log_Shutdown -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void QDECL Log_Write(char *fmt, ...) -{ - va_list ap; - - if (!logfile.fp) return; - va_start(ap, fmt); - vfprintf(logfile.fp, fmt, ap); - va_end(ap); - //fprintf(logfile.fp, "\r\n"); - fflush(logfile.fp); -} //end of the function Log_Write -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void QDECL Log_WriteTimeStamped(char *fmt, ...) -{ - va_list ap; - - if (!logfile.fp) return; - fprintf(logfile.fp, "%d %02d:%02d:%02d:%02d ", - logfile.numwrites, - (int) (botlibglobals.time / 60 / 60), - (int) (botlibglobals.time / 60), - (int) (botlibglobals.time), - (int) ((int) (botlibglobals.time * 100)) - - ((int) botlibglobals.time) * 100); - va_start(ap, fmt); - vfprintf(logfile.fp, fmt, ap); - va_end(ap); - fprintf(logfile.fp, "\r\n"); - logfile.numwrites++; - fflush(logfile.fp); -} //end of the function Log_Write -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -FILE *Log_FilePointer(void) -{ - return logfile.fp; -} //end of the function Log_FilePointer -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_Flush(void) -{ - if (logfile.fp) fflush(logfile.fp); -} //end of the function Log_Flush - diff --git a/tools/quake3/bspc/deps/botlib/l_log.h b/tools/quake3/bspc/deps/botlib/l_log.h deleted file mode 100644 index 3ddb4641..00000000 --- a/tools/quake3/bspc/deps/botlib/l_log.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_log.h - * - * desc: log file - * - * $Archive: /source/code/botlib/l_log.h $ - * - *****************************************************************************/ - -//open a log file -void Log_Open(char *filename); -//close the current log file -void Log_Close(void); -//close log file if present -void Log_Shutdown(void); -//write to the current opened log file -void QDECL Log_Write(char *fmt, ...); -//write to the current opened log file with a time stamp -void QDECL Log_WriteTimeStamped(char *fmt, ...); -//returns a pointer to the log file -FILE *Log_FilePointer(void); -//flush log file -void Log_Flush(void); - diff --git a/tools/quake3/bspc/deps/botlib/l_memory.c b/tools/quake3/bspc/deps/botlib/l_memory.c deleted file mode 100644 index 8ad66572..00000000 --- a/tools/quake3/bspc/deps/botlib/l_memory.c +++ /dev/null @@ -1,463 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_memory.c - * - * desc: memory allocation - * - * $Archive: /MissionPack/code/botlib/l_memory.c $ - * - *****************************************************************************/ - -#include "qcommon/q_shared.h" -#include "botlib.h" -#include "l_log.h" -#include "be_interface.h" - -//#define MEMDEBUG -//#define MEMORYMANEGER - -#define MEM_ID 0x12345678l -#define HUNK_ID 0x87654321l - -int allocatedmemory; -int totalmemorysize; -int numblocks; - -#ifdef MEMORYMANEGER - -typedef struct memoryblock_s -{ - unsigned long int id; - void *ptr; - int size; -#ifdef MEMDEBUG - char *label; - char *file; - int line; -#endif //MEMDEBUG - struct memoryblock_s *prev, *next; -} memoryblock_t; - -memoryblock_t *memory; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void LinkMemoryBlock(memoryblock_t *block) -{ - block->prev = NULL; - block->next = memory; - if (memory) memory->prev = block; - memory = block; -} //end of the function LinkMemoryBlock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void UnlinkMemoryBlock(memoryblock_t *block) -{ - if (block->prev) block->prev->next = block->next; - else memory = block->next; - if (block->next) block->next->prev = block->prev; -} //end of the function UnlinkMemoryBlock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef MEMDEBUG -void *GetMemoryDebug(unsigned long size, char *label, char *file, int line) -#else -void *GetMemory(unsigned long size) -#endif //MEMDEBUG -{ - void *ptr; - memoryblock_t *block; - assert(botimport.GetMemory); // bk001129 - was NULL'ed - ptr = botimport.GetMemory(size + sizeof(memoryblock_t)); - block = (memoryblock_t *) ptr; - block->id = MEM_ID; - block->ptr = (char *) ptr + sizeof(memoryblock_t); - block->size = size + sizeof(memoryblock_t); -#ifdef MEMDEBUG - block->label = label; - block->file = file; - block->line = line; -#endif //MEMDEBUG - LinkMemoryBlock(block); - allocatedmemory += block->size; - totalmemorysize += block->size + sizeof(memoryblock_t); - numblocks++; - return block->ptr; -} //end of the function GetMemoryDebug -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef MEMDEBUG -void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line) -#else -void *GetClearedMemory(unsigned long size) -#endif //MEMDEBUG -{ - void *ptr; -#ifdef MEMDEBUG - ptr = GetMemoryDebug(size, label, file, line); -#else - ptr = GetMemory(size); -#endif //MEMDEBUG - Com_Memset(ptr, 0, size); - return ptr; -} //end of the function GetClearedMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef MEMDEBUG -void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line) -#else -void *GetHunkMemory(unsigned long size) -#endif //MEMDEBUG -{ - void *ptr; - memoryblock_t *block; - - ptr = botimport.HunkAlloc(size + sizeof(memoryblock_t)); - block = (memoryblock_t *) ptr; - block->id = HUNK_ID; - block->ptr = (char *) ptr + sizeof(memoryblock_t); - block->size = size + sizeof(memoryblock_t); -#ifdef MEMDEBUG - block->label = label; - block->file = file; - block->line = line; -#endif //MEMDEBUG - LinkMemoryBlock(block); - allocatedmemory += block->size; - totalmemorysize += block->size + sizeof(memoryblock_t); - numblocks++; - return block->ptr; -} //end of the function GetHunkMemoryDebug -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef MEMDEBUG -void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line) -#else -void *GetClearedHunkMemory(unsigned long size) -#endif //MEMDEBUG -{ - void *ptr; -#ifdef MEMDEBUG - ptr = GetHunkMemoryDebug(size, label, file, line); -#else - ptr = GetHunkMemory(size); -#endif //MEMDEBUG - Com_Memset(ptr, 0, size); - return ptr; -} //end of the function GetClearedHunkMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -memoryblock_t *BlockFromPointer(void *ptr, char *str) -{ - memoryblock_t *block; - - if (!ptr) - { -#ifdef MEMDEBUG - //char *crash = (char *) NULL; - //crash[0] = 1; - botimport.Print(PRT_FATAL, "%s: NULL pointer\n", str); -#endif // MEMDEBUG - return NULL; - } //end if - block = (memoryblock_t *) ((char *) ptr - sizeof(memoryblock_t)); - if (block->id != MEM_ID && block->id != HUNK_ID) - { - botimport.Print(PRT_FATAL, "%s: invalid memory block\n", str); - return NULL; - } //end if - if (block->ptr != ptr) - { - botimport.Print(PRT_FATAL, "%s: memory block pointer invalid\n", str); - return NULL; - } //end if - return block; -} //end of the function BlockFromPointer -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeMemory(void *ptr) -{ - memoryblock_t *block; - - block = BlockFromPointer(ptr, "FreeMemory"); - if (!block) return; - UnlinkMemoryBlock(block); - allocatedmemory -= block->size; - totalmemorysize -= block->size + sizeof(memoryblock_t); - numblocks--; - // - if (block->id == MEM_ID) - { - botimport.FreeMemory(block); - } //end if -} //end of the function FreeMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AvailableMemory(void) -{ - return botimport.AvailableMemory(); -} //end of the function AvailableMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int MemoryByteSize(void *ptr) -{ - memoryblock_t *block; - - block = BlockFromPointer(ptr, "MemoryByteSize"); - if (!block) return 0; - return block->size; -} //end of the function MemoryByteSize -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintUsedMemorySize(void) -{ - botimport.Print(PRT_MESSAGE, "total allocated memory: %d KB\n", allocatedmemory >> 10); - botimport.Print(PRT_MESSAGE, "total botlib memory: %d KB\n", totalmemorysize >> 10); - botimport.Print(PRT_MESSAGE, "total memory blocks: %d\n", numblocks); -} //end of the function PrintUsedMemorySize -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintMemoryLabels(void) -{ - memoryblock_t *block; - int i; - - PrintUsedMemorySize(); - i = 0; - Log_Write("============= Botlib memory log ==============\r\n"); - Log_Write("\r\n"); - for (block = memory; block; block = block->next) - { -#ifdef MEMDEBUG - if (block->id == HUNK_ID) - { - Log_Write("%6d, hunk %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label); - } //end if - else - { - Log_Write("%6d, %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label); - } //end else -#endif //MEMDEBUG - i++; - } //end for -} //end of the function PrintMemoryLabels -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void DumpMemory(void) -{ - memoryblock_t *block; - - for (block = memory; block; block = memory) - { - FreeMemory(block->ptr); - } //end for - totalmemorysize = 0; - allocatedmemory = 0; -} //end of the function DumpMemory - -#else - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef MEMDEBUG -void *GetMemoryDebug(unsigned long size, char *label, char *file, int line) -#else -void *GetMemory(unsigned long size) -#endif //MEMDEBUG -{ - void *ptr; - unsigned long int *memid; - - ptr = botimport.GetMemory(size + sizeof(unsigned long int)); - if (!ptr) return NULL; - memid = (unsigned long int *) ptr; - *memid = MEM_ID; - return (unsigned long int *) ((char *) ptr + sizeof(unsigned long int)); -} //end of the function GetMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef MEMDEBUG -void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line) -#else -void *GetClearedMemory(unsigned long size) -#endif //MEMDEBUG -{ - void *ptr; -#ifdef MEMDEBUG - ptr = GetMemoryDebug(size, label, file, line); -#else - ptr = GetMemory(size); -#endif //MEMDEBUG - Com_Memset(ptr, 0, size); - return ptr; -} //end of the function GetClearedMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef MEMDEBUG -void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line) -#else -void *GetHunkMemory(unsigned long size) -#endif //MEMDEBUG -{ - void *ptr; - unsigned long int *memid; - - ptr = botimport.HunkAlloc(size + sizeof(unsigned long int)); - if (!ptr) return NULL; - memid = (unsigned long int *) ptr; - *memid = HUNK_ID; - return (unsigned long int *) ((char *) ptr + sizeof(unsigned long int)); -} //end of the function GetHunkMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef MEMDEBUG -void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line) -#else -void *GetClearedHunkMemory(unsigned long size) -#endif //MEMDEBUG -{ - void *ptr; -#ifdef MEMDEBUG - ptr = GetHunkMemoryDebug(size, label, file, line); -#else - ptr = GetHunkMemory(size); -#endif //MEMDEBUG - Com_Memset(ptr, 0, size); - return ptr; -} //end of the function GetClearedHunkMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeMemory(void *ptr) -{ - unsigned long int *memid; - - memid = (unsigned long int *) ((char *) ptr - sizeof(unsigned long int)); - - if (*memid == MEM_ID) - { - botimport.FreeMemory(memid); - } //end if -} //end of the function FreeMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int AvailableMemory(void) -{ - return botimport.AvailableMemory(); -} //end of the function AvailableMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintUsedMemorySize(void) -{ -} //end of the function PrintUsedMemorySize -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintMemoryLabels(void) -{ -} //end of the function PrintMemoryLabels - -#endif diff --git a/tools/quake3/bspc/deps/botlib/l_memory.h b/tools/quake3/bspc/deps/botlib/l_memory.h deleted file mode 100644 index 17c89d5b..00000000 --- a/tools/quake3/bspc/deps/botlib/l_memory.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_memory.h - * - * desc: memory management - * - * $Archive: /source/code/botlib/l_memory.h $ - * - *****************************************************************************/ - -//#define MEMDEBUG - -#ifdef MEMDEBUG -#define GetMemory(size) GetMemoryDebug(size, #size, __FILE__, __LINE__); -#define GetClearedMemory(size) GetClearedMemoryDebug(size, #size, __FILE__, __LINE__); -//allocate a memory block of the given size -void *GetMemoryDebug(unsigned long size, char *label, char *file, int line); -//allocate a memory block of the given size and clear it -void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line); -// -#define GetHunkMemory(size) GetHunkMemoryDebug(size, #size, __FILE__, __LINE__); -#define GetClearedHunkMemory(size) GetClearedHunkMemoryDebug(size, #size, __FILE__, __LINE__); -//allocate a memory block of the given size -void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line); -//allocate a memory block of the given size and clear it -void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line); -#else -//allocate a memory block of the given size -void *GetMemory(unsigned long size); -//allocate a memory block of the given size and clear it -void *GetClearedMemory(unsigned long size); -// -#ifdef BSPC -#define GetHunkMemory GetMemory -#define GetClearedHunkMemory GetClearedMemory -#else -//allocate a memory block of the given size -void *GetHunkMemory(unsigned long size); -//allocate a memory block of the given size and clear it -void *GetClearedHunkMemory(unsigned long size); -#endif -#endif - -//free the given memory block -void FreeMemory(void *ptr); -//returns the amount available memory -int AvailableMemory(void); -//prints the total used memory size -void PrintUsedMemorySize(void); -//print all memory blocks with label -void PrintMemoryLabels(void); -//returns the size of the memory block in bytes -int MemoryByteSize(void *ptr); -//free all allocated memory -void DumpMemory(void); diff --git a/tools/quake3/bspc/deps/botlib/l_precomp.c b/tools/quake3/bspc/deps/botlib/l_precomp.c deleted file mode 100644 index 86fdb2a6..00000000 --- a/tools/quake3/bspc/deps/botlib/l_precomp.c +++ /dev/null @@ -1,3233 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// - -/***************************************************************************** - * name: l_precomp.c - * - * desc: pre compiler - * - * $Archive: /MissionPack/code/botlib/l_precomp.c $ - * - *****************************************************************************/ - -//Notes: fix: PC_StringizeTokens - -//#define SCREWUP -//#define BOTLIB -//#define QUAKE -//#define QUAKEC -//#define MEQCC - -#ifdef SCREWUP -#include -#include -#include -#include -#include -#include -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" - -typedef enum {qfalse, qtrue} qboolean; -#endif //SCREWUP - -#ifdef BOTLIB -#include "qcommon/q_shared.h" -#include "botlib.h" -#include "be_interface.h" -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_log.h" -#endif //BOTLIB - -#ifdef MEQCC -#include "qcc.h" -#include "time.h" //time & ctime -#include "math.h" //fabs -#include "l_memory.h" -#include "l_script.h" -#include "l_precomp.h" -#include "l_log.h" - -#define qtrue true -#define qfalse false -#endif //MEQCC - -#ifdef BSPC -//include files for usage in the BSP Converter -#include "../../qbsp.h" -#include "l_log.h" -#include "../../l_mem.h" -#include "l_precomp.h" - -#define qtrue true -#define qfalse false -#define Q_stricmp stricmp - -#endif //BSPC - -#if defined(QUAKE) && !defined(BSPC) -#include "l_utils.h" -#endif //QUAKE - -//#define DEBUG_EVAL - -#define MAX_DEFINEPARMS 128 - -#define DEFINEHASHING 1 - -//directive name with parse function -typedef struct directive_s -{ - char *name; - int (*func)(source_t *source); -} directive_t; - -#define DEFINEHASHSIZE 1024 - -#define TOKEN_HEAP_SIZE 4096 - -int numtokens; -/* -int tokenheapinitialized; //true when the token heap is initialized -token_t token_heap[TOKEN_HEAP_SIZE]; //heap with tokens -token_t *freetokens; //free tokens from the heap -*/ - -//list with global defines added to every source loaded -define_t *globaldefines; - -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void QDECL SourceError(source_t *source, char *str, ...) -{ - char text[1024]; - va_list ap; - - va_start(ap, str); - vsprintf(text, str, ap); - va_end(ap); -#ifdef BOTLIB - botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); -#endif //BOTLIB -#ifdef MEQCC - printf("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); -#endif //MEQCC -#ifdef BSPC - Log_Print("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); -#endif //BSPC -} //end of the function SourceError -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void QDECL SourceWarning(source_t *source, char *str, ...) -{ - char text[1024]; - va_list ap; - - va_start(ap, str); - vsprintf(text, str, ap); - va_end(ap); -#ifdef BOTLIB - botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); -#endif //BOTLIB -#ifdef MEQCC - printf("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); -#endif //MEQCC -#ifdef BSPC - Log_Print("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); -#endif //BSPC -} //end of the function ScriptWarning -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_PushIndent(source_t *source, int type, int skip) -{ - indent_t *indent; - - indent = (indent_t *) GetMemory(sizeof(indent_t)); - indent->type = type; - indent->script = source->scriptstack; - indent->skip = (skip != 0); - source->skip += indent->skip; - indent->next = source->indentstack; - source->indentstack = indent; -} //end of the function PC_PushIndent -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_PopIndent(source_t *source, int *type, int *skip) -{ - indent_t *indent; - - *type = 0; - *skip = 0; - - indent = source->indentstack; - if (!indent) return; - - //must be an indent from the current script - if (source->indentstack->script != source->scriptstack) return; - - *type = indent->type; - *skip = indent->skip; - source->indentstack = source->indentstack->next; - source->skip -= indent->skip; - FreeMemory(indent); -} //end of the function PC_PopIndent -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_PushScript(source_t *source, script_t *script) -{ - script_t *s; - - for (s = source->scriptstack; s; s = s->next) - { - if (!Q_stricmp(s->filename, script->filename)) - { - SourceError(source, "%s recursively included", script->filename); - return; - } //end if - } //end for - //push the script on the script stack - script->next = source->scriptstack; - source->scriptstack = script; -} //end of the function PC_PushScript -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_InitTokenHeap(void) -{ - /* - int i; - - if (tokenheapinitialized) return; - freetokens = NULL; - for (i = 0; i < TOKEN_HEAP_SIZE; i++) - { - token_heap[i].next = freetokens; - freetokens = &token_heap[i]; - } //end for - tokenheapinitialized = qtrue; - */ -} //end of the function PC_InitTokenHeap -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -token_t *PC_CopyToken(token_t *token) -{ - token_t *t; - -// t = (token_t *) malloc(sizeof(token_t)); - t = (token_t *) GetMemory(sizeof(token_t)); -// t = freetokens; - if (!t) - { -#ifdef BSPC - Error("out of token space\n"); -#else - Com_Error(ERR_FATAL, "out of token space\n"); -#endif - return NULL; - } //end if -// freetokens = freetokens->next; - //Com_Memcpy(t, token, sizeof(token_t)); - memcpy(t,token,sizeof(token_t)); - t->next = NULL; - numtokens++; - return t; -} //end of the function PC_CopyToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_FreeToken(token_t *token) -{ - //free(token); - FreeMemory(token); -// token->next = freetokens; -// freetokens = token; - numtokens--; -} //end of the function PC_FreeToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_ReadSourceToken(source_t *source, token_t *token) -{ - token_t *t; - script_t *script; - int type, skip; - - //if there's no token already available - while(!source->tokens) - { - //if there's a token to read from the script - if (PS_ReadToken(source->scriptstack, token)) return qtrue; - //if at the end of the script - if (EndOfScript(source->scriptstack)) - { - //remove all indents of the script - while(source->indentstack && - source->indentstack->script == source->scriptstack) - { - SourceWarning(source, "missing #endif"); - PC_PopIndent(source, &type, &skip); - } //end if - } //end if - //if this was the initial script - if (!source->scriptstack->next) return qfalse; - //remove the script and return to the last one - script = source->scriptstack; - source->scriptstack = source->scriptstack->next; - FreeScript(script); - } //end while - //copy the already available token - //Com_Memcpy(token, source->tokens, sizeof(token_t)); - memcpy(token, source->tokens, sizeof(token_t)); - //free the read token - t = source->tokens; - source->tokens = source->tokens->next; - PC_FreeToken(t); - return qtrue; -} //end of the function PC_ReadSourceToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_UnreadSourceToken(source_t *source, token_t *token) -{ - token_t *t; - - t = PC_CopyToken(token); - t->next = source->tokens; - source->tokens = t; - return qtrue; -} //end of the function PC_UnreadSourceToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_ReadDefineParms(source_t *source, define_t *define, token_t **parms, int maxparms) -{ - token_t token, *t, *last; - int i, done, lastcomma, numparms, indent; - - if (!PC_ReadSourceToken(source, &token)) - { - SourceError(source, "define %s missing parms", define->name); - return qfalse; - } //end if - // - if (define->numparms > maxparms) - { - SourceError(source, "define with more than %d parameters", maxparms); - return qfalse; - } //end if - // - for (i = 0; i < define->numparms; i++) parms[i] = NULL; - //if no leading "(" - if (strcmp(token.string, "(")) - { - PC_UnreadSourceToken(source, &token); - SourceError(source, "define %s missing parms", define->name); - return qfalse; - } //end if - //read the define parameters - for (done = 0, numparms = 0, indent = 0; !done;) - { - if (numparms >= maxparms) - { - SourceError(source, "define %s with too many parms", define->name); - return qfalse; - } //end if - if (numparms >= define->numparms) - { - SourceWarning(source, "define %s has too many parms", define->name); - return qfalse; - } //end if - parms[numparms] = NULL; - lastcomma = 1; - last = NULL; - while(!done) - { - // - if (!PC_ReadSourceToken(source, &token)) - { - SourceError(source, "define %s incomplete", define->name); - return qfalse; - } //end if - // - if (!strcmp(token.string, ",")) - { - if (indent <= 0) - { - if (lastcomma) SourceWarning(source, "too many comma's"); - lastcomma = 1; - break; - } //end if - } //end if - lastcomma = 0; - // - if (!strcmp(token.string, "(")) - { - indent++; - continue; - } //end if - else if (!strcmp(token.string, ")")) - { - if (--indent <= 0) - { - if (!parms[define->numparms-1]) - { - SourceWarning(source, "too few define parms"); - } //end if - done = 1; - break; - } //end if - } //end if - // - if (numparms < define->numparms) - { - // - t = PC_CopyToken(&token); - t->next = NULL; - if (last) last->next = t; - else parms[numparms] = t; - last = t; - } //end if - } //end while - numparms++; - } //end for - return qtrue; -} //end of the function PC_ReadDefineParms -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_StringizeTokens(token_t *tokens, token_t *token) -{ - token_t *t; - - token->type = TT_STRING; - token->whitespace_p = NULL; - token->endwhitespace_p = NULL; - token->string[0] = '\0'; - strcat(token->string, "\""); - for (t = tokens; t; t = t->next) - { - strncat(token->string, t->string, MAX_TOKEN - strlen(token->string)); - } //end for - strncat(token->string, "\"", MAX_TOKEN - strlen(token->string)); - return qtrue; -} //end of the function PC_StringizeTokens -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_MergeTokens(token_t *t1, token_t *t2) -{ - //merging of a name with a name or number - if (t1->type == TT_NAME && (t2->type == TT_NAME || t2->type == TT_NUMBER)) - { - strcat(t1->string, t2->string); - return qtrue; - } //end if - //merging of two strings - if (t1->type == TT_STRING && t2->type == TT_STRING) - { - //remove trailing double quote - t1->string[strlen(t1->string)-1] = '\0'; - //concat without leading double quote - strcat(t1->string, &t2->string[1]); - return qtrue; - } //end if - //FIXME: merging of two number of the same sub type - return qfalse; -} //end of the function PC_MergeTokens -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -/* -void PC_PrintDefine(define_t *define) -{ - printf("define->name = %s\n", define->name); - printf("define->flags = %d\n", define->flags); - printf("define->builtin = %d\n", define->builtin); - printf("define->numparms = %d\n", define->numparms); -// token_t *parms; //define parameters -// token_t *tokens; //macro tokens (possibly containing parm tokens) -// struct define_s *next; //next defined macro in a list -} //end of the function PC_PrintDefine*/ -#if DEFINEHASHING -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_PrintDefineHashTable(define_t **definehash) -{ - int i; - define_t *d; - - for (i = 0; i < DEFINEHASHSIZE; i++) - { - Log_Write("%4d:", i); - for (d = definehash[i]; d; d = d->hashnext) - { - Log_Write(" %s", d->name); - } //end for - Log_Write("\n"); - } //end for -} //end of the function PC_PrintDefineHashTable -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -//char primes[16] = {1, 3, 5, 7, 11, 13, 17, 19, 23, 27, 29, 31, 37, 41, 43, 47}; - -int PC_NameHash(char *name) -{ - int register hash, i; - - hash = 0; - for (i = 0; name[i] != '\0'; i++) - { - hash += name[i] * (119 + i); - //hash += (name[i] << 7) + i; - //hash += (name[i] << (i&15)); - } //end while - hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE-1); - return hash; -} //end of the function PC_NameHash -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_AddDefineToHash(define_t *define, define_t **definehash) -{ - int hash; - - hash = PC_NameHash(define->name); - define->hashnext = definehash[hash]; - definehash[hash] = define; -} //end of the function PC_AddDefineToHash -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -define_t *PC_FindHashedDefine(define_t **definehash, char *name) -{ - define_t *d; - int hash; - - hash = PC_NameHash(name); - for (d = definehash[hash]; d; d = d->hashnext) - { - if (!strcmp(d->name, name)) return d; - } //end for - return NULL; -} //end of the function PC_FindHashedDefine -#endif //DEFINEHASHING -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -define_t *PC_FindDefine(define_t *defines, char *name) -{ - define_t *d; - - for (d = defines; d; d = d->next) - { - if (!strcmp(d->name, name)) return d; - } //end for - return NULL; -} //end of the function PC_FindDefine -//============================================================================ -// -// Parameter: - -// Returns: number of the parm -// if no parm found with the given name -1 is returned -// Changes Globals: - -//============================================================================ -int PC_FindDefineParm(define_t *define, char *name) -{ - token_t *p; - int i; - - i = 0; - for (p = define->parms; p; p = p->next) - { - if (!strcmp(p->string, name)) return i; - i++; - } //end for - return -1; -} //end of the function PC_FindDefineParm -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_FreeDefine(define_t *define) -{ - token_t *t, *next; - - //free the define parameters - for (t = define->parms; t; t = next) - { - next = t->next; - PC_FreeToken(t); - } //end for - //free the define tokens - for (t = define->tokens; t; t = next) - { - next = t->next; - PC_FreeToken(t); - } //end for - //free the define - FreeMemory(define); -} //end of the function PC_FreeDefine -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_AddBuiltinDefines(source_t *source) -{ - int i; - define_t *define; - struct builtin - { - char *string; - int builtin; - } builtin[] = { // bk001204 - brackets - { "__LINE__", BUILTIN_LINE }, - { "__FILE__", BUILTIN_FILE }, - { "__DATE__", BUILTIN_DATE }, - { "__TIME__", BUILTIN_TIME }, -// { "__STDC__", BUILTIN_STDC }, - { NULL, 0 } - }; - - for (i = 0; builtin[i].string; i++) - { - define = (define_t *) GetMemory(sizeof(define_t) + strlen(builtin[i].string) + 1); - //Com_Memset(define, 0, sizeof(define_t)); - memset(define, 0, sizeof(define_t)); - define->name = (char *) define + sizeof(define_t); - strcpy(define->name, builtin[i].string); - define->flags |= DEFINE_FIXED; - define->builtin = builtin[i].builtin; - //add the define to the source -#if DEFINEHASHING - PC_AddDefineToHash(define, source->definehash); -#else - define->next = source->defines; - source->defines = define; -#endif //DEFINEHASHING - } //end for -} //end of the function PC_AddBuiltinDefines -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_ExpandBuiltinDefine(source_t *source, token_t *deftoken, define_t *define, - token_t **firsttoken, token_t **lasttoken) -{ - token_t *token; - unsigned long t; // time_t t; //to prevent LCC warning - char *curtime; - - token = PC_CopyToken(deftoken); - switch(define->builtin) - { - case BUILTIN_LINE: - { - sprintf(token->string, "%d", deftoken->line); -#ifdef NUMBERVALUE - token->intvalue = deftoken->line; - token->floatvalue = deftoken->line; -#endif //NUMBERVALUE - token->type = TT_NUMBER; - token->subtype = TT_DECIMAL | TT_INTEGER; - *firsttoken = token; - *lasttoken = token; - break; - } //end case - case BUILTIN_FILE: - { - strcpy(token->string, source->scriptstack->filename); - token->type = TT_NAME; - token->subtype = strlen(token->string); - *firsttoken = token; - *lasttoken = token; - break; - } //end case - case BUILTIN_DATE: - { - t = time(NULL); - curtime = ctime(&t); - strcpy(token->string, "\""); - strncat(token->string, curtime+4, 7); - strncat(token->string+7, curtime+20, 4); - strcat(token->string, "\""); - free(curtime); - token->type = TT_NAME; - token->subtype = strlen(token->string); - *firsttoken = token; - *lasttoken = token; - break; - } //end case - case BUILTIN_TIME: - { - t = time(NULL); - curtime = ctime(&t); - strcpy(token->string, "\""); - strncat(token->string, curtime+11, 8); - strcat(token->string, "\""); - free(curtime); - token->type = TT_NAME; - token->subtype = strlen(token->string); - *firsttoken = token; - *lasttoken = token; - break; - } //end case - case BUILTIN_STDC: - default: - { - *firsttoken = NULL; - *lasttoken = NULL; - break; - } //end case - } //end switch - return qtrue; -} //end of the function PC_ExpandBuiltinDefine -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_ExpandDefine(source_t *source, token_t *deftoken, define_t *define, - token_t **firsttoken, token_t **lasttoken) -{ - token_t *parms[MAX_DEFINEPARMS], *dt, *pt, *t; - token_t *t1, *t2, *first, *last, *nextpt, token; - int parmnum, i; - - //if it is a builtin define - if (define->builtin) - { - return PC_ExpandBuiltinDefine(source, deftoken, define, firsttoken, lasttoken); - } //end if - //if the define has parameters - if (define->numparms) - { - if (!PC_ReadDefineParms(source, define, parms, MAX_DEFINEPARMS)) return qfalse; -#ifdef DEBUG_EVAL - for (i = 0; i < define->numparms; i++) - { - Log_Write("define parms %d:", i); - for (pt = parms[i]; pt; pt = pt->next) - { - Log_Write("%s", pt->string); - } //end for - } //end for -#endif //DEBUG_EVAL - } //end if - //empty list at first - first = NULL; - last = NULL; - //create a list with tokens of the expanded define - for (dt = define->tokens; dt; dt = dt->next) - { - parmnum = -1; - //if the token is a name, it could be a define parameter - if (dt->type == TT_NAME) - { - parmnum = PC_FindDefineParm(define, dt->string); - } //end if - //if it is a define parameter - if (parmnum >= 0) - { - for (pt = parms[parmnum]; pt; pt = pt->next) - { - t = PC_CopyToken(pt); - //add the token to the list - t->next = NULL; - if (last) last->next = t; - else first = t; - last = t; - } //end for - } //end if - else - { - //if stringizing operator - if (dt->string[0] == '#' && dt->string[1] == '\0') - { - //the stringizing operator must be followed by a define parameter - if (dt->next) parmnum = PC_FindDefineParm(define, dt->next->string); - else parmnum = -1; - // - if (parmnum >= 0) - { - //step over the stringizing operator - dt = dt->next; - //stringize the define parameter tokens - if (!PC_StringizeTokens(parms[parmnum], &token)) - { - SourceError(source, "can't stringize tokens"); - return qfalse; - } //end if - t = PC_CopyToken(&token); - } //end if - else - { - SourceWarning(source, "stringizing operator without define parameter"); - continue; - } //end if - } //end if - else - { - t = PC_CopyToken(dt); - } //end else - //add the token to the list - t->next = NULL; - if (last) last->next = t; - else first = t; - last = t; - } //end else - } //end for - //check for the merging operator - for (t = first; t; ) - { - if (t->next) - { - //if the merging operator - if (t->next->string[0] == '#' && t->next->string[1] == '#') - { - t1 = t; - t2 = t->next->next; - if (t2) - { - if (!PC_MergeTokens(t1, t2)) - { - SourceError(source, "can't merge %s with %s", t1->string, t2->string); - return qfalse; - } //end if - PC_FreeToken(t1->next); - t1->next = t2->next; - if (t2 == last) last = t1; - PC_FreeToken(t2); - continue; - } //end if - } //end if - } //end if - t = t->next; - } //end for - //store the first and last token of the list - *firsttoken = first; - *lasttoken = last; - //free all the parameter tokens - for (i = 0; i < define->numparms; i++) - { - for (pt = parms[i]; pt; pt = nextpt) - { - nextpt = pt->next; - PC_FreeToken(pt); - } //end for - } //end for - // - return qtrue; -} //end of the function PC_ExpandDefine -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_ExpandDefineIntoSource(source_t *source, token_t *deftoken, define_t *define) -{ - token_t *firsttoken, *lasttoken; - - if (!PC_ExpandDefine(source, deftoken, define, &firsttoken, &lasttoken)) return qfalse; - - if (firsttoken && lasttoken) - { - lasttoken->next = source->tokens; - source->tokens = firsttoken; - return qtrue; - } //end if - return qfalse; -} //end of the function PC_ExpandDefineIntoSource -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_ConvertPath(char *path) -{ - char *ptr; - - //remove double path seperators - for (ptr = path; *ptr;) - { - if ((*ptr == '\\' || *ptr == '/') && - (*(ptr+1) == '\\' || *(ptr+1) == '/')) - { - strcpy(ptr, ptr+1); - } //end if - else - { - ptr++; - } //end else - } //end while - //set OS dependent path seperators - for (ptr = path; *ptr;) - { - if (*ptr == '/' || *ptr == '\\') *ptr = PATHSEPERATOR_CHAR; - ptr++; - } //end while -} //end of the function PC_ConvertPath -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_include(source_t *source) -{ - script_t *script; - token_t token; - char path[2 * MAX_PATH]; -#ifdef QUAKE - foundfile_t file; -#endif //QUAKE - - if (source->skip > 0) return qtrue; - // - if (!PC_ReadSourceToken(source, &token)) - { - SourceError(source, "#include without file name"); - return qfalse; - } //end if - if (token.linescrossed > 0) - { - SourceError(source, "#include without file name"); - return qfalse; - } //end if - if (token.type == TT_STRING) - { - StripDoubleQuotes(token.string); - PC_ConvertPath(token.string); - script = LoadScriptFile(token.string); - if (!script) - { - strcpy(path, source->includepath); - strcat(path, token.string); - script = LoadScriptFile(path); - } //end if - } //end if - else if (token.type == TT_PUNCTUATION && *token.string == '<') - { - strcpy(path, source->includepath); - while(PC_ReadSourceToken(source, &token)) - { - if (token.linescrossed > 0) - { - PC_UnreadSourceToken(source, &token); - break; - } //end if - if (token.type == TT_PUNCTUATION && *token.string == '>') break; - strncat(path, token.string, MAX_PATH); - } //end while - if (*token.string != '>') - { - SourceWarning(source, "#include missing trailing >"); - } //end if - if (!strlen(path)) - { - SourceError(source, "#include without file name between < >"); - return qfalse; - } //end if - PC_ConvertPath(path); - script = LoadScriptFile(path); - } //end if - else - { - SourceError(source, "#include without file name"); - return qfalse; - } //end else -#ifdef QUAKE - if (!script) - { - Com_Memset(&file, 0, sizeof(foundfile_t)); - script = LoadScriptFile(path); - if (script) strncpy(script->filename, path, MAX_PATH); - } //end if -#endif //QUAKE - if (!script) - { -#ifdef SCREWUP - SourceWarning(source, "file %s not found", path); - return qtrue; -#else - SourceError(source, "file %s not found", path); - return qfalse; -#endif //SCREWUP - } //end if - PC_PushScript(source, script); - return qtrue; -} //end of the function PC_Directive_include -//============================================================================ -// reads a token from the current line, continues reading on the next -// line only if a backslash '\' is encountered. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_ReadLine(source_t *source, token_t *token) -{ - int crossline; - - crossline = 0; - do - { - if (!PC_ReadSourceToken(source, token)) return qfalse; - - if (token->linescrossed > crossline) - { - PC_UnreadSourceToken(source, token); - return qfalse; - } //end if - crossline = 1; - } while(!strcmp(token->string, "\\")); - return qtrue; -} //end of the function PC_ReadLine -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_WhiteSpaceBeforeToken(token_t *token) -{ - return token->endwhitespace_p - token->whitespace_p > 0; -} //end of the function PC_WhiteSpaceBeforeToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_ClearTokenWhiteSpace(token_t *token) -{ - token->whitespace_p = NULL; - token->endwhitespace_p = NULL; - token->linescrossed = 0; -} //end of the function PC_ClearTokenWhiteSpace -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_undef(source_t *source) -{ - token_t token; - define_t *define, *lastdefine; - int hash; - - if (source->skip > 0) return qtrue; - // - if (!PC_ReadLine(source, &token)) - { - SourceError(source, "undef without name"); - return qfalse; - } //end if - if (token.type != TT_NAME) - { - PC_UnreadSourceToken(source, &token); - SourceError(source, "expected name, found %s", token.string); - return qfalse; - } //end if -#if DEFINEHASHING - - hash = PC_NameHash(token.string); - for (lastdefine = NULL, define = source->definehash[hash]; define; define = define->hashnext) - { - if (!strcmp(define->name, token.string)) - { - if (define->flags & DEFINE_FIXED) - { - SourceWarning(source, "can't undef %s", token.string); - } //end if - else - { - if (lastdefine) lastdefine->hashnext = define->hashnext; - else source->definehash[hash] = define->hashnext; - PC_FreeDefine(define); - } //end else - break; - } //end if - lastdefine = define; - } //end for -#else //DEFINEHASHING - for (lastdefine = NULL, define = source->defines; define; define = define->next) - { - if (!strcmp(define->name, token.string)) - { - if (define->flags & DEFINE_FIXED) - { - SourceWarning(source, "can't undef %s", token.string); - } //end if - else - { - if (lastdefine) lastdefine->next = define->next; - else source->defines = define->next; - PC_FreeDefine(define); - } //end else - break; - } //end if - lastdefine = define; - } //end for -#endif //DEFINEHASHING - return qtrue; -} //end of the function PC_Directive_undef -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_define(source_t *source) -{ - token_t token, *t, *last; - define_t *define; - - if (source->skip > 0) return qtrue; - // - if (!PC_ReadLine(source, &token)) - { - SourceError(source, "#define without name"); - return qfalse; - } //end if - if (token.type != TT_NAME) - { - PC_UnreadSourceToken(source, &token); - SourceError(source, "expected name after #define, found %s", token.string); - return qfalse; - } //end if - //check if the define already exists -#if DEFINEHASHING - define = PC_FindHashedDefine(source->definehash, token.string); -#else - define = PC_FindDefine(source->defines, token.string); -#endif //DEFINEHASHING - if (define) - { - if (define->flags & DEFINE_FIXED) - { - SourceError(source, "can't redefine %s", token.string); - return qfalse; - } //end if - SourceWarning(source, "redefinition of %s", token.string); - //unread the define name before executing the #undef directive - PC_UnreadSourceToken(source, &token); - if (!PC_Directive_undef(source)) return qfalse; - //if the define was not removed (define->flags & DEFINE_FIXED) -#if DEFINEHASHING - define = PC_FindHashedDefine(source->definehash, token.string); -#else - define = PC_FindDefine(source->defines, token.string); -#endif //DEFINEHASHING - } //end if - //allocate define - define = (define_t *) GetMemory(sizeof(define_t) + strlen(token.string) + 1); - //Com_Memset(define, 0, sizeof(define_t)); - memset(define, 0, sizeof(define_t)); - define->name = (char *) define + sizeof(define_t); - strcpy(define->name, token.string); - //add the define to the source -#if DEFINEHASHING - PC_AddDefineToHash(define, source->definehash); -#else //DEFINEHASHING - define->next = source->defines; - source->defines = define; -#endif //DEFINEHASHING - //if nothing is defined, just return - if (!PC_ReadLine(source, &token)) return qtrue; - //if it is a define with parameters - if (!PC_WhiteSpaceBeforeToken(&token) && !strcmp(token.string, "(")) - { - //read the define parameters - last = NULL; - if (!PC_CheckTokenString(source, ")")) - { - while(1) - { - if (!PC_ReadLine(source, &token)) - { - SourceError(source, "expected define parameter"); - return qfalse; - } //end if - //if it isn't a name - if (token.type != TT_NAME) - { - SourceError(source, "invalid define parameter"); - return qfalse; - } //end if - // - if (PC_FindDefineParm(define, token.string) >= 0) - { - SourceError(source, "two the same define parameters"); - return qfalse; - } //end if - //add the define parm - t = PC_CopyToken(&token); - PC_ClearTokenWhiteSpace(t); - t->next = NULL; - if (last) last->next = t; - else define->parms = t; - last = t; - define->numparms++; - //read next token - if (!PC_ReadLine(source, &token)) - { - SourceError(source, "define parameters not terminated"); - return qfalse; - } //end if - // - if (!strcmp(token.string, ")")) break; - //then it must be a comma - if (strcmp(token.string, ",")) - { - SourceError(source, "define not terminated"); - return qfalse; - } //end if - } //end while - } //end if - if (!PC_ReadLine(source, &token)) return qtrue; - } //end if - //read the defined stuff - last = NULL; - do - { - t = PC_CopyToken(&token); - if (t->type == TT_NAME && !strcmp(t->string, define->name)) - { - SourceError(source, "recursive define (removed recursion)"); - continue; - } //end if - PC_ClearTokenWhiteSpace(t); - t->next = NULL; - if (last) last->next = t; - else define->tokens = t; - last = t; - } while(PC_ReadLine(source, &token)); - // - if (last) - { - //check for merge operators at the beginning or end - if (!strcmp(define->tokens->string, "##") || - !strcmp(last->string, "##")) - { - SourceError(source, "define with misplaced ##"); - return qfalse; - } //end if - } //end if - return qtrue; -} //end of the function PC_Directive_define -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -define_t *PC_DefineFromString(char *string) -{ - script_t *script; - source_t src; - token_t *t; - int res, i; - define_t *def; - - PC_InitTokenHeap(); - - script = LoadScriptMemory(string, strlen(string), "*extern"); - //create a new source - memset(&src, 0, sizeof(source_t)); - strncpy(src.filename, "*extern", MAX_PATH); - src.scriptstack = script; -#if DEFINEHASHING - src.definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); -#endif //DEFINEHASHING - //create a define from the source - res = PC_Directive_define(&src); - //free any tokens if left - for (t = src.tokens; t; t = src.tokens) - { - src.tokens = src.tokens->next; - PC_FreeToken(t); - } //end for -#ifdef DEFINEHASHING - def = NULL; - for (i = 0; i < DEFINEHASHSIZE; i++) - { - if (src.definehash[i]) - { - def = src.definehash[i]; - break; - } //end if - } //end for -#else - def = src.defines; -#endif //DEFINEHASHING - // -#if DEFINEHASHING - FreeMemory(src.definehash); -#endif //DEFINEHASHING - // - FreeScript(script); - //if the define was created succesfully - if (res > 0) return def; - //free the define is created - if (src.defines) PC_FreeDefine(def); - // - return NULL; -} //end of the function PC_DefineFromString -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_AddDefine(source_t *source, char *string) -{ - define_t *define; - - define = PC_DefineFromString(string); - if (!define) return qfalse; -#if DEFINEHASHING - PC_AddDefineToHash(define, source->definehash); -#else //DEFINEHASHING - define->next = source->defines; - source->defines = define; -#endif //DEFINEHASHING - return qtrue; -} //end of the function PC_AddDefine -//============================================================================ -// add a globals define that will be added to all opened sources -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_AddGlobalDefine(char *string) -{ - define_t *define; - - define = PC_DefineFromString(string); - if (!define) return qfalse; - define->next = globaldefines; - globaldefines = define; - return qtrue; -} //end of the function PC_AddGlobalDefine -//============================================================================ -// remove the given global define -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_RemoveGlobalDefine(char *name) -{ - define_t *define; - - define = PC_FindDefine(globaldefines, name); - if (define) - { - PC_FreeDefine(define); - return qtrue; - } //end if - return qfalse; -} //end of the function PC_RemoveGlobalDefine -//============================================================================ -// remove all globals defines -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_RemoveAllGlobalDefines(void) -{ - define_t *define; - - for (define = globaldefines; define; define = globaldefines) - { - globaldefines = globaldefines->next; - PC_FreeDefine(define); - } //end for -} //end of the function PC_RemoveAllGlobalDefines -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -define_t *PC_CopyDefine(source_t *source, define_t *define) -{ - define_t *newdefine; - token_t *token, *newtoken, *lasttoken; - - newdefine = (define_t *) GetMemory(sizeof(define_t) + strlen(define->name) + 1); - //copy the define name - newdefine->name = (char *) newdefine + sizeof(define_t); - strcpy(newdefine->name, define->name); - newdefine->flags = define->flags; - newdefine->builtin = define->builtin; - newdefine->numparms = define->numparms; - //the define is not linked - newdefine->next = NULL; - newdefine->hashnext = NULL; - //copy the define tokens - newdefine->tokens = NULL; - for (lasttoken = NULL, token = define->tokens; token; token = token->next) - { - newtoken = PC_CopyToken(token); - newtoken->next = NULL; - if (lasttoken) lasttoken->next = newtoken; - else newdefine->tokens = newtoken; - lasttoken = newtoken; - } //end for - //copy the define parameters - newdefine->parms = NULL; - for (lasttoken = NULL, token = define->parms; token; token = token->next) - { - newtoken = PC_CopyToken(token); - newtoken->next = NULL; - if (lasttoken) lasttoken->next = newtoken; - else newdefine->parms = newtoken; - lasttoken = newtoken; - } //end for - return newdefine; -} //end of the function PC_CopyDefine -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_AddGlobalDefinesToSource(source_t *source) -{ - define_t *define, *newdefine; - - for (define = globaldefines; define; define = define->next) - { - newdefine = PC_CopyDefine(source, define); -#if DEFINEHASHING - PC_AddDefineToHash(newdefine, source->definehash); -#else //DEFINEHASHING - newdefine->next = source->defines; - source->defines = newdefine; -#endif //DEFINEHASHING - } //end for -} //end of the function PC_AddGlobalDefinesToSource -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_if_def(source_t *source, int type) -{ - token_t token; - define_t *d; - int skip; - - if (!PC_ReadLine(source, &token)) - { - SourceError(source, "#ifdef without name"); - return qfalse; - } //end if - if (token.type != TT_NAME) - { - PC_UnreadSourceToken(source, &token); - SourceError(source, "expected name after #ifdef, found %s", token.string); - return qfalse; - } //end if -#if DEFINEHASHING - d = PC_FindHashedDefine(source->definehash, token.string); -#else - d = PC_FindDefine(source->defines, token.string); -#endif //DEFINEHASHING - skip = (type == INDENT_IFDEF) == (d == NULL); - PC_PushIndent(source, type, skip); - return qtrue; -} //end of the function PC_Directiveif_def -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_ifdef(source_t *source) -{ - return PC_Directive_if_def(source, INDENT_IFDEF); -} //end of the function PC_Directive_ifdef -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_ifndef(source_t *source) -{ - return PC_Directive_if_def(source, INDENT_IFNDEF); -} //end of the function PC_Directive_ifndef -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_else(source_t *source) -{ - int type, skip; - - PC_PopIndent(source, &type, &skip); - if (!type) - { - SourceError(source, "misplaced #else"); - return qfalse; - } //end if - if (type == INDENT_ELSE) - { - SourceError(source, "#else after #else"); - return qfalse; - } //end if - PC_PushIndent(source, INDENT_ELSE, !skip); - return qtrue; -} //end of the function PC_Directive_else -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_endif(source_t *source) -{ - int type, skip; - - PC_PopIndent(source, &type, &skip); - if (!type) - { - SourceError(source, "misplaced #endif"); - return qfalse; - } //end if - return qtrue; -} //end of the function PC_Directive_endif -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -typedef struct operator_s -{ - int operator; - int priority; - int parentheses; - struct operator_s *prev, *next; -} operator_t; - -typedef struct value_s -{ - signed long int intvalue; - double floatvalue; - int parentheses; - struct value_s *prev, *next; -} value_t; - -int PC_OperatorPriority(int op) -{ - switch(op) - { - case P_MUL: return 15; - case P_DIV: return 15; - case P_MOD: return 15; - case P_ADD: return 14; - case P_SUB: return 14; - - case P_LOGIC_AND: return 7; - case P_LOGIC_OR: return 6; - case P_LOGIC_GEQ: return 12; - case P_LOGIC_LEQ: return 12; - case P_LOGIC_EQ: return 11; - case P_LOGIC_UNEQ: return 11; - - case P_LOGIC_NOT: return 16; - case P_LOGIC_GREATER: return 12; - case P_LOGIC_LESS: return 12; - - case P_RSHIFT: return 13; - case P_LSHIFT: return 13; - - case P_BIN_AND: return 10; - case P_BIN_OR: return 8; - case P_BIN_XOR: return 9; - case P_BIN_NOT: return 16; - - case P_COLON: return 5; - case P_QUESTIONMARK: return 5; - } //end switch - return qfalse; -} //end of the function PC_OperatorPriority - -//#define AllocValue() GetClearedMemory(sizeof(value_t)); -//#define FreeValue(val) FreeMemory(val) -//#define AllocOperator(op) op = (operator_t *) GetClearedMemory(sizeof(operator_t)); -//#define FreeOperator(op) FreeMemory(op); - -#define MAX_VALUES 64 -#define MAX_OPERATORS 64 -#define AllocValue(val) \ - if (numvalues >= MAX_VALUES) { \ - SourceError(source, "out of value space\n"); \ - error = 1; \ - break; \ - } \ - else \ - val = &value_heap[numvalues++]; -#define FreeValue(val) -// -#define AllocOperator(op) \ - if (numoperators >= MAX_OPERATORS) { \ - SourceError(source, "out of operator space\n"); \ - error = 1; \ - break; \ - } \ - else \ - op = &operator_heap[numoperators++]; -#define FreeOperator(op) - -int PC_EvaluateTokens(source_t *source, token_t *tokens, signed long int *intvalue, - double *floatvalue, int integer) -{ - operator_t *o, *firstoperator, *lastoperator; - value_t *v, *firstvalue, *lastvalue, *v1, *v2; - token_t *t; - int brace = 0; - int parentheses = 0; - int error = 0; - int lastwasvalue = 0; - int negativevalue = 0; - int questmarkintvalue = 0; - double questmarkfloatvalue = 0; - int gotquestmarkvalue = qfalse; - int lastoperatortype = 0; - // - operator_t operator_heap[MAX_OPERATORS]; - int numoperators = 0; - value_t value_heap[MAX_VALUES]; - int numvalues = 0; - - firstoperator = lastoperator = NULL; - firstvalue = lastvalue = NULL; - if (intvalue) *intvalue = 0; - if (floatvalue) *floatvalue = 0; - for (t = tokens; t; t = t->next) - { - switch(t->type) - { - case TT_NAME: - { - if (lastwasvalue || negativevalue) - { - SourceError(source, "syntax error in #if/#elif"); - error = 1; - break; - } //end if - if (strcmp(t->string, "defined")) - { - SourceError(source, "undefined name %s in #if/#elif", t->string); - error = 1; - break; - } //end if - t = t->next; - if (!strcmp(t->string, "(")) - { - brace = qtrue; - t = t->next; - } //end if - if (!t || t->type != TT_NAME) - { - SourceError(source, "defined without name in #if/#elif"); - error = 1; - break; - } //end if - //v = (value_t *) GetClearedMemory(sizeof(value_t)); - AllocValue(v); -#if DEFINEHASHING - if (PC_FindHashedDefine(source->definehash, t->string)) -#else - if (PC_FindDefine(source->defines, t->string)) -#endif //DEFINEHASHING - { - v->intvalue = 1; - v->floatvalue = 1; - } //end if - else - { - v->intvalue = 0; - v->floatvalue = 0; - } //end else - v->parentheses = parentheses; - v->next = NULL; - v->prev = lastvalue; - if (lastvalue) lastvalue->next = v; - else firstvalue = v; - lastvalue = v; - if (brace) - { - t = t->next; - if (!t || strcmp(t->string, ")")) - { - SourceError(source, "defined without ) in #if/#elif"); - error = 1; - break; - } //end if - } //end if - brace = qfalse; - // defined() creates a value - lastwasvalue = 1; - break; - } //end case - case TT_NUMBER: - { - if (lastwasvalue) - { - SourceError(source, "syntax error in #if/#elif"); - error = 1; - break; - } //end if - //v = (value_t *) GetClearedMemory(sizeof(value_t)); - AllocValue(v); - if (negativevalue) - { - v->intvalue = - (signed int) t->intvalue; - v->floatvalue = - t->floatvalue; - } //end if - else - { - v->intvalue = t->intvalue; - v->floatvalue = t->floatvalue; - } //end else - v->parentheses = parentheses; - v->next = NULL; - v->prev = lastvalue; - if (lastvalue) lastvalue->next = v; - else firstvalue = v; - lastvalue = v; - //last token was a value - lastwasvalue = 1; - // - negativevalue = 0; - break; - } //end case - case TT_PUNCTUATION: - { - if (negativevalue) - { - SourceError(source, "misplaced minus sign in #if/#elif"); - error = 1; - break; - } //end if - if (t->subtype == P_PARENTHESESOPEN) - { - parentheses++; - break; - } //end if - else if (t->subtype == P_PARENTHESESCLOSE) - { - parentheses--; - if (parentheses < 0) - { - SourceError(source, "too many ) in #if/#elsif"); - error = 1; - } //end if - break; - } //end else if - //check for invalid operators on floating point values - if (!integer) - { - if (t->subtype == P_BIN_NOT || t->subtype == P_MOD || - t->subtype == P_RSHIFT || t->subtype == P_LSHIFT || - t->subtype == P_BIN_AND || t->subtype == P_BIN_OR || - t->subtype == P_BIN_XOR) - { - SourceError(source, "illigal operator %s on floating point operands\n", t->string); - error = 1; - break; - } //end if - } //end if - switch(t->subtype) - { - case P_LOGIC_NOT: - case P_BIN_NOT: - { - if (lastwasvalue) - { - SourceError(source, "! or ~ after value in #if/#elif"); - error = 1; - break; - } //end if - break; - } //end case - case P_INC: - case P_DEC: - { - SourceError(source, "++ or -- used in #if/#elif"); - break; - } //end case - case P_SUB: - { - if (!lastwasvalue) - { - negativevalue = 1; - break; - } //end if - } //end case - - case P_MUL: - case P_DIV: - case P_MOD: - case P_ADD: - - case P_LOGIC_AND: - case P_LOGIC_OR: - case P_LOGIC_GEQ: - case P_LOGIC_LEQ: - case P_LOGIC_EQ: - case P_LOGIC_UNEQ: - - case P_LOGIC_GREATER: - case P_LOGIC_LESS: - - case P_RSHIFT: - case P_LSHIFT: - - case P_BIN_AND: - case P_BIN_OR: - case P_BIN_XOR: - - case P_COLON: - case P_QUESTIONMARK: - { - if (!lastwasvalue) - { - SourceError(source, "operator %s after operator in #if/#elif", t->string); - error = 1; - break; - } //end if - break; - } //end case - default: - { - SourceError(source, "invalid operator %s in #if/#elif", t->string); - error = 1; - break; - } //end default - } //end switch - if (!error && !negativevalue) - { - //o = (operator_t *) GetClearedMemory(sizeof(operator_t)); - AllocOperator(o); - o->operator = t->subtype; - o->priority = PC_OperatorPriority(t->subtype); - o->parentheses = parentheses; - o->next = NULL; - o->prev = lastoperator; - if (lastoperator) lastoperator->next = o; - else firstoperator = o; - lastoperator = o; - lastwasvalue = 0; - } //end if - break; - } //end case - default: - { - SourceError(source, "unknown %s in #if/#elif", t->string); - error = 1; - break; - } //end default - } //end switch - if (error) break; - } //end for - if (!error) - { - if (!lastwasvalue) - { - SourceError(source, "trailing operator in #if/#elif"); - error = 1; - } //end if - else if (parentheses) - { - SourceError(source, "too many ( in #if/#elif"); - error = 1; - } //end else if - } //end if - // - gotquestmarkvalue = qfalse; - questmarkintvalue = 0; - questmarkfloatvalue = 0; - //while there are operators - while(!error && firstoperator) - { - v = firstvalue; - for (o = firstoperator; o->next; o = o->next) - { - //if the current operator is nested deeper in parentheses - //than the next operator - if (o->parentheses > o->next->parentheses) break; - //if the current and next operator are nested equally deep in parentheses - if (o->parentheses == o->next->parentheses) - { - //if the priority of the current operator is equal or higher - //than the priority of the next operator - if (o->priority >= o->next->priority) break; - } //end if - //if the arity of the operator isn't equal to 1 - if (o->operator != P_LOGIC_NOT - && o->operator != P_BIN_NOT) v = v->next; - //if there's no value or no next value - if (!v) - { - SourceError(source, "mising values in #if/#elif"); - error = 1; - break; - } //end if - } //end for - if (error) break; - v1 = v; - v2 = v->next; -#ifdef DEBUG_EVAL - if (integer) - { - Log_Write("operator %s, value1 = %d", PunctuationFromNum(source->scriptstack, o->operator), v1->intvalue); - if (v2) Log_Write("value2 = %d", v2->intvalue); - } //end if - else - { - Log_Write("operator %s, value1 = %f", PunctuationFromNum(source->scriptstack, o->operator), v1->floatvalue); - if (v2) Log_Write("value2 = %f", v2->floatvalue); - } //end else -#endif //DEBUG_EVAL - switch(o->operator) - { - case P_LOGIC_NOT: v1->intvalue = !v1->intvalue; - v1->floatvalue = !v1->floatvalue; break; - case P_BIN_NOT: v1->intvalue = ~v1->intvalue; - break; - case P_MUL: v1->intvalue *= v2->intvalue; - v1->floatvalue *= v2->floatvalue; break; - case P_DIV: if (!v2->intvalue || !v2->floatvalue) - { - SourceError(source, "divide by zero in #if/#elif\n"); - error = 1; - break; - } - v1->intvalue /= v2->intvalue; - v1->floatvalue /= v2->floatvalue; break; - case P_MOD: if (!v2->intvalue) - { - SourceError(source, "divide by zero in #if/#elif\n"); - error = 1; - break; - } - v1->intvalue %= v2->intvalue; break; - case P_ADD: v1->intvalue += v2->intvalue; - v1->floatvalue += v2->floatvalue; break; - case P_SUB: v1->intvalue -= v2->intvalue; - v1->floatvalue -= v2->floatvalue; break; - case P_LOGIC_AND: v1->intvalue = v1->intvalue && v2->intvalue; - v1->floatvalue = v1->floatvalue && v2->floatvalue; break; - case P_LOGIC_OR: v1->intvalue = v1->intvalue || v2->intvalue; - v1->floatvalue = v1->floatvalue || v2->floatvalue; break; - case P_LOGIC_GEQ: v1->intvalue = v1->intvalue >= v2->intvalue; - v1->floatvalue = v1->floatvalue >= v2->floatvalue; break; - case P_LOGIC_LEQ: v1->intvalue = v1->intvalue <= v2->intvalue; - v1->floatvalue = v1->floatvalue <= v2->floatvalue; break; - case P_LOGIC_EQ: v1->intvalue = v1->intvalue == v2->intvalue; - v1->floatvalue = v1->floatvalue == v2->floatvalue; break; - case P_LOGIC_UNEQ: v1->intvalue = v1->intvalue != v2->intvalue; - v1->floatvalue = v1->floatvalue != v2->floatvalue; break; - case P_LOGIC_GREATER: v1->intvalue = v1->intvalue > v2->intvalue; - v1->floatvalue = v1->floatvalue > v2->floatvalue; break; - case P_LOGIC_LESS: v1->intvalue = v1->intvalue < v2->intvalue; - v1->floatvalue = v1->floatvalue < v2->floatvalue; break; - case P_RSHIFT: v1->intvalue >>= v2->intvalue; - break; - case P_LSHIFT: v1->intvalue <<= v2->intvalue; - break; - case P_BIN_AND: v1->intvalue &= v2->intvalue; - break; - case P_BIN_OR: v1->intvalue |= v2->intvalue; - break; - case P_BIN_XOR: v1->intvalue ^= v2->intvalue; - break; - case P_COLON: - { - if (!gotquestmarkvalue) - { - SourceError(source, ": without ? in #if/#elif"); - error = 1; - break; - } //end if - if (integer) - { - if (!questmarkintvalue) v1->intvalue = v2->intvalue; - } //end if - else - { - if (!questmarkfloatvalue) v1->floatvalue = v2->floatvalue; - } //end else - gotquestmarkvalue = qfalse; - break; - } //end case - case P_QUESTIONMARK: - { - if (gotquestmarkvalue) - { - SourceError(source, "? after ? in #if/#elif"); - error = 1; - break; - } //end if - questmarkintvalue = v1->intvalue; - questmarkfloatvalue = v1->floatvalue; - gotquestmarkvalue = qtrue; - break; - } //end if - } //end switch -#ifdef DEBUG_EVAL - if (integer) Log_Write("result value = %d", v1->intvalue); - else Log_Write("result value = %f", v1->floatvalue); -#endif //DEBUG_EVAL - if (error) break; - lastoperatortype = o->operator; - //if not an operator with arity 1 - if (o->operator != P_LOGIC_NOT - && o->operator != P_BIN_NOT) - { - //remove the second value if not question mark operator - if (o->operator != P_QUESTIONMARK) v = v->next; - // - if (v->prev) v->prev->next = v->next; - else firstvalue = v->next; - if (v->next) v->next->prev = v->prev; - else lastvalue = v->prev; - //FreeMemory(v); - FreeValue(v); - } //end if - //remove the operator - if (o->prev) o->prev->next = o->next; - else firstoperator = o->next; - if (o->next) o->next->prev = o->prev; - else lastoperator = o->prev; - //FreeMemory(o); - FreeOperator(o); - } //end while - if (firstvalue) - { - if (intvalue) *intvalue = firstvalue->intvalue; - if (floatvalue) *floatvalue = firstvalue->floatvalue; - } //end if - for (o = firstoperator; o; o = lastoperator) - { - lastoperator = o->next; - //FreeMemory(o); - FreeOperator(o); - } //end for - for (v = firstvalue; v; v = lastvalue) - { - lastvalue = v->next; - //FreeMemory(v); - FreeValue(v); - } //end for - if (!error) return qtrue; - if (intvalue) *intvalue = 0; - if (floatvalue) *floatvalue = 0; - return qfalse; -} //end of the function PC_EvaluateTokens -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Evaluate(source_t *source, signed long int *intvalue, - double *floatvalue, int integer) -{ - token_t token, *firsttoken, *lasttoken; - token_t *t, *nexttoken; - define_t *define; - int defined = qfalse; - - if (intvalue) *intvalue = 0; - if (floatvalue) *floatvalue = 0; - // - if (!PC_ReadLine(source, &token)) - { - SourceError(source, "no value after #if/#elif"); - return qfalse; - } //end if - firsttoken = NULL; - lasttoken = NULL; - do - { - //if the token is a name - if (token.type == TT_NAME) - { - if (defined) - { - defined = qfalse; - t = PC_CopyToken(&token); - t->next = NULL; - if (lasttoken) lasttoken->next = t; - else firsttoken = t; - lasttoken = t; - } //end if - else if (!strcmp(token.string, "defined")) - { - defined = qtrue; - t = PC_CopyToken(&token); - t->next = NULL; - if (lasttoken) lasttoken->next = t; - else firsttoken = t; - lasttoken = t; - } //end if - else - { - //then it must be a define -#if DEFINEHASHING - define = PC_FindHashedDefine(source->definehash, token.string); -#else - define = PC_FindDefine(source->defines, token.string); -#endif //DEFINEHASHING - if (!define) - { - SourceError(source, "can't evaluate %s, not defined", token.string); - return qfalse; - } //end if - if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse; - } //end else - } //end if - //if the token is a number or a punctuation - else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) - { - t = PC_CopyToken(&token); - t->next = NULL; - if (lasttoken) lasttoken->next = t; - else firsttoken = t; - lasttoken = t; - } //end else - else //can't evaluate the token - { - SourceError(source, "can't evaluate %s", token.string); - return qfalse; - } //end else - } while(PC_ReadLine(source, &token)); - // - if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse; - // -#ifdef DEBUG_EVAL - Log_Write("eval:"); -#endif //DEBUG_EVAL - for (t = firsttoken; t; t = nexttoken) - { -#ifdef DEBUG_EVAL - Log_Write(" %s", t->string); -#endif //DEBUG_EVAL - nexttoken = t->next; - PC_FreeToken(t); - } //end for -#ifdef DEBUG_EVAL - if (integer) Log_Write("eval result: %d", *intvalue); - else Log_Write("eval result: %f", *floatvalue); -#endif //DEBUG_EVAL - // - return qtrue; -} //end of the function PC_Evaluate -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_DollarEvaluate(source_t *source, signed long int *intvalue, - double *floatvalue, int integer) -{ - int indent, defined = qfalse; - token_t token, *firsttoken, *lasttoken; - token_t *t, *nexttoken; - define_t *define; - - if (intvalue) *intvalue = 0; - if (floatvalue) *floatvalue = 0; - // - if (!PC_ReadSourceToken(source, &token)) - { - SourceError(source, "no leading ( after $evalint/$evalfloat"); - return qfalse; - } //end if - if (!PC_ReadSourceToken(source, &token)) - { - SourceError(source, "nothing to evaluate"); - return qfalse; - } //end if - indent = 1; - firsttoken = NULL; - lasttoken = NULL; - do - { - //if the token is a name - if (token.type == TT_NAME) - { - if (defined) - { - defined = qfalse; - t = PC_CopyToken(&token); - t->next = NULL; - if (lasttoken) lasttoken->next = t; - else firsttoken = t; - lasttoken = t; - } //end if - else if (!strcmp(token.string, "defined")) - { - defined = qtrue; - t = PC_CopyToken(&token); - t->next = NULL; - if (lasttoken) lasttoken->next = t; - else firsttoken = t; - lasttoken = t; - } //end if - else - { - //then it must be a define -#if DEFINEHASHING - define = PC_FindHashedDefine(source->definehash, token.string); -#else - define = PC_FindDefine(source->defines, token.string); -#endif //DEFINEHASHING - if (!define) - { - SourceError(source, "can't evaluate %s, not defined", token.string); - return qfalse; - } //end if - if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse; - } //end else - } //end if - //if the token is a number or a punctuation - else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) - { - if (*token.string == '(') indent++; - else if (*token.string == ')') indent--; - if (indent <= 0) break; - t = PC_CopyToken(&token); - t->next = NULL; - if (lasttoken) lasttoken->next = t; - else firsttoken = t; - lasttoken = t; - } //end else - else //can't evaluate the token - { - SourceError(source, "can't evaluate %s", token.string); - return qfalse; - } //end else - } while(PC_ReadSourceToken(source, &token)); - // - if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse; - // -#ifdef DEBUG_EVAL - Log_Write("$eval:"); -#endif //DEBUG_EVAL - for (t = firsttoken; t; t = nexttoken) - { -#ifdef DEBUG_EVAL - Log_Write(" %s", t->string); -#endif //DEBUG_EVAL - nexttoken = t->next; - PC_FreeToken(t); - } //end for -#ifdef DEBUG_EVAL - if (integer) Log_Write("$eval result: %d", *intvalue); - else Log_Write("$eval result: %f", *floatvalue); -#endif //DEBUG_EVAL - // - return qtrue; -} //end of the function PC_DollarEvaluate -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_elif(source_t *source) -{ - signed long int value; - int type, skip; - - PC_PopIndent(source, &type, &skip); - if (!type || type == INDENT_ELSE) - { - SourceError(source, "misplaced #elif"); - return qfalse; - } //end if - if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; - skip = (value == 0); - PC_PushIndent(source, INDENT_ELIF, skip); - return qtrue; -} //end of the function PC_Directive_elif -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_if(source_t *source) -{ - signed long int value; - int skip; - - if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; - skip = (value == 0); - PC_PushIndent(source, INDENT_IF, skip); - return qtrue; -} //end of the function PC_Directive -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_line(source_t *source) -{ - SourceError(source, "#line directive not supported"); - return qfalse; -} //end of the function PC_Directive_line -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_error(source_t *source) -{ - token_t token; - - strcpy(token.string, ""); - PC_ReadSourceToken(source, &token); - SourceError(source, "#error directive: %s", token.string); - return qfalse; -} //end of the function PC_Directive_error -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_pragma(source_t *source) -{ - token_t token; - - SourceWarning(source, "#pragma directive not supported"); - while(PC_ReadLine(source, &token)) ; - return qtrue; -} //end of the function PC_Directive_pragma -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void UnreadSignToken(source_t *source) -{ - token_t token; - - token.line = source->scriptstack->line; - token.whitespace_p = source->scriptstack->script_p; - token.endwhitespace_p = source->scriptstack->script_p; - token.linescrossed = 0; - strcpy(token.string, "-"); - token.type = TT_PUNCTUATION; - token.subtype = P_SUB; - PC_UnreadSourceToken(source, &token); -} //end of the function UnreadSignToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_eval(source_t *source) -{ - signed long int value; - token_t token; - - if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; - // - token.line = source->scriptstack->line; - token.whitespace_p = source->scriptstack->script_p; - token.endwhitespace_p = source->scriptstack->script_p; - token.linescrossed = 0; - sprintf(token.string, "%d", abs(value)); - token.type = TT_NUMBER; - token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL; - PC_UnreadSourceToken(source, &token); - if (value < 0) UnreadSignToken(source); - return qtrue; -} //end of the function PC_Directive_eval -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_Directive_evalfloat(source_t *source) -{ - double value; - token_t token; - - if (!PC_Evaluate(source, NULL, &value, qfalse)) return qfalse; - token.line = source->scriptstack->line; - token.whitespace_p = source->scriptstack->script_p; - token.endwhitespace_p = source->scriptstack->script_p; - token.linescrossed = 0; - sprintf(token.string, "%1.2f", fabs(value)); - token.type = TT_NUMBER; - token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL; - PC_UnreadSourceToken(source, &token); - if (value < 0) UnreadSignToken(source); - return qtrue; -} //end of the function PC_Directive_evalfloat -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -directive_t directives[20] = -{ - {"if", PC_Directive_if}, - {"ifdef", PC_Directive_ifdef}, - {"ifndef", PC_Directive_ifndef}, - {"elif", PC_Directive_elif}, - {"else", PC_Directive_else}, - {"endif", PC_Directive_endif}, - {"include", PC_Directive_include}, - {"define", PC_Directive_define}, - {"undef", PC_Directive_undef}, - {"line", PC_Directive_line}, - {"error", PC_Directive_error}, - {"pragma", PC_Directive_pragma}, - {"eval", PC_Directive_eval}, - {"evalfloat", PC_Directive_evalfloat}, - {NULL, NULL} -}; - -int PC_ReadDirective(source_t *source) -{ - token_t token; - int i; - - //read the directive name - if (!PC_ReadSourceToken(source, &token)) - { - SourceError(source, "found # without name"); - return qfalse; - } //end if - //directive name must be on the same line - if (token.linescrossed > 0) - { - PC_UnreadSourceToken(source, &token); - SourceError(source, "found # at end of line"); - return qfalse; - } //end if - //if if is a name - if (token.type == TT_NAME) - { - //find the precompiler directive - for (i = 0; directives[i].name; i++) - { - if (!strcmp(directives[i].name, token.string)) - { - return directives[i].func(source); - } //end if - } //end for - } //end if - SourceError(source, "unknown precompiler directive %s", token.string); - return qfalse; -} //end of the function PC_ReadDirective -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_DollarDirective_evalint(source_t *source) -{ - signed long int value; - token_t token; - - if (!PC_DollarEvaluate(source, &value, NULL, qtrue)) return qfalse; - // - token.line = source->scriptstack->line; - token.whitespace_p = source->scriptstack->script_p; - token.endwhitespace_p = source->scriptstack->script_p; - token.linescrossed = 0; - sprintf(token.string, "%d", abs(value)); - token.type = TT_NUMBER; - token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL; -#ifdef NUMBERVALUE - token.intvalue = value; - token.floatvalue = value; -#endif //NUMBERVALUE - PC_UnreadSourceToken(source, &token); - if (value < 0) UnreadSignToken(source); - return qtrue; -} //end of the function PC_DollarDirective_evalint -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_DollarDirective_evalfloat(source_t *source) -{ - double value; - token_t token; - - if (!PC_DollarEvaluate(source, NULL, &value, qfalse)) return qfalse; - token.line = source->scriptstack->line; - token.whitespace_p = source->scriptstack->script_p; - token.endwhitespace_p = source->scriptstack->script_p; - token.linescrossed = 0; - sprintf(token.string, "%1.2f", fabs(value)); - token.type = TT_NUMBER; - token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL; -#ifdef NUMBERVALUE - token.intvalue = (unsigned long) value; - token.floatvalue = value; -#endif //NUMBERVALUE - PC_UnreadSourceToken(source, &token); - if (value < 0) UnreadSignToken(source); - return qtrue; -} //end of the function PC_DollarDirective_evalfloat -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -directive_t dollardirectives[20] = -{ - {"evalint", PC_DollarDirective_evalint}, - {"evalfloat", PC_DollarDirective_evalfloat}, - {NULL, NULL} -}; - -int PC_ReadDollarDirective(source_t *source) -{ - token_t token; - int i; - - //read the directive name - if (!PC_ReadSourceToken(source, &token)) - { - SourceError(source, "found $ without name"); - return qfalse; - } //end if - //directive name must be on the same line - if (token.linescrossed > 0) - { - PC_UnreadSourceToken(source, &token); - SourceError(source, "found $ at end of line"); - return qfalse; - } //end if - //if if is a name - if (token.type == TT_NAME) - { - //find the precompiler directive - for (i = 0; dollardirectives[i].name; i++) - { - if (!strcmp(dollardirectives[i].name, token.string)) - { - return dollardirectives[i].func(source); - } //end if - } //end for - } //end if - PC_UnreadSourceToken(source, &token); - SourceError(source, "unknown precompiler directive %s", token.string); - return qfalse; -} //end of the function PC_ReadDirective - -#ifdef QUAKEC -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int BuiltinFunction(source_t *source) -{ - token_t token; - - if (!PC_ReadSourceToken(source, &token)) return qfalse; - if (token.type == TT_NUMBER) - { - PC_UnreadSourceToken(source, &token); - return qtrue; - } //end if - else - { - PC_UnreadSourceToken(source, &token); - return qfalse; - } //end else -} //end of the function BuiltinFunction -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int QuakeCMacro(source_t *source) -{ - int i; - token_t token; - - if (!PC_ReadSourceToken(source, &token)) return qtrue; - if (token.type != TT_NAME) - { - PC_UnreadSourceToken(source, &token); - return qtrue; - } //end if - //find the precompiler directive - for (i = 0; dollardirectives[i].name; i++) - { - if (!strcmp(dollardirectives[i].name, token.string)) - { - PC_UnreadSourceToken(source, &token); - return qfalse; - } //end if - } //end for - PC_UnreadSourceToken(source, &token); - return qtrue; -} //end of the function QuakeCMacro -#endif //QUAKEC -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_ReadToken(source_t *source, token_t *token) -{ - define_t *define; - - while(1) - { - if (!PC_ReadSourceToken(source, token)) return qfalse; - //check for precompiler directives - if (token->type == TT_PUNCTUATION && *token->string == '#') - { -#ifdef QUAKEC - if (!BuiltinFunction(source)) -#endif //QUAKC - { - //read the precompiler directive - if (!PC_ReadDirective(source)) return qfalse; - continue; - } //end if - } //end if - if (token->type == TT_PUNCTUATION && *token->string == '$') - { -#ifdef QUAKEC - if (!QuakeCMacro(source)) -#endif //QUAKEC - { - //read the precompiler directive - if (!PC_ReadDollarDirective(source)) return qfalse; - continue; - } //end if - } //end if - // recursively concatenate strings that are behind each other still resolving defines - if (token->type == TT_STRING) - { - token_t newtoken; - if (PC_ReadToken(source, &newtoken)) - { - if (newtoken.type == TT_STRING) - { - token->string[strlen(token->string)-1] = '\0'; - if (strlen(token->string) + strlen(newtoken.string+1) + 1 >= MAX_TOKEN) - { - SourceError(source, "string longer than MAX_TOKEN %d\n", MAX_TOKEN); - return qfalse; - } - strcat(token->string, newtoken.string+1); - } - else - { - PC_UnreadToken(source, &newtoken); - } - } - } //end if - //if skipping source because of conditional compilation - if (source->skip) continue; - //if the token is a name - if (token->type == TT_NAME) - { - //check if the name is a define macro -#if DEFINEHASHING - define = PC_FindHashedDefine(source->definehash, token->string); -#else - define = PC_FindDefine(source->defines, token->string); -#endif //DEFINEHASHING - //if it is a define macro - if (define) - { - //expand the defined macro - if (!PC_ExpandDefineIntoSource(source, token, define)) return qfalse; - continue; - } //end if - } //end if - //copy token for unreading - //Com_Memcpy(&source->token, token, sizeof(token_t)); - memcpy(&source->token, token, sizeof(token_t)); - //found a token - return qtrue; - } //end while -} //end of the function PC_ReadToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_ExpectTokenString(source_t *source, char *string) -{ - token_t token; - - if (!PC_ReadToken(source, &token)) - { - SourceError(source, "couldn't find expected %s", string); - return qfalse; - } //end if - - if (strcmp(token.string, string)) - { - SourceError(source, "expected %s, found %s", string, token.string); - return qfalse; - } //end if - return qtrue; -} //end of the function PC_ExpectTokenString -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token) -{ - char str[MAX_TOKEN]; - - if (!PC_ReadToken(source, token)) - { - SourceError(source, "couldn't read expected token"); - return qfalse; - } //end if - - if (token->type != type) - { - strcpy(str, ""); - if (type == TT_STRING) strcpy(str, "string"); - if (type == TT_LITERAL) strcpy(str, "literal"); - if (type == TT_NUMBER) strcpy(str, "number"); - if (type == TT_NAME) strcpy(str, "name"); - if (type == TT_PUNCTUATION) strcpy(str, "punctuation"); - SourceError(source, "expected a %s, found %s", str, token->string); - return qfalse; - } //end if - if (token->type == TT_NUMBER) - { - if ((token->subtype & subtype) != subtype) - { - if (subtype & TT_DECIMAL) strcpy(str, "decimal"); - if (subtype & TT_HEX) strcpy(str, "hex"); - if (subtype & TT_OCTAL) strcpy(str, "octal"); - if (subtype & TT_BINARY) strcpy(str, "binary"); - if (subtype & TT_LONG) strcat(str, " long"); - if (subtype & TT_UNSIGNED) strcat(str, " unsigned"); - if (subtype & TT_FLOAT) strcat(str, " float"); - if (subtype & TT_INTEGER) strcat(str, " integer"); - SourceError(source, "expected %s, found %s", str, token->string); - return qfalse; - } //end if - } //end if - else if (token->type == TT_PUNCTUATION) - { - if (token->subtype != subtype) - { - SourceError(source, "found %s", token->string); - return qfalse; - } //end if - } //end else if - return qtrue; -} //end of the function PC_ExpectTokenType -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_ExpectAnyToken(source_t *source, token_t *token) -{ - if (!PC_ReadToken(source, token)) - { - SourceError(source, "couldn't read expected token"); - return qfalse; - } //end if - else - { - return qtrue; - } //end else -} //end of the function PC_ExpectAnyToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_CheckTokenString(source_t *source, char *string) -{ - token_t tok; - - if (!PC_ReadToken(source, &tok)) return qfalse; - //if the token is available - if (!strcmp(tok.string, string)) return qtrue; - // - PC_UnreadSourceToken(source, &tok); - return qfalse; -} //end of the function PC_CheckTokenString -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token) -{ - token_t tok; - - if (!PC_ReadToken(source, &tok)) return qfalse; - //if the type matches - if (tok.type == type && - (tok.subtype & subtype) == subtype) - { - memcpy(token, &tok, sizeof(token_t)); - return qtrue; - } //end if - // - PC_UnreadSourceToken(source, &tok); - return qfalse; -} //end of the function PC_CheckTokenType -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_SkipUntilString(source_t *source, char *string) -{ - token_t token; - - while(PC_ReadToken(source, &token)) - { - if (!strcmp(token.string, string)) return qtrue; - } //end while - return qfalse; -} //end of the function PC_SkipUntilString -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_UnreadLastToken(source_t *source) -{ - PC_UnreadSourceToken(source, &source->token); -} //end of the function PC_UnreadLastToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_UnreadToken(source_t *source, token_t *token) -{ - PC_UnreadSourceToken(source, token); -} //end of the function PC_UnreadToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_SetIncludePath(source_t *source, char *path) -{ - strncpy(source->includepath, path, MAX_PATH); - //add trailing path seperator - if (source->includepath[strlen(source->includepath)-1] != '\\' && - source->includepath[strlen(source->includepath)-1] != '/') - { - strcat(source->includepath, PATHSEPERATOR_STR); - } //end if -} //end of the function PC_SetIncludePath -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_SetPunctuations(source_t *source, punctuation_t *p) -{ - source->punctuations = p; -} //end of the function PC_SetPunctuations -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -source_t *LoadSourceFile(const char *filename) -{ - source_t *source; - script_t *script; - - PC_InitTokenHeap(); - - script = LoadScriptFile(filename); - if (!script) return NULL; - - script->next = NULL; - - source = (source_t *) GetMemory(sizeof(source_t)); - memset(source, 0, sizeof(source_t)); - - strncpy(source->filename, filename, MAX_PATH); - source->scriptstack = script; - source->tokens = NULL; - source->defines = NULL; - source->indentstack = NULL; - source->skip = 0; - -#if DEFINEHASHING - source->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); -#endif //DEFINEHASHING - PC_AddGlobalDefinesToSource(source); - return source; -} //end of the function LoadSourceFile -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -source_t *LoadSourceMemory(char *ptr, int length, char *name) -{ - source_t *source; - script_t *script; - - PC_InitTokenHeap(); - - script = LoadScriptMemory(ptr, length, name); - if (!script) return NULL; - script->next = NULL; - - source = (source_t *) GetMemory(sizeof(source_t)); - //Com_Memset(source, 0, sizeof(source_t)); - memset(source, 0, sizeof(source_t)); - strncpy(source->filename, name, MAX_PATH); - source->scriptstack = script; - source->tokens = NULL; - source->defines = NULL; - source->indentstack = NULL; - source->skip = 0; - -#if DEFINEHASHING - source->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); -#endif //DEFINEHASHING - PC_AddGlobalDefinesToSource(source); - return source; -} //end of the function LoadSourceMemory -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void FreeSource(source_t *source) -{ - script_t *script; - token_t *token; - define_t *define; - indent_t *indent; - int i; - - //PC_PrintDefineHashTable(source->definehash); - //free all the scripts - while(source->scriptstack) - { - script = source->scriptstack; - source->scriptstack = source->scriptstack->next; - FreeScript(script); - } //end for - //free all the tokens - while(source->tokens) - { - token = source->tokens; - source->tokens = source->tokens->next; - PC_FreeToken(token); - } //end for -#if DEFINEHASHING - for (i = 0; i < DEFINEHASHSIZE; i++) - { - while(source->definehash[i]) - { - define = source->definehash[i]; - source->definehash[i] = source->definehash[i]->hashnext; - PC_FreeDefine(define); - } //end while - } //end for -#else //DEFINEHASHING - //free all defines - while(source->defines) - { - define = source->defines; - source->defines = source->defines->next; - PC_FreeDefine(define); - } //end for -#endif //DEFINEHASHING - //free all indents - while(source->indentstack) - { - indent = source->indentstack; - source->indentstack = source->indentstack->next; - FreeMemory(indent); - } //end for -#if DEFINEHASHING - // - if (source->definehash) FreeMemory(source->definehash); -#endif //DEFINEHASHING - //free the source itself - FreeMemory(source); -} //end of the function FreeSource -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ - -#define MAX_SOURCEFILES 64 - -source_t *sourceFiles[MAX_SOURCEFILES]; - -int PC_LoadSourceHandle(const char *filename) -{ - source_t *source; - int i; - - for (i = 1; i < MAX_SOURCEFILES; i++) - { - if (!sourceFiles[i]) - break; - } //end for - if (i >= MAX_SOURCEFILES) - return 0; - PS_SetBaseFolder(""); - source = LoadSourceFile(filename); - if (!source) - return 0; - sourceFiles[i] = source; - return i; -} //end of the function PC_LoadSourceHandle -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_FreeSourceHandle(int handle) -{ - if (handle < 1 || handle >= MAX_SOURCEFILES) - return qfalse; - if (!sourceFiles[handle]) - return qfalse; - - FreeSource(sourceFiles[handle]); - sourceFiles[handle] = NULL; - return qtrue; -} //end of the function PC_FreeSourceHandle -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_ReadTokenHandle(int handle, pc_token_t *pc_token) -{ - token_t token; - int ret; - - if (handle < 1 || handle >= MAX_SOURCEFILES) - return 0; - if (!sourceFiles[handle]) - return 0; - - ret = PC_ReadToken(sourceFiles[handle], &token); - strcpy(pc_token->string, token.string); - pc_token->type = token.type; - pc_token->subtype = token.subtype; - pc_token->intvalue = token.intvalue; - pc_token->floatvalue = token.floatvalue; - if (pc_token->type == TT_STRING) - StripDoubleQuotes(pc_token->string); - return ret; -} //end of the function PC_ReadTokenHandle -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PC_SourceFileAndLine(int handle, char *filename, int *line) -{ - if (handle < 1 || handle >= MAX_SOURCEFILES) - return qfalse; - if (!sourceFiles[handle]) - return qfalse; - - strcpy(filename, sourceFiles[handle]->filename); - if (sourceFiles[handle]->scriptstack) - *line = sourceFiles[handle]->scriptstack->line; - else - *line = 0; - return qtrue; -} //end of the function PC_SourceFileAndLine -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_SetBaseFolder(char *path) -{ - PS_SetBaseFolder(path); -} //end of the function PC_SetBaseFolder -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PC_CheckOpenSourceHandles(void) -{ - int i; - - for (i = 1; i < MAX_SOURCEFILES; i++) - { - if (sourceFiles[i]) - { -#ifdef BOTLIB - botimport.Print(PRT_ERROR, "file %s still open in precompiler\n", sourceFiles[i]->scriptstack->filename); -#endif //BOTLIB - } //end if - } //end for -} //end of the function PC_CheckOpenSourceHandles - diff --git a/tools/quake3/bspc/deps/botlib/l_precomp.h b/tools/quake3/bspc/deps/botlib/l_precomp.h deleted file mode 100644 index aa929a87..00000000 --- a/tools/quake3/bspc/deps/botlib/l_precomp.h +++ /dev/null @@ -1,180 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_precomp.h - * - * desc: pre compiler - * - * $Archive: /source/code/botlib/l_precomp.h $ - * - *****************************************************************************/ - -#ifndef MAX_PATH - #define MAX_PATH MAX_QPATH -#endif - -#ifndef PATH_SEPERATORSTR - #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) - #define PATHSEPERATOR_STR "\\" - #else - #define PATHSEPERATOR_STR "/" - #endif -#endif -#ifndef PATH_SEPERATORCHAR - #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) - #define PATHSEPERATOR_CHAR '\\' - #else - #define PATHSEPERATOR_CHAR '/' - #endif -#endif - -#if defined(BSPC) && !defined(QDECL) -#define QDECL -#endif - - -#define DEFINE_FIXED 0x0001 - -#define BUILTIN_LINE 1 -#define BUILTIN_FILE 2 -#define BUILTIN_DATE 3 -#define BUILTIN_TIME 4 -#define BUILTIN_STDC 5 - -#define INDENT_IF 0x0001 -#define INDENT_ELSE 0x0002 -#define INDENT_ELIF 0x0004 -#define INDENT_IFDEF 0x0008 -#define INDENT_IFNDEF 0x0010 - -//macro definitions -typedef struct define_s -{ - char *name; //define name - int flags; //define flags - int builtin; // > 0 if builtin define - int numparms; //number of define parameters - token_t *parms; //define parameters - token_t *tokens; //macro tokens (possibly containing parm tokens) - struct define_s *next; //next defined macro in a list - struct define_s *hashnext; //next define in the hash chain -} define_t; - -//indents -//used for conditional compilation directives: -//#if, #else, #elif, #ifdef, #ifndef -typedef struct indent_s -{ - int type; //indent type - int skip; //true if skipping current indent - script_t *script; //script the indent was in - struct indent_s *next; //next indent on the indent stack -} indent_t; - -//source file -typedef struct source_s -{ - char filename[1024]; //file name of the script - char includepath[1024]; //path to include files - punctuation_t *punctuations; //punctuations to use - script_t *scriptstack; //stack with scripts of the source - token_t *tokens; //tokens to read first - define_t *defines; //list with macro definitions - define_t **definehash; //hash chain with defines - indent_t *indentstack; //stack with indents - int skip; // > 0 if skipping conditional code - token_t token; //last read token -} source_t; - - -//read a token from the source -int PC_ReadToken(source_t *source, token_t *token); -//expect a certain token -int PC_ExpectTokenString(source_t *source, char *string); -//expect a certain token type -int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token); -//expect a token -int PC_ExpectAnyToken(source_t *source, token_t *token); -//returns true when the token is available -int PC_CheckTokenString(source_t *source, char *string); -//returns true an reads the token when a token with the given type is available -int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token); -//skip tokens until the given token string is read -int PC_SkipUntilString(source_t *source, char *string); -//unread the last token read from the script -void PC_UnreadLastToken(source_t *source); -//unread the given token -void PC_UnreadToken(source_t *source, token_t *token); -//read a token only if on the same line, lines are concatenated with a slash -int PC_ReadLine(source_t *source, token_t *token); -//returns true if there was a white space in front of the token -int PC_WhiteSpaceBeforeToken(token_t *token); -//add a define to the source -int PC_AddDefine(source_t *source, char *string); -//add a globals define that will be added to all opened sources -int PC_AddGlobalDefine(char *string); -//remove the given global define -int PC_RemoveGlobalDefine(char *name); -//remove all globals defines -void PC_RemoveAllGlobalDefines(void); -//add builtin defines -void PC_AddBuiltinDefines(source_t *source); -//set the source include path -void PC_SetIncludePath(source_t *source, char *path); -//set the punction set -void PC_SetPunctuations(source_t *source, punctuation_t *p); -//set the base folder to load files from -void PC_SetBaseFolder(char *path); -//load a source file -source_t *LoadSourceFile(const char *filename); -//load a source from memory -source_t *LoadSourceMemory(char *ptr, int length, char *name); -//free the given source -void FreeSource(source_t *source); -//print a source error -void QDECL SourceError(source_t *source, char *str, ...); -//print a source warning -void QDECL SourceWarning(source_t *source, char *str, ...); - -#ifdef BSPC -// some of BSPC source does include game/qcommon/q_shared.h and some does not -// we define pc_token_s pc_token_t if needed (yes, it's ugly) -#ifndef __Q_SHARED_H -#define MAX_TOKENLENGTH 1024 -typedef struct pc_token_s -{ - int type; - int subtype; - int intvalue; - float floatvalue; - char string[MAX_TOKENLENGTH]; -} pc_token_t; -#endif //!_Q_SHARED_H -#endif //BSPC - -// -int PC_LoadSourceHandle(const char *filename); -int PC_FreeSourceHandle(int handle); -int PC_ReadTokenHandle(int handle, pc_token_t *pc_token); -int PC_SourceFileAndLine(int handle, char *filename, int *line); -void PC_CheckOpenSourceHandles(void); diff --git a/tools/quake3/bspc/deps/botlib/l_script.c b/tools/quake3/bspc/deps/botlib/l_script.c deleted file mode 100644 index f19aba73..00000000 --- a/tools/quake3/bspc/deps/botlib/l_script.c +++ /dev/null @@ -1,1441 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_script.c - * - * desc: lexicographical parser - * - * $Archive: /MissionPack/code/botlib/l_script.c $ - * - *****************************************************************************/ - -//#define SCREWUP -//#define BOTLIB -//#define MEQCC -//#define BSPC - -#ifdef SCREWUP -#include -#include -#include -#include -#include -#include "l_memory.h" -#include "l_script.h" - -typedef enum {qfalse, qtrue} qboolean; - -#endif //SCREWUP - -#ifdef BOTLIB -//include files for usage in the bot library -#include "qcommon/q_shared.h" -#include "botlib.h" -#include "be_interface.h" -#include "l_script.h" -#include "l_memory.h" -#include "l_log.h" -#include "l_libvar.h" -#endif //BOTLIB - -#ifdef MEQCC -//include files for usage in MrElusive's QuakeC Compiler -#include "qcc.h" -#include "l_script.h" -#include "l_memory.h" -#include "l_log.h" - -#define qtrue true -#define qfalse false -#endif //MEQCC - -#ifdef BSPC -//include files for usage in the BSP Converter -#include "../../qbsp.h" -#include "l_log.h" -#include "../../l_mem.h" -#include "../../l_cmd.h" - -#define qtrue true -#define qfalse false -#endif //BSPC - - -#define PUNCTABLE - -//longer punctuations first -punctuation_t default_punctuations[] = -{ - //binary operators - {">>=",P_RSHIFT_ASSIGN, NULL}, - {"<<=",P_LSHIFT_ASSIGN, NULL}, - // - {"...",P_PARMS, NULL}, - //define merge operator - {"##",P_PRECOMPMERGE, NULL}, - //logic operators - {"&&",P_LOGIC_AND, NULL}, - {"||",P_LOGIC_OR, NULL}, - {">=",P_LOGIC_GEQ, NULL}, - {"<=",P_LOGIC_LEQ, NULL}, - {"==",P_LOGIC_EQ, NULL}, - {"!=",P_LOGIC_UNEQ, NULL}, - //arithmatic operators - {"*=",P_MUL_ASSIGN, NULL}, - {"/=",P_DIV_ASSIGN, NULL}, - {"%=",P_MOD_ASSIGN, NULL}, - {"+=",P_ADD_ASSIGN, NULL}, - {"-=",P_SUB_ASSIGN, NULL}, - {"++",P_INC, NULL}, - {"--",P_DEC, NULL}, - //binary operators - {"&=",P_BIN_AND_ASSIGN, NULL}, - {"|=",P_BIN_OR_ASSIGN, NULL}, - {"^=",P_BIN_XOR_ASSIGN, NULL}, - {">>",P_RSHIFT, NULL}, - {"<<",P_LSHIFT, NULL}, - //reference operators - {"->",P_POINTERREF, NULL}, - //C++ - {"::",P_CPP1, NULL}, - {".*",P_CPP2, NULL}, - //arithmatic operators - {"*",P_MUL, NULL}, - {"/",P_DIV, NULL}, - {"%",P_MOD, NULL}, - {"+",P_ADD, NULL}, - {"-",P_SUB, NULL}, - {"=",P_ASSIGN, NULL}, - //binary operators - {"&",P_BIN_AND, NULL}, - {"|",P_BIN_OR, NULL}, - {"^",P_BIN_XOR, NULL}, - {"~",P_BIN_NOT, NULL}, - //logic operators - {"!",P_LOGIC_NOT, NULL}, - {">",P_LOGIC_GREATER, NULL}, - {"<",P_LOGIC_LESS, NULL}, - //reference operator - {".",P_REF, NULL}, - //seperators - {",",P_COMMA, NULL}, - {";",P_SEMICOLON, NULL}, - //label indication - {":",P_COLON, NULL}, - //if statement - {"?",P_QUESTIONMARK, NULL}, - //embracements - {"(",P_PARENTHESESOPEN, NULL}, - {")",P_PARENTHESESCLOSE, NULL}, - {"{",P_BRACEOPEN, NULL}, - {"}",P_BRACECLOSE, NULL}, - {"[",P_SQBRACKETOPEN, NULL}, - {"]",P_SQBRACKETCLOSE, NULL}, - // - {"\\",P_BACKSLASH, NULL}, - //precompiler operator - {"#",P_PRECOMP, NULL}, -#ifdef DOLLAR - {"$",P_DOLLAR, NULL}, -#endif //DOLLAR - {NULL, 0} -}; - -#ifdef BSPC -char basefolder[MAX_PATH]; -#else -char basefolder[MAX_QPATH]; -#endif - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PS_CreatePunctuationTable(script_t *script, punctuation_t *punctuations) -{ - int i; - punctuation_t *p, *lastp, *newp; - - //get memory for the table - if (!script->punctuationtable) script->punctuationtable = (punctuation_t **) - GetMemory(256 * sizeof(punctuation_t *)); - //Com_Memset(script->punctuationtable, 0, 256 * sizeof(punctuation_t *)); - memset(script->punctuationtable, 0, 256 * sizeof(punctuation_t *)); - - //add the punctuations in the list to the punctuation table - for (i = 0; punctuations[i].p; i++) - { - newp = &punctuations[i]; - lastp = NULL; - //sort the punctuations in this table entry on length (longer punctuations first) - for (p = script->punctuationtable[(unsigned int) newp->p[0]]; p; p = p->next) - { - if (strlen(p->p) < strlen(newp->p)) - { - newp->next = p; - if (lastp) lastp->next = newp; - else script->punctuationtable[(unsigned int) newp->p[0]] = newp; - break; - } //end if - lastp = p; - } //end for - if (!p) - { - newp->next = NULL; - if (lastp) lastp->next = newp; - else script->punctuationtable[(unsigned int) newp->p[0]] = newp; - } //end if - } //end for -} //end of the function PS_CreatePunctuationTable -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *PunctuationFromNum(script_t *script, int num) -{ - int i; - - for (i = 0; script->punctuations[i].p; i++) - { - if (script->punctuations[i].n == num) return script->punctuations[i].p; - } //end for - return "unkown punctuation"; -} //end of the function PunctuationFromNum -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void QDECL ScriptError(script_t *script, char *str, ...) -{ - char text[1024]; - va_list ap; - - if (script->flags & SCFL_NOERRORS) return; - - va_start(ap, str); - vsprintf(text, str, ap); - va_end(ap); -#ifdef BOTLIB - botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", script->filename, script->line, text); -#endif //BOTLIB -#ifdef MEQCC - printf("error: file %s, line %d: %s\n", script->filename, script->line, text); -#endif //MEQCC -#ifdef BSPC - Log_Print("error: file %s, line %d: %s\n", script->filename, script->line, text); -#endif //BSPC -} //end of the function ScriptError -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void QDECL ScriptWarning(script_t *script, char *str, ...) -{ - char text[1024]; - va_list ap; - - if (script->flags & SCFL_NOWARNINGS) return; - - va_start(ap, str); - vsprintf(text, str, ap); - va_end(ap); -#ifdef BOTLIB - botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", script->filename, script->line, text); -#endif //BOTLIB -#ifdef MEQCC - printf("warning: file %s, line %d: %s\n", script->filename, script->line, text); -#endif //MEQCC -#ifdef BSPC - Log_Print("warning: file %s, line %d: %s\n", script->filename, script->line, text); -#endif //BSPC -} //end of the function ScriptWarning -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void SetScriptPunctuations(script_t *script, punctuation_t *p) -{ -#ifdef PUNCTABLE - if (p) PS_CreatePunctuationTable(script, p); - else PS_CreatePunctuationTable(script, default_punctuations); -#endif //PUNCTABLE - if (p) script->punctuations = p; - else script->punctuations = default_punctuations; -} //end of the function SetScriptPunctuations -//============================================================================ -// Reads spaces, tabs, C-like comments etc. -// When a newline character is found the scripts line counter is increased. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_ReadWhiteSpace(script_t *script) -{ - while(1) - { - //skip white space - while(*script->script_p <= ' ') - { - if (!*script->script_p) return 0; - if (*script->script_p == '\n') script->line++; - script->script_p++; - } //end while - //skip comments - if (*script->script_p == '/') - { - //comments // - if (*(script->script_p+1) == '/') - { - script->script_p++; - do - { - script->script_p++; - if (!*script->script_p) return 0; - } //end do - while(*script->script_p != '\n'); - script->line++; - script->script_p++; - if (!*script->script_p) return 0; - continue; - } //end if - //comments /* */ - else if (*(script->script_p+1) == '*') - { - script->script_p++; - do - { - script->script_p++; - if (!*script->script_p) return 0; - if (*script->script_p == '\n') script->line++; - } //end do - while(!(*script->script_p == '*' && *(script->script_p+1) == '/')); - script->script_p++; - if (!*script->script_p) return 0; - script->script_p++; - if (!*script->script_p) return 0; - continue; - } //end if - } //end if - break; - } //end while - return 1; -} //end of the function PS_ReadWhiteSpace -//============================================================================ -// Reads an escape character. -// -// Parameter: script : script to read from -// ch : place to store the read escape character -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_ReadEscapeCharacter(script_t *script, char *ch) -{ - int c, val, i; - - //step over the leading '\\' - script->script_p++; - //determine the escape character - switch(*script->script_p) - { - case '\\': c = '\\'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = '\v'; break; - case 'b': c = '\b'; break; - case 'f': c = '\f'; break; - case 'a': c = '\a'; break; - case '\'': c = '\''; break; - case '\"': c = '\"'; break; - case '\?': c = '\?'; break; - case 'x': - { - script->script_p++; - for (i = 0, val = 0; ; i++, script->script_p++) - { - c = *script->script_p; - if (c >= '0' && c <= '9') c = c - '0'; - else if (c >= 'A' && c <= 'Z') c = c - 'A' + 10; - else if (c >= 'a' && c <= 'z') c = c - 'a' + 10; - else break; - val = (val << 4) + c; - } //end for - script->script_p--; - if (val > 0xFF) - { - ScriptWarning(script, "too large value in escape character"); - val = 0xFF; - } //end if - c = val; - break; - } //end case - default: //NOTE: decimal ASCII code, NOT octal - { - if (*script->script_p < '0' || *script->script_p > '9') ScriptError(script, "unknown escape char"); - for (i = 0, val = 0; ; i++, script->script_p++) - { - c = *script->script_p; - if (c >= '0' && c <= '9') c = c - '0'; - else break; - val = val * 10 + c; - } //end for - script->script_p--; - if (val > 0xFF) - { - ScriptWarning(script, "too large value in escape character"); - val = 0xFF; - } //end if - c = val; - break; - } //end default - } //end switch - //step over the escape character or the last digit of the number - script->script_p++; - //store the escape character - *ch = c; - //succesfully read escape character - return 1; -} //end of the function PS_ReadEscapeCharacter -//============================================================================ -// Reads C-like string. Escape characters are interpretted. -// Quotes are included with the string. -// Reads two strings with a white space between them as one string. -// -// Parameter: script : script to read from -// token : buffer to store the string -// Returns: qtrue when a string was read succesfully -// Changes Globals: - -//============================================================================ -int PS_ReadString(script_t *script, token_t *token, int quote) -{ - int len, tmpline; - char *tmpscript_p; - - if (quote == '\"') token->type = TT_STRING; - else token->type = TT_LITERAL; - - len = 0; - //leading quote - token->string[len++] = *script->script_p++; - // - while(1) - { - //minus 2 because trailing double quote and zero have to be appended - if (len >= MAX_TOKEN - 2) - { - ScriptError(script, "string longer than MAX_TOKEN = %d", MAX_TOKEN); - return 0; - } //end if - //if there is an escape character and - //if escape characters inside a string are allowed - if (*script->script_p == '\\' && !(script->flags & SCFL_NOSTRINGESCAPECHARS)) - { - if (!PS_ReadEscapeCharacter(script, &token->string[len])) - { - token->string[len] = 0; - return 0; - } //end if - len++; - } //end if - //if a trailing quote - else if (*script->script_p == quote) - { - //step over the double quote - script->script_p++; - //if white spaces in a string are not allowed - if (script->flags & SCFL_NOSTRINGWHITESPACES) break; - // - tmpscript_p = script->script_p; - tmpline = script->line; - //read unusefull stuff between possible two following strings - if (!PS_ReadWhiteSpace(script)) - { - script->script_p = tmpscript_p; - script->line = tmpline; - break; - } //end if - //if there's no leading double qoute - if (*script->script_p != quote) - { - script->script_p = tmpscript_p; - script->line = tmpline; - break; - } //end if - //step over the new leading double quote - script->script_p++; - } //end if - else - { - if (*script->script_p == '\0') - { - token->string[len] = 0; - ScriptError(script, "missing trailing quote"); - return 0; - } //end if - if (*script->script_p == '\n') - { - token->string[len] = 0; - ScriptError(script, "newline inside string %s", token->string); - return 0; - } //end if - token->string[len++] = *script->script_p++; - } //end else - } //end while - //trailing quote - token->string[len++] = quote; - //end string with a zero - token->string[len] = '\0'; - //the sub type is the length of the string - token->subtype = len; - return 1; -} //end of the function PS_ReadString -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_ReadName(script_t *script, token_t *token) -{ - int len = 0; - char c; - - token->type = TT_NAME; - do - { - token->string[len++] = *script->script_p++; - if (len >= MAX_TOKEN) - { - ScriptError(script, "name longer than MAX_TOKEN = %d", MAX_TOKEN); - return 0; - } //end if - c = *script->script_p; - } while ((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c == '_'); - token->string[len] = '\0'; - //the sub type is the length of the name - token->subtype = len; - return 1; -} //end of the function PS_ReadName -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void NumberValue(char *string, int subtype, unsigned long int *intvalue, - long double *floatvalue) -{ - unsigned long int dotfound = 0; - - *intvalue = 0; - *floatvalue = 0; - //floating point number - if (subtype & TT_FLOAT) - { - while(*string) - { - if (*string == '.') - { - if (dotfound) return; - dotfound = 10; - string++; - } //end if - if (dotfound) - { - *floatvalue = *floatvalue + (long double) (*string - '0') / - (long double) dotfound; - dotfound *= 10; - } //end if - else - { - *floatvalue = *floatvalue * 10.0 + (long double) (*string - '0'); - } //end else - string++; - } //end while - *intvalue = (unsigned long) *floatvalue; - } //end if - else if (subtype & TT_DECIMAL) - { - while(*string) *intvalue = *intvalue * 10 + (*string++ - '0'); - *floatvalue = *intvalue; - } //end else if - else if (subtype & TT_HEX) - { - //step over the leading 0x or 0X - string += 2; - while(*string) - { - *intvalue <<= 4; - if (*string >= 'a' && *string <= 'f') *intvalue += *string - 'a' + 10; - else if (*string >= 'A' && *string <= 'F') *intvalue += *string - 'A' + 10; - else *intvalue += *string - '0'; - string++; - } //end while - *floatvalue = *intvalue; - } //end else if - else if (subtype & TT_OCTAL) - { - //step over the first zero - string += 1; - while(*string) *intvalue = (*intvalue << 3) + (*string++ - '0'); - *floatvalue = *intvalue; - } //end else if - else if (subtype & TT_BINARY) - { - //step over the leading 0b or 0B - string += 2; - while(*string) *intvalue = (*intvalue << 1) + (*string++ - '0'); - *floatvalue = *intvalue; - } //end else if -} //end of the function NumberValue -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_ReadNumber(script_t *script, token_t *token) -{ - int len = 0, i; - int octal, dot; - char c; -// unsigned long int intvalue = 0; -// long double floatvalue = 0; - - token->type = TT_NUMBER; - //check for a hexadecimal number - if (*script->script_p == '0' && - (*(script->script_p + 1) == 'x' || - *(script->script_p + 1) == 'X')) - { - token->string[len++] = *script->script_p++; - token->string[len++] = *script->script_p++; - c = *script->script_p; - //hexadecimal - while((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'A')) - { - token->string[len++] = *script->script_p++; - if (len >= MAX_TOKEN) - { - ScriptError(script, "hexadecimal number longer than MAX_TOKEN = %d", MAX_TOKEN); - return 0; - } //end if - c = *script->script_p; - } //end while - token->subtype |= TT_HEX; - } //end if -#ifdef BINARYNUMBERS - //check for a binary number - else if (*script->script_p == '0' && - (*(script->script_p + 1) == 'b' || - *(script->script_p + 1) == 'B')) - { - token->string[len++] = *script->script_p++; - token->string[len++] = *script->script_p++; - c = *script->script_p; - //binary - while(c == '0' || c == '1') - { - token->string[len++] = *script->script_p++; - if (len >= MAX_TOKEN) - { - ScriptError(script, "binary number longer than MAX_TOKEN = %d", MAX_TOKEN); - return 0; - } //end if - c = *script->script_p; - } //end while - token->subtype |= TT_BINARY; - } //end if -#endif //BINARYNUMBERS - else //decimal or octal integer or floating point number - { - octal = qfalse; - dot = qfalse; - if (*script->script_p == '0') octal = qtrue; - while(1) - { - c = *script->script_p; - if (c == '.') dot = qtrue; - else if (c == '8' || c == '9') octal = qfalse; - else if (c < '0' || c > '9') break; - token->string[len++] = *script->script_p++; - if (len >= MAX_TOKEN - 1) - { - ScriptError(script, "number longer than MAX_TOKEN = %d", MAX_TOKEN); - return 0; - } //end if - } //end while - if (octal) token->subtype |= TT_OCTAL; - else token->subtype |= TT_DECIMAL; - if (dot) token->subtype |= TT_FLOAT; - } //end else - for (i = 0; i < 2; i++) - { - c = *script->script_p; - //check for a LONG number - if ( (c == 'l' || c == 'L') // bk001204 - brackets - && !(token->subtype & TT_LONG)) - { - script->script_p++; - token->subtype |= TT_LONG; - } //end if - //check for an UNSIGNED number - else if ( (c == 'u' || c == 'U') // bk001204 - brackets - && !(token->subtype & (TT_UNSIGNED | TT_FLOAT))) - { - script->script_p++; - token->subtype |= TT_UNSIGNED; - } //end if - } //end for - token->string[len] = '\0'; -#ifdef NUMBERVALUE - NumberValue(token->string, token->subtype, &token->intvalue, &token->floatvalue); -#endif //NUMBERVALUE - if (!(token->subtype & TT_FLOAT)) token->subtype |= TT_INTEGER; - return 1; -} //end of the function PS_ReadNumber -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_ReadLiteral(script_t *script, token_t *token) -{ - token->type = TT_LITERAL; - //first quote - token->string[0] = *script->script_p++; - //check for end of file - if (!*script->script_p) - { - ScriptError(script, "end of file before trailing \'"); - return 0; - } //end if - //if it is an escape character - if (*script->script_p == '\\') - { - if (!PS_ReadEscapeCharacter(script, &token->string[1])) return 0; - } //end if - else - { - token->string[1] = *script->script_p++; - } //end else - //check for trailing quote - if (*script->script_p != '\'') - { - ScriptWarning(script, "too many characters in literal, ignored"); - while(*script->script_p && - *script->script_p != '\'' && - *script->script_p != '\n') - { - script->script_p++; - } //end while - if (*script->script_p == '\'') script->script_p++; - } //end if - //store the trailing quote - token->string[2] = *script->script_p++; - //store trailing zero to end the string - token->string[3] = '\0'; - //the sub type is the integer literal value - token->subtype = token->string[1]; - // - return 1; -} //end of the function PS_ReadLiteral -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_ReadPunctuation(script_t *script, token_t *token) -{ - int len; - char *p; - punctuation_t *punc; - -#ifdef PUNCTABLE - for (punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next) - { -#else - int i; - - for (i = 0; script->punctuations[i].p; i++) - { - punc = &script->punctuations[i]; -#endif //PUNCTABLE - p = punc->p; - len = strlen(p); - //if the script contains at least as much characters as the punctuation - if (script->script_p + len <= script->end_p) - { - //if the script contains the punctuation - if (!strncmp(script->script_p, p, len)) - { - strncpy(token->string, p, MAX_TOKEN); - script->script_p += len; - token->type = TT_PUNCTUATION; - //sub type is the number of the punctuation - token->subtype = punc->n; - return 1; - } //end if - } //end if - } //end for - return 0; -} //end of the function PS_ReadPunctuation -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_ReadPrimitive(script_t *script, token_t *token) -{ - int len; - - len = 0; - while(*script->script_p > ' ' && *script->script_p != ';') - { - if (len >= MAX_TOKEN) - { - ScriptError(script, "primitive token longer than MAX_TOKEN = %d", MAX_TOKEN); - return 0; - } //end if - token->string[len++] = *script->script_p++; - } //end while - token->string[len] = 0; - //copy the token into the script structure - memcpy(&script->token, token, sizeof(token_t)); - //primitive reading successfull - return 1; -} //end of the function PS_ReadPrimitive -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_ReadToken(script_t *script, token_t *token) -{ - //if there is a token available (from UnreadToken) - if (script->tokenavailable) - { - script->tokenavailable = 0; - //Com_Memcpy(token, &script->token, sizeof(token_t)); - memcpy(token, &script->token, sizeof(token_t)); - return 1; - } //end if - //save script pointer - script->lastscript_p = script->script_p; - //save line counter - script->lastline = script->line; - //clear the token stuff - //Com_Memset(token, 0, sizeof(token_t)); - memset(token, 0, sizeof(token_t)); - //start of the white space - script->whitespace_p = script->script_p; - token->whitespace_p = script->script_p; - //read unusefull stuff - if (!PS_ReadWhiteSpace(script)) return 0; - //end of the white space - script->endwhitespace_p = script->script_p; - token->endwhitespace_p = script->script_p; - //line the token is on - token->line = script->line; - //number of lines crossed before token - token->linescrossed = script->line - script->lastline; - //if there is a leading double quote - if (*script->script_p == '\"') - { - if (!PS_ReadString(script, token, '\"')) return 0; - } //end if - //if an literal - else if (*script->script_p == '\'') - { - //if (!PS_ReadLiteral(script, token)) return 0; - if (!PS_ReadString(script, token, '\'')) return 0; - } //end if - //if there is a number - else if ((*script->script_p >= '0' && *script->script_p <= '9') || - (*script->script_p == '.' && - (*(script->script_p + 1) >= '0' && *(script->script_p + 1) <= '9'))) - { - if (!PS_ReadNumber(script, token)) return 0; - } //end if - //if this is a primitive script - else if (script->flags & SCFL_PRIMITIVE) - { - return PS_ReadPrimitive(script, token); - } //end else if - //if there is a name - else if ((*script->script_p >= 'a' && *script->script_p <= 'z') || - (*script->script_p >= 'A' && *script->script_p <= 'Z') || - *script->script_p == '_') - { - if (!PS_ReadName(script, token)) return 0; - } //end if - //check for punctuations - else if (!PS_ReadPunctuation(script, token)) - { - ScriptError(script, "can't read token"); - return 0; - } //end if - //copy the token into the script structure - //Com_Memcpy(&script->token, token, sizeof(token_t)); - memcpy(&script->token, token, sizeof(token_t)); - //succesfully read a token - return 1; -} //end of the function PS_ReadToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_ExpectTokenString(script_t *script, char *string) -{ - token_t token; - - if (!PS_ReadToken(script, &token)) - { - ScriptError(script, "couldn't find expected %s", string); - return 0; - } //end if - - if (strcmp(token.string, string)) - { - ScriptError(script, "expected %s, found %s", string, token.string); - return 0; - } //end if - return 1; -} //end of the function PS_ExpectToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token) -{ - char str[MAX_TOKEN]; - - if (!PS_ReadToken(script, token)) - { - ScriptError(script, "couldn't read expected token"); - return 0; - } //end if - - if (token->type != type) - { - if (type == TT_STRING) strcpy(str, "string"); - if (type == TT_LITERAL) strcpy(str, "literal"); - if (type == TT_NUMBER) strcpy(str, "number"); - if (type == TT_NAME) strcpy(str, "name"); - if (type == TT_PUNCTUATION) strcpy(str, "punctuation"); - ScriptError(script, "expected a %s, found %s", str, token->string); - return 0; - } //end if - if (token->type == TT_NUMBER) - { - if ((token->subtype & subtype) != subtype) - { - if (subtype & TT_DECIMAL) strcpy(str, "decimal"); - if (subtype & TT_HEX) strcpy(str, "hex"); - if (subtype & TT_OCTAL) strcpy(str, "octal"); - if (subtype & TT_BINARY) strcpy(str, "binary"); - if (subtype & TT_LONG) strcat(str, " long"); - if (subtype & TT_UNSIGNED) strcat(str, " unsigned"); - if (subtype & TT_FLOAT) strcat(str, " float"); - if (subtype & TT_INTEGER) strcat(str, " integer"); - ScriptError(script, "expected %s, found %s", str, token->string); - return 0; - } //end if - } //end if - else if (token->type == TT_PUNCTUATION) - { - if (subtype < 0) - { - ScriptError(script, "BUG: wrong punctuation subtype"); - return 0; - } //end if - if (token->subtype != subtype) - { - ScriptError(script, "expected %s, found %s", - script->punctuations[subtype], token->string); - return 0; - } //end if - } //end else if - return 1; -} //end of the function PS_ExpectTokenType -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_ExpectAnyToken(script_t *script, token_t *token) -{ - if (!PS_ReadToken(script, token)) - { - ScriptError(script, "couldn't read expected token"); - return 0; - } //end if - else - { - return 1; - } //end else -} //end of the function PS_ExpectAnyToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_CheckTokenString(script_t *script, char *string) -{ - token_t tok; - - if (!PS_ReadToken(script, &tok)) return 0; - //if the token is available - if (!strcmp(tok.string, string)) return 1; - //token not available - script->script_p = script->lastscript_p; - return 0; -} //end of the function PS_CheckTokenString -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token) -{ - token_t tok; - - if (!PS_ReadToken(script, &tok)) return 0; - //if the type matches - if (tok.type == type && - (tok.subtype & subtype) == subtype) - { - //Com_Memcpy(token, &tok, sizeof(token_t)); - memcpy(token, &tok, sizeof(token_t)); - return 1; - } //end if - //token is not available - script->script_p = script->lastscript_p; - return 0; -} //end of the function PS_CheckTokenType -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int PS_SkipUntilString(script_t *script, char *string) -{ - token_t token; - - while(PS_ReadToken(script, &token)) - { - if (!strcmp(token.string, string)) return 1; - } //end while - return 0; -} //end of the function PS_SkipUntilString -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PS_UnreadLastToken(script_t *script) -{ - script->tokenavailable = 1; -} //end of the function UnreadLastToken -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PS_UnreadToken(script_t *script, token_t *token) -{ - //Com_Memcpy(&script->token, token, sizeof(token_t)); - memcpy(&script->token, token, sizeof(token_t)); - script->tokenavailable = 1; -} //end of the function UnreadToken -//============================================================================ -// returns the next character of the read white space, returns NULL if none -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -char PS_NextWhiteSpaceChar(script_t *script) -{ - if (script->whitespace_p != script->endwhitespace_p) - { - return *script->whitespace_p++; - } //end if - else - { - return 0; - } //end else -} //end of the function PS_NextWhiteSpaceChar -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void StripDoubleQuotes(char *string) -{ - if (*string == '\"') - { - memmove(string, string+1, strlen(string)); - } //end if - if (string[strlen(string)-1] == '\"') - { - string[strlen(string)-1] = '\0'; - } //end if -} //end of the function StripDoubleQuotes -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void StripSingleQuotes(char *string) -{ - if (*string == '\'') - { - strcpy(string, string+1); - } //end if - if (string[strlen(string)-1] == '\'') - { - string[strlen(string)-1] = '\0'; - } //end if -} //end of the function StripSingleQuotes -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -float ReadSignedFloat(script_t *script) -{ - token_t token; - long double sign = 1; - - PS_ExpectAnyToken(script, &token); - if (!strcmp(token.string, "-")) - { - sign = -1; - PS_ExpectTokenType(script, TT_NUMBER, 0, &token); - } //end if - else if (token.type != TT_NUMBER) - { - ScriptError(script, "expected float value, found %s\n", token.string); - } //end else if - return sign * token.floatvalue; -} //end of the function ReadSignedFloat -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -signed long int ReadSignedInt(script_t *script) -{ - token_t token; - signed long int sign = 1; - - PS_ExpectAnyToken(script, &token); - if (!strcmp(token.string, "-")) - { - sign = -1; - PS_ExpectTokenType(script, TT_NUMBER, TT_INTEGER, &token); - } //end if - else if (token.type != TT_NUMBER || token.subtype == TT_FLOAT) - { - ScriptError(script, "expected integer value, found %s\n", token.string); - } //end else if - return sign * token.intvalue; -} //end of the function ReadSignedInt -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void SetScriptFlags(script_t *script, int flags) -{ - script->flags = flags; -} //end of the function SetScriptFlags -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int GetScriptFlags(script_t *script) -{ - return script->flags; -} //end of the function GetScriptFlags -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void ResetScript(script_t *script) -{ - //pointer in script buffer - script->script_p = script->buffer; - //pointer in script buffer before reading token - script->lastscript_p = script->buffer; - //begin of white space - script->whitespace_p = NULL; - //end of white space - script->endwhitespace_p = NULL; - //set if there's a token available in script->token - script->tokenavailable = 0; - // - script->line = 1; - script->lastline = 1; - //clear the saved token - //Com_Memset(&script->token, 0, sizeof(token_t)); - memset(&script->token, 0, sizeof(token_t)); -} //end of the function ResetScript -//============================================================================ -// returns true if at the end of the script -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int EndOfScript(script_t *script) -{ - return script->script_p >= script->end_p; -} //end of the function EndOfScript -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int NumLinesCrossed(script_t *script) -{ - return script->line - script->lastline; -} //end of the function NumLinesCrossed -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int ScriptSkipTo(script_t *script, char *value) -{ - int len; - char firstchar; - - firstchar = *value; - len = strlen(value); - do - { - if (!PS_ReadWhiteSpace(script)) return 0; - if (*script->script_p == firstchar) - { - if (!strncmp(script->script_p, value, len)) - { - return 1; - } //end if - } //end if - script->script_p++; - } while(1); -} //end of the function ScriptSkipTo -#ifndef BOTLIB -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -int FileLength(FILE *fp) -{ - int pos; - int end; - - pos = ftell(fp); - fseek(fp, 0, SEEK_END); - end = ftell(fp); - fseek(fp, pos, SEEK_SET); - - return end; -} //end of the function FileLength -#endif -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -script_t *LoadScriptFile(const char *filename) -{ -#ifdef BOTLIB - fileHandle_t fp; - char pathname[MAX_QPATH]; -#else - FILE *fp; -#endif - int length; - void *buffer; - script_t *script; - -#ifdef BOTLIB - if (strlen(basefolder)) - Com_sprintf(pathname, sizeof(pathname), "%s/%s", basefolder, filename); - else - Com_sprintf(pathname, sizeof(pathname), "%s", filename); - length = botimport.FS_FOpenFile( pathname, &fp, FS_READ ); - if (!fp) return NULL; -#else - fp = fopen(filename, "rb"); - if (!fp) return NULL; - - length = FileLength(fp); -#endif - - buffer = GetClearedMemory(sizeof(script_t) + length + 1); - script = (script_t *) buffer; - //Com_Memset(script, 0, sizeof(script_t)); - memset(script, 0, sizeof(script_t)); - strcpy(script->filename, filename); - script->buffer = (char *) buffer + sizeof(script_t); - script->buffer[length] = 0; - script->length = length; - //pointer in script buffer - script->script_p = script->buffer; - //pointer in script buffer before reading token - script->lastscript_p = script->buffer; - //pointer to end of script buffer - script->end_p = &script->buffer[length]; - //set if there's a token available in script->token - script->tokenavailable = 0; - // - script->line = 1; - script->lastline = 1; - // - SetScriptPunctuations(script, NULL); - // -#ifdef BOTLIB - botimport.FS_Read(script->buffer, length, fp); - botimport.FS_FCloseFile(fp); -#else - if (fread(script->buffer, length, 1, fp) != 1) - { - FreeMemory(buffer); - script = NULL; - } //end if - fclose(fp); -#endif - // - script->length = COM_Compress(script->buffer); - - return script; -} //end of the function LoadScriptFile -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -script_t *LoadScriptMemory(char *ptr, int length, char *name) -{ - void *buffer; - script_t *script; - - buffer = GetClearedMemory(sizeof(script_t) + length + 1); - script = (script_t *) buffer; - //Com_Memset(script, 0, sizeof(script_t)); - memset(script, 0, sizeof(script_t)); - strcpy(script->filename, name); - script->buffer = (char *) buffer + sizeof(script_t); - script->buffer[length] = 0; - script->length = length; - //pointer in script buffer - script->script_p = script->buffer; - //pointer in script buffer before reading token - script->lastscript_p = script->buffer; - //pointer to end of script buffer - script->end_p = &script->buffer[length]; - //set if there's a token available in script->token - script->tokenavailable = 0; - // - script->line = 1; - script->lastline = 1; - // - SetScriptPunctuations(script, NULL); - // - //Com_Memcpy(script->buffer, ptr, length); - memcpy(script->buffer, ptr, length); - // - return script; -} //end of the function LoadScriptMemory -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void FreeScript(script_t *script) -{ -#ifdef PUNCTABLE - if (script->punctuationtable) FreeMemory(script->punctuationtable); -#endif //PUNCTABLE - FreeMemory(script); -} //end of the function FreeScript -//============================================================================ -// -// Parameter: - -// Returns: - -// Changes Globals: - -//============================================================================ -void PS_SetBaseFolder(char *path) -{ - Q_strncpyz(basefolder, path, sizeof(basefolder)); -} //end of the function PS_SetBaseFolder diff --git a/tools/quake3/bspc/deps/botlib/l_script.h b/tools/quake3/bspc/deps/botlib/l_script.h deleted file mode 100644 index ceb0469c..00000000 --- a/tools/quake3/bspc/deps/botlib/l_script.h +++ /dev/null @@ -1,247 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_script.h - * - * desc: lexicographical parser - * - * $Archive: /source/code/botlib/l_script.h $ - * - *****************************************************************************/ - -//undef if binary numbers of the form 0b... or 0B... are not allowed -#define BINARYNUMBERS -//undef if not using the token.intvalue and token.floatvalue -#define NUMBERVALUE -//use dollar sign also as punctuation -#define DOLLAR - -//maximum token length -#define MAX_TOKEN 1024 - -#if defined(BSPC) && !defined(QDECL) -#define QDECL -#endif - - -//script flags -#define SCFL_NOERRORS 0x0001 -#define SCFL_NOWARNINGS 0x0002 -#define SCFL_NOSTRINGWHITESPACES 0x0004 -#define SCFL_NOSTRINGESCAPECHARS 0x0008 -#define SCFL_PRIMITIVE 0x0010 -#define SCFL_NOBINARYNUMBERS 0x0020 -#define SCFL_NONUMBERVALUES 0x0040 - -//token types -#define TT_STRING 1 // string -#define TT_LITERAL 2 // literal -#define TT_NUMBER 3 // number -#define TT_NAME 4 // name -#define TT_PUNCTUATION 5 // punctuation - -//string sub type -//--------------- -// the length of the string -//literal sub type -//---------------- -// the ASCII code of the literal -//number sub type -//--------------- -#define TT_DECIMAL 0x0008 // decimal number -#define TT_HEX 0x0100 // hexadecimal number -#define TT_OCTAL 0x0200 // octal number -#ifdef BINARYNUMBERS -#define TT_BINARY 0x0400 // binary number -#endif //BINARYNUMBERS -#define TT_FLOAT 0x0800 // floating point number -#define TT_INTEGER 0x1000 // integer number -#define TT_LONG 0x2000 // long number -#define TT_UNSIGNED 0x4000 // unsigned number -//punctuation sub type -//-------------------- -#define P_RSHIFT_ASSIGN 1 -#define P_LSHIFT_ASSIGN 2 -#define P_PARMS 3 -#define P_PRECOMPMERGE 4 - -#define P_LOGIC_AND 5 -#define P_LOGIC_OR 6 -#define P_LOGIC_GEQ 7 -#define P_LOGIC_LEQ 8 -#define P_LOGIC_EQ 9 -#define P_LOGIC_UNEQ 10 - -#define P_MUL_ASSIGN 11 -#define P_DIV_ASSIGN 12 -#define P_MOD_ASSIGN 13 -#define P_ADD_ASSIGN 14 -#define P_SUB_ASSIGN 15 -#define P_INC 16 -#define P_DEC 17 - -#define P_BIN_AND_ASSIGN 18 -#define P_BIN_OR_ASSIGN 19 -#define P_BIN_XOR_ASSIGN 20 -#define P_RSHIFT 21 -#define P_LSHIFT 22 - -#define P_POINTERREF 23 -#define P_CPP1 24 -#define P_CPP2 25 -#define P_MUL 26 -#define P_DIV 27 -#define P_MOD 28 -#define P_ADD 29 -#define P_SUB 30 -#define P_ASSIGN 31 - -#define P_BIN_AND 32 -#define P_BIN_OR 33 -#define P_BIN_XOR 34 -#define P_BIN_NOT 35 - -#define P_LOGIC_NOT 36 -#define P_LOGIC_GREATER 37 -#define P_LOGIC_LESS 38 - -#define P_REF 39 -#define P_COMMA 40 -#define P_SEMICOLON 41 -#define P_COLON 42 -#define P_QUESTIONMARK 43 - -#define P_PARENTHESESOPEN 44 -#define P_PARENTHESESCLOSE 45 -#define P_BRACEOPEN 46 -#define P_BRACECLOSE 47 -#define P_SQBRACKETOPEN 48 -#define P_SQBRACKETCLOSE 49 -#define P_BACKSLASH 50 - -#define P_PRECOMP 51 -#define P_DOLLAR 52 -//name sub type -//------------- -// the length of the name - -//punctuation -typedef struct punctuation_s -{ - char *p; //punctuation character(s) - int n; //punctuation indication - struct punctuation_s *next; //next punctuation -} punctuation_t; - -//token -typedef struct token_s -{ - char string[MAX_TOKEN]; //available token - int type; //last read token type - int subtype; //last read token sub type -#ifdef NUMBERVALUE - unsigned long int intvalue; //integer value - float floatvalue; //floating point value -#endif //NUMBERVALUE - char *whitespace_p; //start of white space before token - char *endwhitespace_p; //start of white space before token - int line; //line the token was on - int linescrossed; //lines crossed in white space - struct token_s *next; //next token in chain -} token_t; - -//script file -typedef struct script_s -{ - char filename[1024]; //file name of the script - char *buffer; //buffer containing the script - char *script_p; //current pointer in the script - char *end_p; //pointer to the end of the script - char *lastscript_p; //script pointer before reading token - char *whitespace_p; //begin of the white space - char *endwhitespace_p; //end of the white space - int length; //length of the script in bytes - int line; //current line in script - int lastline; //line before reading token - int tokenavailable; //set by UnreadLastToken - int flags; //several script flags - punctuation_t *punctuations; //the punctuations used in the script - punctuation_t **punctuationtable; - token_t token; //available token - struct script_s *next; //next script in a chain -} script_t; - -//read a token from the script -int PS_ReadToken(script_t *script, token_t *token); -//expect a certain token -int PS_ExpectTokenString(script_t *script, char *string); -//expect a certain token type -int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token); -//expect a token -int PS_ExpectAnyToken(script_t *script, token_t *token); -//returns true when the token is available -int PS_CheckTokenString(script_t *script, char *string); -//returns true an reads the token when a token with the given type is available -int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token); -//skip tokens until the given token string is read -int PS_SkipUntilString(script_t *script, char *string); -//unread the last token read from the script -void PS_UnreadLastToken(script_t *script); -//unread the given token -void PS_UnreadToken(script_t *script, token_t *token); -//returns the next character of the read white space, returns NULL if none -char PS_NextWhiteSpaceChar(script_t *script); -//remove any leading and trailing double quotes from the token -void StripDoubleQuotes(char *string); -//remove any leading and trailing single quotes from the token -void StripSingleQuotes(char *string); -//read a possible signed integer -signed long int ReadSignedInt(script_t *script); -//read a possible signed floating point number -float ReadSignedFloat(script_t *script); -//set an array with punctuations, NULL restores default C/C++ set -void SetScriptPunctuations(script_t *script, punctuation_t *p); -//set script flags -void SetScriptFlags(script_t *script, int flags); -//get script flags -int GetScriptFlags(script_t *script); -//reset a script -void ResetScript(script_t *script); -//returns true if at the end of the script -int EndOfScript(script_t *script); -//returns a pointer to the punctuation with the given number -char *PunctuationFromNum(script_t *script, int num); -//load a script from the given file at the given offset with the given length -script_t *LoadScriptFile(const char *filename); -//load a script from the given memory with the given length -script_t *LoadScriptMemory(char *ptr, int length, char *name); -//free a script -void FreeScript(script_t *script); -//set the base folder to load files from -void PS_SetBaseFolder(char *path); -//print a script error with filename and line number -void QDECL ScriptError(script_t *script, char *str, ...); -//print a script warning with filename and line number -void QDECL ScriptWarning(script_t *script, char *str, ...); - - diff --git a/tools/quake3/bspc/deps/botlib/l_struct.c b/tools/quake3/bspc/deps/botlib/l_struct.c deleted file mode 100644 index 9719e459..00000000 --- a/tools/quake3/bspc/deps/botlib/l_struct.c +++ /dev/null @@ -1,462 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_struct.c - * - * desc: structure reading / writing - * - * $Archive: /MissionPack/CODE/botlib/l_struct.c $ - * - *****************************************************************************/ - -#ifdef BOTLIB -#include "qcommon/q_shared.h" -#include "botlib.h" //for the include of be_interface.h -#include "l_script.h" -#include "l_precomp.h" -#include "l_struct.h" -#include "l_utils.h" -#include "be_interface.h" -#endif //BOTLIB - -#ifdef BSPC -//include files for usage in the BSP Converter -#include "../../qbsp.h" -#include "l_log.h" -#include "../../l_mem.h" -#include "l_precomp.h" -#include "l_struct.h" - -#define qtrue true -#define qfalse false -#endif //BSPC - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -fielddef_t *FindField(fielddef_t *defs, char *name) -{ - int i; - - for (i = 0; defs[i].name; i++) - { - if (!strcmp(defs[i].name, name)) return &defs[i]; - } //end for - return NULL; -} //end of the function FindField -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean ReadNumber(source_t *source, fielddef_t *fd, void *p) -{ - token_t token; - int negative = qfalse; - long int intval, intmin = 0, intmax = 0; - double floatval; - - if (!PC_ExpectAnyToken(source, &token)) return 0; - - //check for minus sign - if (token.type == TT_PUNCTUATION) - { - if (fd->type & FT_UNSIGNED) - { - SourceError(source, "expected unsigned value, found %s", token.string); - return 0; - } //end if - //if not a minus sign - if (strcmp(token.string, "-")) - { - SourceError(source, "unexpected punctuation %s", token.string); - return 0; - } //end if - negative = qtrue; - //read the number - if (!PC_ExpectAnyToken(source, &token)) return 0; - } //end if - //check if it is a number - if (token.type != TT_NUMBER) - { - SourceError(source, "expected number, found %s", token.string); - return 0; - } //end if - //check for a float value - if (token.subtype & TT_FLOAT) - { - if ((fd->type & FT_TYPE) != FT_FLOAT) - { - SourceError(source, "unexpected float"); - return 0; - } //end if - floatval = token.floatvalue; - if (negative) floatval = -floatval; - if (fd->type & FT_BOUNDED) - { - if (floatval < fd->floatmin || floatval > fd->floatmax) - { - SourceError(source, "float out of range [%f, %f]", fd->floatmin, fd->floatmax); - return 0; - } //end if - } //end if - *(float *) p = (float) floatval; - return 1; - } //end if - // - intval = token.intvalue; - if (negative) intval = -intval; - //check bounds - if ((fd->type & FT_TYPE) == FT_CHAR) - { - if (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 255;} - else {intmin = -128; intmax = 127;} - } //end if - if ((fd->type & FT_TYPE) == FT_INT) - { - if (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 65535;} - else {intmin = -32768; intmax = 32767;} - } //end else if - if ((fd->type & FT_TYPE) == FT_CHAR || (fd->type & FT_TYPE) == FT_INT) - { - if (fd->type & FT_BOUNDED) - { - intmin = Maximum(intmin, fd->floatmin); - intmax = Minimum(intmax, fd->floatmax); - } //end if - if (intval < intmin || intval > intmax) - { - SourceError(source, "value %d out of range [%d, %d]", intval, intmin, intmax); - return 0; - } //end if - } //end if - else if ((fd->type & FT_TYPE) == FT_FLOAT) - { - if (fd->type & FT_BOUNDED) - { - if (intval < fd->floatmin || intval > fd->floatmax) - { - SourceError(source, "value %d out of range [%f, %f]", intval, fd->floatmin, fd->floatmax); - return 0; - } //end if - } //end if - } //end else if - //store the value - if ((fd->type & FT_TYPE) == FT_CHAR) - { - if (fd->type & FT_UNSIGNED) *(unsigned char *) p = (unsigned char) intval; - else *(char *) p = (char) intval; - } //end if - else if ((fd->type & FT_TYPE) == FT_INT) - { - if (fd->type & FT_UNSIGNED) *(unsigned int *) p = (unsigned int) intval; - else *(int *) p = (int) intval; - } //end else - else if ((fd->type & FT_TYPE) == FT_FLOAT) - { - *(float *) p = (float) intval; - } //end else - return 1; -} //end of the function ReadNumber -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean ReadChar(source_t *source, fielddef_t *fd, void *p) -{ - token_t token; - - if (!PC_ExpectAnyToken(source, &token)) return 0; - - //take literals into account - if (token.type == TT_LITERAL) - { - StripSingleQuotes(token.string); - *(char *) p = token.string[0]; - } //end if - else - { - PC_UnreadLastToken(source); - if (!ReadNumber(source, fd, p)) return 0; - } //end if - return 1; -} //end of the function ReadChar -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int ReadString(source_t *source, fielddef_t *fd, void *p) -{ - token_t token; - - if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) return 0; - //remove the double quotes - StripDoubleQuotes(token.string); - //copy the string - strncpy((char *) p, token.string, MAX_STRINGFIELD); - //make sure the string is closed with a zero - ((char *)p)[MAX_STRINGFIELD-1] = '\0'; - // - return 1; -} //end of the function ReadString -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int ReadStructure(source_t *source, structdef_t *def, char *structure) -{ - token_t token; - fielddef_t *fd; - void *p; - int num; - - if (!PC_ExpectTokenString(source, "{")) return 0; - while(1) - { - if (!PC_ExpectAnyToken(source, &token)) return qfalse; - //if end of structure - if (!strcmp(token.string, "}")) break; - //find the field with the name - fd = FindField(def->fields, token.string); - if (!fd) - { - SourceError(source, "unknown structure field %s", token.string); - return qfalse; - } //end if - if (fd->type & FT_ARRAY) - { - num = fd->maxarray; - if (!PC_ExpectTokenString(source, "{")) return qfalse; - } //end if - else - { - num = 1; - } //end else - p = (void *)(structure + fd->offset); - while (num-- > 0) - { - if (fd->type & FT_ARRAY) - { - if (PC_CheckTokenString(source, "}")) break; - } //end if - switch(fd->type & FT_TYPE) - { - case FT_CHAR: - { - if (!ReadChar(source, fd, p)) return qfalse; - p = (char *) p + sizeof(char); - break; - } //end case - case FT_INT: - { - if (!ReadNumber(source, fd, p)) return qfalse; - p = (char *) p + sizeof(int); - break; - } //end case - case FT_FLOAT: - { - if (!ReadNumber(source, fd, p)) return qfalse; - p = (char *) p + sizeof(float); - break; - } //end case - case FT_STRING: - { - if (!ReadString(source, fd, p)) return qfalse; - p = (char *) p + MAX_STRINGFIELD; - break; - } //end case - case FT_STRUCT: - { - if (!fd->substruct) - { - SourceError(source, "BUG: no sub structure defined"); - return qfalse; - } //end if - ReadStructure(source, fd->substruct, (char *) p); - p = (char *) p + fd->substruct->size; - break; - } //end case - } //end switch - if (fd->type & FT_ARRAY) - { - if (!PC_ExpectAnyToken(source, &token)) return qfalse; - if (!strcmp(token.string, "}")) break; - if (strcmp(token.string, ",")) - { - SourceError(source, "expected a comma, found %s", token.string); - return qfalse; - } //end if - } //end if - } //end while - } //end while - return qtrue; -} //end of the function ReadStructure -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int WriteIndent(FILE *fp, int indent) -{ - while(indent-- > 0) - { - if (fprintf(fp, "\t") < 0) return qfalse; - } //end while - return qtrue; -} //end of the function WriteIndent -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int WriteFloat(FILE *fp, float value) -{ - char buf[128]; - int l; - - sprintf(buf, "%f", value); - l = strlen(buf); - //strip any trailing zeros - while(l-- > 1) - { - if (buf[l] != '0' && buf[l] != '.') break; - if (buf[l] == '.') - { - buf[l] = 0; - break; - } //end if - buf[l] = 0; - } //end while - //write the float to file - if (fprintf(fp, "%s", buf) < 0) return 0; - return 1; -} //end of the function WriteFloat -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int WriteStructWithIndent(FILE *fp, structdef_t *def, char *structure, int indent) -{ - int i, num; - void *p; - fielddef_t *fd; - - if (!WriteIndent(fp, indent)) return qfalse; - if (fprintf(fp, "{\r\n") < 0) return qfalse; - - indent++; - for (i = 0; def->fields[i].name; i++) - { - fd = &def->fields[i]; - if (!WriteIndent(fp, indent)) return qfalse; - if (fprintf(fp, "%s\t", fd->name) < 0) return qfalse; - p = (void *)(structure + fd->offset); - if (fd->type & FT_ARRAY) - { - num = fd->maxarray; - if (fprintf(fp, "{") < 0) return qfalse; - } //end if - else - { - num = 1; - } //end else - while(num-- > 0) - { - switch(fd->type & FT_TYPE) - { - case FT_CHAR: - { - if (fprintf(fp, "%d", *(char *) p) < 0) return qfalse; - p = (char *) p + sizeof(char); - break; - } //end case - case FT_INT: - { - if (fprintf(fp, "%d", *(int *) p) < 0) return qfalse; - p = (char *) p + sizeof(int); - break; - } //end case - case FT_FLOAT: - { - if (!WriteFloat(fp, *(float *)p)) return qfalse; - p = (char *) p + sizeof(float); - break; - } //end case - case FT_STRING: - { - if (fprintf(fp, "\"%s\"", (char *) p) < 0) return qfalse; - p = (char *) p + MAX_STRINGFIELD; - break; - } //end case - case FT_STRUCT: - { - if (!WriteStructWithIndent(fp, fd->substruct, structure, indent)) return qfalse; - p = (char *) p + fd->substruct->size; - break; - } //end case - } //end switch - if (fd->type & FT_ARRAY) - { - if (num > 0) - { - if (fprintf(fp, ",") < 0) return qfalse; - } //end if - else - { - if (fprintf(fp, "}") < 0) return qfalse; - } //end else - } //end if - } //end while - if (fprintf(fp, "\r\n") < 0) return qfalse; - } //end for - indent--; - - if (!WriteIndent(fp, indent)) return qfalse; - if (fprintf(fp, "}\r\n") < 0) return qfalse; - return qtrue; -} //end of the function WriteStructWithIndent -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int WriteStructure(FILE *fp, structdef_t *def, char *structure) -{ - return WriteStructWithIndent(fp, def, structure, 0); -} //end of the function WriteStructure - diff --git a/tools/quake3/bspc/deps/botlib/l_struct.h b/tools/quake3/bspc/deps/botlib/l_struct.h deleted file mode 100644 index b4e030d3..00000000 --- a/tools/quake3/bspc/deps/botlib/l_struct.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_struct.h - * - * desc: structure reading/writing - * - * $Archive: /source/code/botlib/l_struct.h $ - * - *****************************************************************************/ - -#include - -#define MAX_STRINGFIELD 80 -//field types -#define FT_CHAR 1 // char -#define FT_INT 2 // int -#define FT_FLOAT 3 // float -#define FT_STRING 4 // char [MAX_STRINGFIELD] -#define FT_STRUCT 6 // struct (sub structure) -//type only mask -#define FT_TYPE 0x00FF // only type, clear subtype -//sub types -#define FT_ARRAY 0x0100 // array of type -#define FT_BOUNDED 0x0200 // bounded value -#define FT_UNSIGNED 0x0400 - -//structure field definition -typedef struct fielddef_s -{ - char *name; //name of the field - size_t offset; //offset in the structure - int type; //type of the field - //type specific fields - int maxarray; //maximum array size - float floatmin, floatmax; //float min and max - struct structdef_s *substruct; //sub structure -} fielddef_t; - -//structure definition -typedef struct structdef_s -{ - int size; - fielddef_t *fields; -} structdef_t; - -//read a structure from a script -int ReadStructure(source_t *source, structdef_t *def, char *structure); -//write a structure to a file -int WriteStructure(FILE *fp, structdef_t *def, char *structure); -//writes indents -int WriteIndent(FILE *fp, int indent); -//writes a float without traling zeros -int WriteFloat(FILE *fp, float value); - - diff --git a/tools/quake3/bspc/deps/botlib/l_utils.h b/tools/quake3/bspc/deps/botlib/l_utils.h deleted file mode 100644 index 6944d06f..00000000 --- a/tools/quake3/bspc/deps/botlib/l_utils.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/***************************************************************************** - * name: l_util.h - * - * desc: utils - * - * $Archive: /source/code/botlib/l_util.h $ - * - *****************************************************************************/ - -#define Vector2Angles(v,a) vectoangles(v,a) -#ifndef MAX_PATH -#define MAX_PATH MAX_QPATH -#endif -#define Maximum(x,y) (x > y ? x : y) -#define Minimum(x,y) (x < y ? x : y) diff --git a/tools/quake3/bspc/deps/botlib/lcc.mak b/tools/quake3/bspc/deps/botlib/lcc.mak deleted file mode 100644 index f5567c3a..00000000 --- a/tools/quake3/bspc/deps/botlib/lcc.mak +++ /dev/null @@ -1,55 +0,0 @@ -# -# Makefile for Gladiator Bot library: gladiator.dll -# Intended for LCC-Win32 -# - -CC=lcc -CFLAGS=-DC_ONLY -o -OBJS= be_aas_bspq2.obj \ - be_aas_bsphl.obj \ - be_aas_cluster.obj \ - be_aas_debug.obj \ - be_aas_entity.obj \ - be_aas_file.obj \ - be_aas_light.obj \ - be_aas_main.obj \ - be_aas_move.obj \ - be_aas_optimize.obj \ - be_aas_reach.obj \ - be_aas_route.obj \ - be_aas_routealt.obj \ - be_aas_sample.obj \ - be_aas_sound.obj \ - be_ai2_dm.obj \ - be_ai2_dmnet.obj \ - be_ai2_main.obj \ - be_ai_char.obj \ - be_ai_chat.obj \ - be_ai_goal.obj \ - be_ai_load.obj \ - be_ai_move.obj \ - be_ai_weap.obj \ - be_ai_weight.obj \ - be_ea.obj \ - be_interface.obj \ - l_crc.obj \ - l_libvar.obj \ - l_log.obj \ - l_memory.obj \ - l_precomp.obj \ - l_script.obj \ - l_struct.obj \ - l_utils.obj \ - q_shared.obj - -all: gladiator.dll - -gladiator.dll: $(OBJS) - lcclnk -dll -entry GetBotAPI *.obj botlib.def -o gladiator.dll - -clean: - del *.obj gladiator.dll - -%.obj: %.c - $(CC) $(CFLAGS) $< - diff --git a/tools/quake3/bspc/deps/botlib/linux-i386.mak b/tools/quake3/bspc/deps/botlib/linux-i386.mak deleted file mode 100644 index c9607a7b..00000000 --- a/tools/quake3/bspc/deps/botlib/linux-i386.mak +++ /dev/null @@ -1,92 +0,0 @@ -# -# Makefile for Gladiator Bot library: gladiator.so -# Intended for gcc/Linux -# - -ARCH=i386 -CC=gcc -BASE_CFLAGS=-Dstricmp=strcasecmp - -#use these cflags to optimize it -CFLAGS=$(BASE_CFLAGS) -m486 -O6 -ffast-math -funroll-loops \ - -fomit-frame-pointer -fexpensive-optimizations -malign-loops=2 \ - -malign-jumps=2 -malign-functions=2 -#use these when debugging -#CFLAGS=$(BASE_CFLAGS) -g - -LDFLAGS=-ldl -lm -SHLIBEXT=so -SHLIBCFLAGS=-fPIC -SHLIBLDFLAGS=-shared - -DO_CC=$(CC) $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< - -############################################################################# -# SETUP AND BUILD -# GLADIATOR BOT -############################################################################# - -.c.o: - $(DO_CC) - -GAME_OBJS = \ - be_aas_bsphl.o\ - be_aas_bspq2.o\ - be_aas_cluster.o\ - be_aas_debug.o\ - be_aas_entity.o\ - be_aas_file.o\ - be_aas_light.o\ - be_aas_main.o\ - be_aas_move.o\ - be_aas_optimize.o\ - be_aas_reach.o\ - be_aas_route.o\ - be_aas_routealt.o\ - be_aas_sample.o\ - be_aas_sound.o\ - be_ai2_dmq2.o\ - be_ai2_dmhl.o\ - be_ai2_dmnet.o\ - be_ai2_main.o\ - be_ai_char.o\ - be_ai_chat.o\ - be_ai_goal.o\ - be_ai_load.o\ - be_ai_move.o\ - be_ai_weap.o\ - be_ai_weight.o\ - be_ea.o\ - be_interface.o\ - l_crc.o\ - l_libvar.o\ - l_log.o\ - l_memory.o\ - l_precomp.o\ - l_script.o\ - l_struct.o\ - l_utils.o\ - q_shared.o - -glad$(ARCH).$(SHLIBEXT) : $(GAME_OBJS) - $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(GAME_OBJS) - - -############################################################################# -# MISC -############################################################################# - -clean: - -rm -f $(GAME_OBJS) - -depend: - gcc -MM $(GAME_OBJS:.o=.c) - - -install: - cp gladiator.so .. - -# -# From "make depend" -# - diff --git a/tools/quake3/bspc/deps/qcommon/cm_load.c b/tools/quake3/bspc/deps/qcommon/cm_load.c deleted file mode 100644 index 95a5fb5d..00000000 --- a/tools/quake3/bspc/deps/qcommon/cm_load.c +++ /dev/null @@ -1,839 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// cmodel.c -- model loading - -#include "cm_local.h" - -#ifdef BSPC - -#include "../../l_qfiles.h" - -void SetPlaneSignbits (cplane_t *out) { - int bits, j; - - // for fast box on planeside test - bits = 0; - for (j=0 ; j<3 ; j++) { - if (out->normal[j] < 0) { - bits |= 1<signbits = bits; -} -#endif //BSPC - -// to allow boxes to be treated as brush models, we allocate -// some extra indexes along with those needed by the map -#define BOX_BRUSHES 1 -#define BOX_SIDES 6 -#define BOX_LEAFS 2 -#define BOX_PLANES 12 - -#define LL(x) x=LittleLong(x) - - -clipMap_t cm; -int c_pointcontents; -int c_traces, c_brush_traces, c_patch_traces; - - -byte *cmod_base; - -#ifndef BSPC -cvar_t *cm_noAreas; -cvar_t *cm_noCurves; -cvar_t *cm_playerCurveClip; -#endif - -cmodel_t box_model; -cplane_t *box_planes; -cbrush_t *box_brush; - - - -void CM_InitBoxHull (void); -void CM_FloodAreaConnections (void); - - -/* -=============================================================================== - - MAP LOADING - -=============================================================================== -*/ - -/* -================= -CMod_LoadShaders -================= -*/ -void CMod_LoadShaders( lump_t *l ) { - dshader_t *in, *out; - int i, count; - - in = (void *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) { - Com_Error (ERR_DROP, "CMod_LoadShaders: funny lump size"); - } - count = l->filelen / sizeof(*in); - - if (count < 1) { - Com_Error (ERR_DROP, "Map with no shaders"); - } - cm.shaders = Hunk_Alloc( count * sizeof( *cm.shaders ), h_high ); - cm.numShaders = count; - - Com_Memcpy( cm.shaders, in, count * sizeof( *cm.shaders ) ); - - out = cm.shaders; - for ( i=0 ; icontentFlags = LittleLong( out->contentFlags ); - out->surfaceFlags = LittleLong( out->surfaceFlags ); - } -} - - -/* -================= -CMod_LoadSubmodels -================= -*/ -void CMod_LoadSubmodels( lump_t *l ) { - dmodel_t *in; - cmodel_t *out; - int i, j, count; - int *indexes; - - in = (void *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Com_Error (ERR_DROP, "CMod_LoadSubmodels: funny lump size"); - count = l->filelen / sizeof(*in); - - if (count < 1) - Com_Error (ERR_DROP, "Map with no models"); - cm.cmodels = Hunk_Alloc( count * sizeof( *cm.cmodels ), h_high ); - cm.numSubModels = count; - - if ( count > MAX_SUBMODELS ) { - Com_Error( ERR_DROP, "MAX_SUBMODELS exceeded" ); - } - - for ( i=0 ; imins[j] = LittleFloat (in->mins[j]) - 1; - out->maxs[j] = LittleFloat (in->maxs[j]) + 1; - } - - if ( i == 0 ) { - continue; // world model doesn't need other info - } - - // make a "leaf" just to hold the model's brushes and surfaces - out->leaf.numLeafBrushes = LittleLong( in->numBrushes ); - indexes = Hunk_Alloc( out->leaf.numLeafBrushes * 4, h_high ); - out->leaf.firstLeafBrush = indexes - cm.leafbrushes; - for ( j = 0 ; j < out->leaf.numLeafBrushes ; j++ ) { - indexes[j] = LittleLong( in->firstBrush ) + j; - } - - out->leaf.numLeafSurfaces = LittleLong( in->numSurfaces ); - indexes = Hunk_Alloc( out->leaf.numLeafSurfaces * 4, h_high ); - out->leaf.firstLeafSurface = indexes - cm.leafsurfaces; - for ( j = 0 ; j < out->leaf.numLeafSurfaces ; j++ ) { - indexes[j] = LittleLong( in->firstSurface ) + j; - } - } -} - - -/* -================= -CMod_LoadNodes - -================= -*/ -void CMod_LoadNodes( lump_t *l ) { - dnode_t *in; - int child; - cNode_t *out; - int i, j, count; - - in = (void *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); - count = l->filelen / sizeof(*in); - - if (count < 1) - Com_Error (ERR_DROP, "Map has no nodes"); - cm.nodes = Hunk_Alloc( count * sizeof( *cm.nodes ), h_high ); - cm.numNodes = count; - - out = cm.nodes; - - for (i=0 ; iplane = cm.planes + LittleLong( in->planeNum ); - for (j=0 ; j<2 ; j++) - { - child = LittleLong (in->children[j]); - out->children[j] = child; - } - } - -} - -/* -================= -CM_BoundBrush - -================= -*/ -void CM_BoundBrush( cbrush_t *b ) { - b->bounds[0][0] = -b->sides[0].plane->dist; - b->bounds[1][0] = b->sides[1].plane->dist; - - b->bounds[0][1] = -b->sides[2].plane->dist; - b->bounds[1][1] = b->sides[3].plane->dist; - - b->bounds[0][2] = -b->sides[4].plane->dist; - b->bounds[1][2] = b->sides[5].plane->dist; -} - - -/* -================= -CMod_LoadBrushes - -================= -*/ -void CMod_LoadBrushes( lump_t *l ) { - dbrush_t *in; - cbrush_t *out; - int i, count; - - in = (void *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) { - Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); - } - count = l->filelen / sizeof(*in); - - cm.brushes = Hunk_Alloc( ( BOX_BRUSHES + count ) * sizeof( *cm.brushes ), h_high ); - cm.numBrushes = count; - - out = cm.brushes; - - for ( i=0 ; isides = cm.brushsides + LittleLong(in->firstSide); - out->numsides = LittleLong(in->numSides); - - out->shaderNum = LittleLong( in->shaderNum ); - if ( out->shaderNum < 0 || out->shaderNum >= cm.numShaders ) { - Com_Error( ERR_DROP, "CMod_LoadBrushes: bad shaderNum: %i", out->shaderNum ); - } - out->contents = cm.shaders[out->shaderNum].contentFlags; - - CM_BoundBrush( out ); - } - -} - -/* -================= -CMod_LoadLeafs -================= -*/ -void CMod_LoadLeafs (lump_t *l) -{ - int i; - cLeaf_t *out; - dleaf_t *in; - int count; - - in = (void *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); - count = l->filelen / sizeof(*in); - - if (count < 1) - Com_Error (ERR_DROP, "Map with no leafs"); - - cm.leafs = Hunk_Alloc( ( BOX_LEAFS + count ) * sizeof( *cm.leafs ), h_high ); - cm.numLeafs = count; - - out = cm.leafs; - for ( i=0 ; icluster = LittleLong (in->cluster); - out->area = LittleLong (in->area); - out->firstLeafBrush = LittleLong (in->firstLeafBrush); - out->numLeafBrushes = LittleLong (in->numLeafBrushes); - out->firstLeafSurface = LittleLong (in->firstLeafSurface); - out->numLeafSurfaces = LittleLong (in->numLeafSurfaces); - - if (out->cluster >= cm.numClusters) - cm.numClusters = out->cluster + 1; - if (out->area >= cm.numAreas) - cm.numAreas = out->area + 1; - } - - cm.areas = Hunk_Alloc( cm.numAreas * sizeof( *cm.areas ), h_high ); - cm.areaPortals = Hunk_Alloc( cm.numAreas * cm.numAreas * sizeof( *cm.areaPortals ), h_high ); -} - -/* -================= -CMod_LoadPlanes -================= -*/ -void CMod_LoadPlanes (lump_t *l) -{ - int i, j; - cplane_t *out; - dplane_t *in; - int count; - int bits; - - in = (void *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); - count = l->filelen / sizeof(*in); - - if (count < 1) - Com_Error (ERR_DROP, "Map with no planes"); - cm.planes = Hunk_Alloc( ( BOX_PLANES + count ) * sizeof( *cm.planes ), h_high ); - cm.numPlanes = count; - - out = cm.planes; - - for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); - if (out->normal[j] < 0) - bits |= 1<dist = LittleFloat (in->dist); - out->type = PlaneTypeForNormal( out->normal ); - out->signbits = bits; - } -} - -/* -================= -CMod_LoadLeafBrushes -================= -*/ -void CMod_LoadLeafBrushes (lump_t *l) -{ - int i; - int *out; - int *in; - int count; - - in = (void *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); - count = l->filelen / sizeof(*in); - - cm.leafbrushes = Hunk_Alloc( (count + BOX_BRUSHES) * sizeof( *cm.leafbrushes ), h_high ); - cm.numLeafBrushes = count; - - out = cm.leafbrushes; - - for ( i=0 ; ifileofs); - if (l->filelen % sizeof(*in)) - Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); - count = l->filelen / sizeof(*in); - - cm.leafsurfaces = Hunk_Alloc( count * sizeof( *cm.leafsurfaces ), h_high ); - cm.numLeafSurfaces = count; - - out = cm.leafsurfaces; - - for ( i=0 ; ifileofs); - if ( l->filelen % sizeof(*in) ) { - Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); - } - count = l->filelen / sizeof(*in); - - cm.brushsides = Hunk_Alloc( ( BOX_SIDES + count ) * sizeof( *cm.brushsides ), h_high ); - cm.numBrushSides = count; - - out = cm.brushsides; - - for ( i=0 ; iplaneNum ); - out->plane = &cm.planes[num]; - out->shaderNum = LittleLong( in->shaderNum ); - if ( out->shaderNum < 0 || out->shaderNum >= cm.numShaders ) { - Com_Error( ERR_DROP, "CMod_LoadBrushSides: bad shaderNum: %i", out->shaderNum ); - } - out->surfaceFlags = cm.shaders[out->shaderNum].surfaceFlags; - } -} - - -/* -================= -CMod_LoadEntityString -================= -*/ -void CMod_LoadEntityString( lump_t *l ) { - cm.entityString = Hunk_Alloc( l->filelen, h_high ); - cm.numEntityChars = l->filelen; - Com_Memcpy (cm.entityString, cmod_base + l->fileofs, l->filelen); -} - -/* -================= -CMod_LoadVisibility -================= -*/ -#define VIS_HEADER 8 -void CMod_LoadVisibility( lump_t *l ) { - int len; - byte *buf; - - len = l->filelen; - if ( !len ) { - cm.clusterBytes = ( cm.numClusters + 31 ) & ~31; - cm.visibility = Hunk_Alloc( cm.clusterBytes, h_high ); - Com_Memset( cm.visibility, 255, cm.clusterBytes ); - return; - } - buf = cmod_base + l->fileofs; - - cm.vised = qtrue; - cm.visibility = Hunk_Alloc( len, h_high ); - cm.numClusters = LittleLong( ((int *)buf)[0] ); - cm.clusterBytes = LittleLong( ((int *)buf)[1] ); - Com_Memcpy (cm.visibility, buf + VIS_HEADER, len - VIS_HEADER ); -} - -//================================================================== - - -/* -================= -CMod_LoadPatches -================= -*/ -#define MAX_PATCH_VERTS 1024 -void CMod_LoadPatches( lump_t *surfs, lump_t *verts ) { - drawVert_t *dv, *dv_p; - dsurface_t *in; - int count; - int i, j; - int c; - cPatch_t *patch; - vec3_t points[MAX_PATCH_VERTS]; - int width, height; - int shaderNum; - - in = (void *)(cmod_base + surfs->fileofs); - if (surfs->filelen % sizeof(*in)) - Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); - cm.numSurfaces = count = surfs->filelen / sizeof(*in); - cm.surfaces = Hunk_Alloc( cm.numSurfaces * sizeof( cm.surfaces[0] ), h_high ); - - dv = (void *)(cmod_base + verts->fileofs); - if (verts->filelen % sizeof(*dv)) - Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); - - // scan through all the surfaces, but only load patches, - // not planar faces - for ( i = 0 ; i < count ; i++, in++ ) { - if ( LittleLong( in->surfaceType ) != MST_PATCH ) { - continue; // ignore other surfaces - } - // FIXME: check for non-colliding patches - - cm.surfaces[ i ] = patch = Hunk_Alloc( sizeof( *patch ), h_high ); - - // load the full drawverts onto the stack - width = LittleLong( in->patchWidth ); - height = LittleLong( in->patchHeight ); - c = width * height; - if ( c > MAX_PATCH_VERTS ) { - Com_Error( ERR_DROP, "ParseMesh: MAX_PATCH_VERTS" ); - } - - dv_p = dv + LittleLong( in->firstVert ); - for ( j = 0 ; j < c ; j++, dv_p++ ) { - points[j][0] = LittleFloat( dv_p->xyz[0] ); - points[j][1] = LittleFloat( dv_p->xyz[1] ); - points[j][2] = LittleFloat( dv_p->xyz[2] ); - } - - shaderNum = LittleLong( in->shaderNum ); - patch->contents = cm.shaders[shaderNum].contentFlags; - patch->surfaceFlags = cm.shaders[shaderNum].surfaceFlags; - - // create the internal facet structure - patch->pc = CM_GeneratePatchCollide( width, height, points ); - } -} - -//================================================================== - -unsigned CM_LumpChecksum(lump_t *lump) { - return LittleLong (Com_BlockChecksum (cmod_base + lump->fileofs, lump->filelen)); -} - -unsigned CM_Checksum(dheader_t *header) { - unsigned checksums[16]; - checksums[0] = CM_LumpChecksum(&header->lumps[LUMP_SHADERS]); - checksums[1] = CM_LumpChecksum(&header->lumps[LUMP_LEAFS]); - checksums[2] = CM_LumpChecksum(&header->lumps[LUMP_LEAFBRUSHES]); - checksums[3] = CM_LumpChecksum(&header->lumps[LUMP_LEAFSURFACES]); - checksums[4] = CM_LumpChecksum(&header->lumps[LUMP_PLANES]); - checksums[5] = CM_LumpChecksum(&header->lumps[LUMP_BRUSHSIDES]); - checksums[6] = CM_LumpChecksum(&header->lumps[LUMP_BRUSHES]); - checksums[7] = CM_LumpChecksum(&header->lumps[LUMP_MODELS]); - checksums[8] = CM_LumpChecksum(&header->lumps[LUMP_NODES]); - checksums[9] = CM_LumpChecksum(&header->lumps[LUMP_SURFACES]); - checksums[10] = CM_LumpChecksum(&header->lumps[LUMP_DRAWVERTS]); - - return LittleLong(Com_BlockChecksum(checksums, 11 * 4)); -} - -/* -================== -CM_LoadMap - -Loads in the map and all submodels -================== -*/ -void CM_LoadMap( const char *name, qboolean clientload, int *checksum ) { - int *buf; - int i; - dheader_t header; - int length; - static unsigned last_checksum; - - if ( !name || !name[0] ) { - Com_Error( ERR_DROP, "CM_LoadMap: NULL name" ); - } - -#ifndef BSPC - cm_noAreas = Cvar_Get ("cm_noAreas", "0", CVAR_CHEAT); - cm_noCurves = Cvar_Get ("cm_noCurves", "0", CVAR_CHEAT); - cm_playerCurveClip = Cvar_Get ("cm_playerCurveClip", "1", CVAR_ARCHIVE|CVAR_CHEAT ); -#endif - Com_DPrintf( "CM_LoadMap( %s, %i )\n", name, clientload ); - - if ( !strcmp( cm.name, name ) && clientload ) { - *checksum = last_checksum; - return; - } - - // free old stuff - Com_Memset( &cm, 0, sizeof( cm ) ); - CM_ClearLevelPatches(); - - if ( !name[0] ) { - cm.numLeafs = 1; - cm.numClusters = 1; - cm.numAreas = 1; - cm.cmodels = Hunk_Alloc( sizeof( *cm.cmodels ), h_high ); - *checksum = 0; - return; - } - - // - // load the file - // -#ifndef BSPC - length = FS_ReadFile( name, (void **)&buf ); -#else - length = LoadQuakeFile((quakefile_t *) name, (void **)&buf); -#endif - - if ( !buf ) { - Com_Error (ERR_DROP, "Couldn't load %s", name); - } - - last_checksum = LittleLong (Com_BlockChecksum (buf, length)); - *checksum = last_checksum; - - header = *(dheader_t *)buf; - for (i=0 ; i= cm.numSubModels ) { - Com_Error (ERR_DROP, "CM_InlineModel: bad number"); - } - return index; -} - -int CM_NumClusters( void ) { - return cm.numClusters; -} - -int CM_NumInlineModels( void ) { - return cm.numSubModels; -} - -char *CM_EntityString( void ) { - return cm.entityString; -} - -int CM_LeafCluster( int leafnum ) { - if (leafnum < 0 || leafnum >= cm.numLeafs) { - Com_Error (ERR_DROP, "CM_LeafCluster: bad number"); - } - return cm.leafs[leafnum].cluster; -} - -int CM_LeafArea( int leafnum ) { - if ( leafnum < 0 || leafnum >= cm.numLeafs ) { - Com_Error (ERR_DROP, "CM_LeafArea: bad number"); - } - return cm.leafs[leafnum].area; -} - -//======================================================================= - - -/* -=================== -CM_InitBoxHull - -Set up the planes and nodes so that the six floats of a bounding box -can just be stored out and get a proper clipping hull structure. -=================== -*/ -void CM_InitBoxHull (void) -{ - int i; - int side; - cplane_t *p; - cbrushside_t *s; - - box_planes = &cm.planes[cm.numPlanes]; - - box_brush = &cm.brushes[cm.numBrushes]; - box_brush->numsides = 6; - box_brush->sides = cm.brushsides + cm.numBrushSides; - box_brush->contents = CONTENTS_BODY; - - box_model.leaf.numLeafBrushes = 1; -// box_model.leaf.firstLeafBrush = cm.numBrushes; - box_model.leaf.firstLeafBrush = cm.numLeafBrushes; - cm.leafbrushes[cm.numLeafBrushes] = cm.numBrushes; - - for (i=0 ; i<6 ; i++) - { - side = i&1; - - // brush sides - s = &cm.brushsides[cm.numBrushSides+i]; - s->plane = cm.planes + (cm.numPlanes+i*2+side); - s->surfaceFlags = 0; - - // planes - p = &box_planes[i*2]; - p->type = i>>1; - p->signbits = 0; - VectorClear (p->normal); - p->normal[i>>1] = 1; - - p = &box_planes[i*2+1]; - p->type = 3 + (i>>1); - p->signbits = 0; - VectorClear (p->normal); - p->normal[i>>1] = -1; - - SetPlaneSignbits( p ); - } -} - -/* -=================== -CM_TempBoxModel - -To keep everything totally uniform, bounding boxes are turned into small -BSP trees instead of being compared directly. -Capsules are handled differently though. -=================== -*/ -clipHandle_t CM_TempBoxModel( const vec3_t mins, const vec3_t maxs, int capsule ) { - - VectorCopy( mins, box_model.mins ); - VectorCopy( maxs, box_model.maxs ); - - if ( capsule ) { - return CAPSULE_MODEL_HANDLE; - } - - box_planes[0].dist = maxs[0]; - box_planes[1].dist = -maxs[0]; - box_planes[2].dist = mins[0]; - box_planes[3].dist = -mins[0]; - box_planes[4].dist = maxs[1]; - box_planes[5].dist = -maxs[1]; - box_planes[6].dist = mins[1]; - box_planes[7].dist = -mins[1]; - box_planes[8].dist = maxs[2]; - box_planes[9].dist = -maxs[2]; - box_planes[10].dist = mins[2]; - box_planes[11].dist = -mins[2]; - - VectorCopy( mins, box_brush->bounds[0] ); - VectorCopy( maxs, box_brush->bounds[1] ); - - return BOX_MODEL_HANDLE; -} - -/* -=================== -CM_ModelBounds -=================== -*/ -void CM_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) { - cmodel_t *cmod; - - cmod = CM_ClipHandleToModel( model ); - VectorCopy( cmod->mins, mins ); - VectorCopy( cmod->maxs, maxs ); -} - - diff --git a/tools/quake3/bspc/deps/qcommon/cm_local.h b/tools/quake3/bspc/deps/qcommon/cm_local.h deleted file mode 100644 index 5848fdc2..00000000 --- a/tools/quake3/bspc/deps/qcommon/cm_local.h +++ /dev/null @@ -1,196 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "q_shared.h" -#include "qcommon.h" -#include "cm_polylib.h" - -#define MAX_SUBMODELS 256 -#define BOX_MODEL_HANDLE 255 -#define CAPSULE_MODEL_HANDLE 254 - - -typedef struct { - cplane_t *plane; - int children[2]; // negative numbers are leafs -} cNode_t; - -typedef struct { - int cluster; - int area; - - int firstLeafBrush; - int numLeafBrushes; - - int firstLeafSurface; - int numLeafSurfaces; -} cLeaf_t; - -typedef struct cmodel_s { - vec3_t mins, maxs; - cLeaf_t leaf; // submodels don't reference the main tree -} cmodel_t; - -typedef struct { - cplane_t *plane; - int surfaceFlags; - int shaderNum; -} cbrushside_t; - -typedef struct { - int shaderNum; // the shader that determined the contents - int contents; - vec3_t bounds[2]; - int numsides; - cbrushside_t *sides; - int checkcount; // to avoid repeated testings -} cbrush_t; - - -typedef struct { - int checkcount; // to avoid repeated testings - int surfaceFlags; - int contents; - struct patchCollide_s *pc; -} cPatch_t; - - -typedef struct { - int floodnum; - int floodvalid; -} cArea_t; - -typedef struct { - char name[MAX_QPATH]; - - int numShaders; - dshader_t *shaders; - - int numBrushSides; - cbrushside_t *brushsides; - - int numPlanes; - cplane_t *planes; - - int numNodes; - cNode_t *nodes; - - int numLeafs; - cLeaf_t *leafs; - - int numLeafBrushes; - int *leafbrushes; - - int numLeafSurfaces; - int *leafsurfaces; - - int numSubModels; - cmodel_t *cmodels; - - int numBrushes; - cbrush_t *brushes; - - int numClusters; - int clusterBytes; - byte *visibility; - qboolean vised; // if false, visibility is just a single cluster of ffs - - int numEntityChars; - char *entityString; - - int numAreas; - cArea_t *areas; - int *areaPortals; // [ numAreas*numAreas ] reference counts - - int numSurfaces; - cPatch_t **surfaces; // non-patches will be NULL - - int floodvalid; - int checkcount; // incremented on each trace -} clipMap_t; - - -// keep 1/8 unit away to keep the position valid before network snapping -// and to avoid various numeric issues -#define SURFACE_CLIP_EPSILON (0.125) - -extern clipMap_t cm; -extern int c_pointcontents; -extern int c_traces, c_brush_traces, c_patch_traces; -extern cvar_t *cm_noAreas; -extern cvar_t *cm_noCurves; -extern cvar_t *cm_playerCurveClip; - -// cm_test.c - -// Used for oriented capsule collision detection -typedef struct -{ - qboolean use; - float radius; - float halfheight; - vec3_t offset; -} sphere_t; - -typedef struct { - vec3_t start; - vec3_t end; - vec3_t size[2]; // size of the box being swept through the model - vec3_t offsets[8]; // [signbits][x] = either size[0][x] or size[1][x] - float maxOffset; // longest corner length from origin - vec3_t extents; // greatest of abs(size[0]) and abs(size[1]) - vec3_t bounds[2]; // enclosing box of start and end surrounding by size - vec3_t modelOrigin;// origin of the model tracing through - int contents; // ored contents of the model tracing through - qboolean isPoint; // optimized case - trace_t trace; // returned from trace call - sphere_t sphere; // sphere for oriendted capsule collision -} traceWork_t; - -typedef struct leafList_s { - int count; - int maxcount; - qboolean overflowed; - int *list; - vec3_t bounds[2]; - int lastLeaf; // for overflows where each leaf can't be stored individually - void (*storeLeafs)( struct leafList_s *ll, int nodenum ); -} leafList_t; - - -int CM_BoxBrushes( const vec3_t mins, const vec3_t maxs, cbrush_t **list, int listsize ); - -void CM_StoreLeafs( leafList_t *ll, int nodenum ); -void CM_StoreBrushes( leafList_t *ll, int nodenum ); - -void CM_BoxLeafnums_r( leafList_t *ll, int nodenum ); - -cmodel_t *CM_ClipHandleToModel( clipHandle_t handle ); -qboolean CM_BoundsIntersect( const vec3_t mins, const vec3_t maxs, const vec3_t mins2, const vec3_t maxs2 ); -qboolean CM_BoundsIntersectPoint( const vec3_t mins, const vec3_t maxs, const vec3_t point ); - -// cm_patch.c - -struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *points ); -void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); -qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); -void CM_ClearLevelPatches( void ); diff --git a/tools/quake3/bspc/deps/qcommon/cm_patch.c b/tools/quake3/bspc/deps/qcommon/cm_patch.c deleted file mode 100644 index 7894d9f5..00000000 --- a/tools/quake3/bspc/deps/qcommon/cm_patch.c +++ /dev/null @@ -1,1773 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "cm_local.h" -#include "cm_patch.h" - -/* - -This file does not reference any globals, and has these entry points: - -void CM_ClearLevelPatches( void ); -struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, const vec3_t *points ); -void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); -qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); -void CM_DrawDebugSurface( void (*drawPoly)(int color, int numPoints, flaot *points) ); - - -WARNING: this may misbehave with meshes that have rows or columns that only -degenerate a few triangles. Completely degenerate rows and columns are handled -properly. -*/ - -/* -#define MAX_FACETS 1024 -#define MAX_PATCH_PLANES 2048 - -typedef struct { - float plane[4]; - int signbits; // signx + (signy<<1) + (signz<<2), used as lookup during collision -} patchPlane_t; - -typedef struct { - int surfacePlane; - int numBorders; // 3 or four + 6 axial bevels + 4 or 3 * 4 edge bevels - int borderPlanes[4+6+16]; - int borderInward[4+6+16]; - qboolean borderNoAdjust[4+6+16]; -} facet_t; - -typedef struct patchCollide_s { - vec3_t bounds[2]; - int numPlanes; // surface planes plus edge planes - patchPlane_t *planes; - int numFacets; - facet_t *facets; -} patchCollide_t; - - -#define MAX_GRID_SIZE 129 - -typedef struct { - int width; - int height; - qboolean wrapWidth; - qboolean wrapHeight; - vec3_t points[MAX_GRID_SIZE][MAX_GRID_SIZE]; // [width][height] -} cGrid_t; - -#define SUBDIVIDE_DISTANCE 16 //4 // never more than this units away from curve -#define PLANE_TRI_EPSILON 0.1 -#define WRAP_POINT_EPSILON 0.1 -*/ - -int c_totalPatchBlocks; -int c_totalPatchSurfaces; -int c_totalPatchEdges; - -static const patchCollide_t *debugPatchCollide; -static const facet_t *debugFacet; -static qboolean debugBlock; -static vec3_t debugBlockPoints[4]; - -/* -================= -CM_ClearLevelPatches -================= -*/ -void CM_ClearLevelPatches( void ) { - debugPatchCollide = NULL; - debugFacet = NULL; -} - -/* -================= -CM_SignbitsForNormal -================= -*/ -static int CM_SignbitsForNormal( vec3_t normal ) { - int bits, j; - - bits = 0; - for (j=0 ; j<3 ; j++) { - if ( normal[j] < 0 ) { - bits |= 1<= SUBDIVIDE_DISTANCE; -} - -/* -=============== -CM_Subdivide - -a, b, and c are control points. -the subdivided sequence will be: a, out1, out2, out3, c -=============== -*/ -static void CM_Subdivide( vec3_t a, vec3_t b, vec3_t c, vec3_t out1, vec3_t out2, vec3_t out3 ) { - int i; - - for ( i = 0 ; i < 3 ; i++ ) { - out1[i] = 0.5 * (a[i] + b[i]); - out3[i] = 0.5 * (b[i] + c[i]); - out2[i] = 0.5 * (out1[i] + out3[i]); - } -} - -/* -================= -CM_TransposeGrid - -Swaps the rows and columns in place -================= -*/ -static void CM_TransposeGrid( cGrid_t *grid ) { - int i, j, l; - vec3_t temp; - qboolean tempWrap; - - if ( grid->width > grid->height ) { - for ( i = 0 ; i < grid->height ; i++ ) { - for ( j = i + 1 ; j < grid->width ; j++ ) { - if ( j < grid->height ) { - // swap the value - VectorCopy( grid->points[i][j], temp ); - VectorCopy( grid->points[j][i], grid->points[i][j] ); - VectorCopy( temp, grid->points[j][i] ); - } else { - // just copy - VectorCopy( grid->points[j][i], grid->points[i][j] ); - } - } - } - } else { - for ( i = 0 ; i < grid->width ; i++ ) { - for ( j = i + 1 ; j < grid->height ; j++ ) { - if ( j < grid->width ) { - // swap the value - VectorCopy( grid->points[j][i], temp ); - VectorCopy( grid->points[i][j], grid->points[j][i] ); - VectorCopy( temp, grid->points[i][j] ); - } else { - // just copy - VectorCopy( grid->points[i][j], grid->points[j][i] ); - } - } - } - } - - l = grid->width; - grid->width = grid->height; - grid->height = l; - - tempWrap = grid->wrapWidth; - grid->wrapWidth = grid->wrapHeight; - grid->wrapHeight = tempWrap; -} - -/* -=================== -CM_SetGridWrapWidth - -If the left and right columns are exactly equal, set grid->wrapWidth qtrue -=================== -*/ -static void CM_SetGridWrapWidth( cGrid_t *grid ) { - int i, j; - float d; - - for ( i = 0 ; i < grid->height ; i++ ) { - for ( j = 0 ; j < 3 ; j++ ) { - d = grid->points[0][i][j] - grid->points[grid->width-1][i][j]; - if ( d < -WRAP_POINT_EPSILON || d > WRAP_POINT_EPSILON ) { - break; - } - } - if ( j != 3 ) { - break; - } - } - if ( i == grid->height ) { - grid->wrapWidth = qtrue; - } else { - grid->wrapWidth = qfalse; - } -} - -/* -================= -CM_SubdivideGridColumns - -Adds columns as necessary to the grid until -all the aproximating points are within SUBDIVIDE_DISTANCE -from the true curve -================= -*/ -static void CM_SubdivideGridColumns( cGrid_t *grid ) { - int i, j, k; - - for ( i = 0 ; i < grid->width - 2 ; ) { - // grid->points[i][x] is an interpolating control point - // grid->points[i+1][x] is an aproximating control point - // grid->points[i+2][x] is an interpolating control point - - // - // first see if we can collapse the aproximating collumn away - // - for ( j = 0 ; j < grid->height ; j++ ) { - if ( CM_NeedsSubdivision( grid->points[i][j], grid->points[i+1][j], grid->points[i+2][j] ) ) { - break; - } - } - if ( j == grid->height ) { - // all of the points were close enough to the linear midpoints - // that we can collapse the entire column away - for ( j = 0 ; j < grid->height ; j++ ) { - // remove the column - for ( k = i + 2 ; k < grid->width ; k++ ) { - VectorCopy( grid->points[k][j], grid->points[k-1][j] ); - } - } - - grid->width--; - - // go to the next curve segment - i++; - continue; - } - - // - // we need to subdivide the curve - // - for ( j = 0 ; j < grid->height ; j++ ) { - vec3_t prev, mid, next; - - // save the control points now - VectorCopy( grid->points[i][j], prev ); - VectorCopy( grid->points[i+1][j], mid ); - VectorCopy( grid->points[i+2][j], next ); - - // make room for two additional columns in the grid - // columns i+1 will be replaced, column i+2 will become i+4 - // i+1, i+2, and i+3 will be generated - for ( k = grid->width - 1 ; k > i + 1 ; k-- ) { - VectorCopy( grid->points[k][j], grid->points[k+2][j] ); - } - - // generate the subdivided points - CM_Subdivide( prev, mid, next, grid->points[i+1][j], grid->points[i+2][j], grid->points[i+3][j] ); - } - - grid->width += 2; - - // the new aproximating point at i+1 may need to be removed - // or subdivided farther, so don't advance i - } -} - -/* -====================== -CM_ComparePoints -====================== -*/ -#define POINT_EPSILON 0.1 -static qboolean CM_ComparePoints( float *a, float *b ) { - float d; - - d = a[0] - b[0]; - if ( d < -POINT_EPSILON || d > POINT_EPSILON ) { - return qfalse; - } - d = a[1] - b[1]; - if ( d < -POINT_EPSILON || d > POINT_EPSILON ) { - return qfalse; - } - d = a[2] - b[2]; - if ( d < -POINT_EPSILON || d > POINT_EPSILON ) { - return qfalse; - } - return qtrue; -} - -/* -================= -CM_RemoveDegenerateColumns - -If there are any identical columns, remove them -================= -*/ -static void CM_RemoveDegenerateColumns( cGrid_t *grid ) { - int i, j, k; - - for ( i = 0 ; i < grid->width - 1 ; i++ ) { - for ( j = 0 ; j < grid->height ; j++ ) { - if ( !CM_ComparePoints( grid->points[i][j], grid->points[i+1][j] ) ) { - break; - } - } - - if ( j != grid->height ) { - continue; // not degenerate - } - - for ( j = 0 ; j < grid->height ; j++ ) { - // remove the column - for ( k = i + 2 ; k < grid->width ; k++ ) { - VectorCopy( grid->points[k][j], grid->points[k-1][j] ); - } - } - grid->width--; - - // check against the next column - i--; - } -} - -/* -================================================================================ - -PATCH COLLIDE GENERATION - -================================================================================ -*/ - -static int numPlanes; -static patchPlane_t planes[MAX_PATCH_PLANES]; - -static int numFacets; -static facet_t facets[MAX_PATCH_PLANES]; //maybe MAX_FACETS ?? - -#define NORMAL_EPSILON 0.0001 -#define DIST_EPSILON 0.02 - -/* -================== -CM_PlaneEqual -================== -*/ -int CM_PlaneEqual(patchPlane_t *p, float plane[4], int *flipped) { - float invplane[4]; - - if ( - fabs(p->plane[0] - plane[0]) < NORMAL_EPSILON - && fabs(p->plane[1] - plane[1]) < NORMAL_EPSILON - && fabs(p->plane[2] - plane[2]) < NORMAL_EPSILON - && fabs(p->plane[3] - plane[3]) < DIST_EPSILON ) - { - *flipped = qfalse; - return qtrue; - } - - VectorNegate(plane, invplane); - invplane[3] = -plane[3]; - - if ( - fabs(p->plane[0] - invplane[0]) < NORMAL_EPSILON - && fabs(p->plane[1] - invplane[1]) < NORMAL_EPSILON - && fabs(p->plane[2] - invplane[2]) < NORMAL_EPSILON - && fabs(p->plane[3] - invplane[3]) < DIST_EPSILON ) - { - *flipped = qtrue; - return qtrue; - } - - return qfalse; -} - -/* -================== -CM_SnapVector -================== -*/ -void CM_SnapVector(vec3_t normal) { - int i; - - for (i=0 ; i<3 ; i++) - { - if ( fabs(normal[i] - 1) < NORMAL_EPSILON ) - { - VectorClear (normal); - normal[i] = 1; - break; - } - if ( fabs(normal[i] - -1) < NORMAL_EPSILON ) - { - VectorClear (normal); - normal[i] = -1; - break; - } - } -} - -/* -================== -CM_FindPlane2 -================== -*/ -int CM_FindPlane2(float plane[4], int *flipped) { - int i; - - // see if the points are close enough to an existing plane - for ( i = 0 ; i < numPlanes ; i++ ) { - if (CM_PlaneEqual(&planes[i], plane, flipped)) return i; - } - - // add a new plane - if ( numPlanes == MAX_PATCH_PLANES ) { - Com_Error( ERR_DROP, "MAX_PATCH_PLANES" ); - } - - Vector4Copy( plane, planes[numPlanes].plane ); - planes[numPlanes].signbits = CM_SignbitsForNormal( plane ); - - numPlanes++; - - *flipped = qfalse; - - return numPlanes-1; -} - -/* -================== -CM_FindPlane -================== -*/ -static int CM_FindPlane( float *p1, float *p2, float *p3 ) { - float plane[4]; - int i; - float d; - - if ( !CM_PlaneFromPoints( plane, p1, p2, p3 ) ) { - return -1; - } - - // see if the points are close enough to an existing plane - for ( i = 0 ; i < numPlanes ; i++ ) { - if ( DotProduct( plane, planes[i].plane ) < 0 ) { - continue; // allow backwards planes? - } - - d = DotProduct( p1, planes[i].plane ) - planes[i].plane[3]; - if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) { - continue; - } - - d = DotProduct( p2, planes[i].plane ) - planes[i].plane[3]; - if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) { - continue; - } - - d = DotProduct( p3, planes[i].plane ) - planes[i].plane[3]; - if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) { - continue; - } - - // found it - return i; - } - - // add a new plane - if ( numPlanes == MAX_PATCH_PLANES ) { - Com_Error( ERR_DROP, "MAX_PATCH_PLANES" ); - } - - Vector4Copy( plane, planes[numPlanes].plane ); - planes[numPlanes].signbits = CM_SignbitsForNormal( plane ); - - numPlanes++; - - return numPlanes-1; -} - -/* -================== -CM_PointOnPlaneSide -================== -*/ -static int CM_PointOnPlaneSide( float *p, int planeNum ) { - float *plane; - float d; - - if ( planeNum == -1 ) { - return SIDE_ON; - } - plane = planes[ planeNum ].plane; - - d = DotProduct( p, plane ) - plane[3]; - - if ( d > PLANE_TRI_EPSILON ) { - return SIDE_FRONT; - } - - if ( d < -PLANE_TRI_EPSILON ) { - return SIDE_BACK; - } - - return SIDE_ON; -} - -/* -================== -CM_GridPlane -================== -*/ -static int CM_GridPlane( int gridPlanes[MAX_GRID_SIZE][MAX_GRID_SIZE][2], int i, int j, int tri ) { - int p; - - p = gridPlanes[i][j][tri]; - if ( p != -1 ) { - return p; - } - p = gridPlanes[i][j][!tri]; - if ( p != -1 ) { - return p; - } - - // should never happen - Com_Printf( "WARNING: CM_GridPlane unresolvable\n" ); - return -1; -} - -/* -================== -CM_EdgePlaneNum -================== -*/ -static int CM_EdgePlaneNum( cGrid_t *grid, int gridPlanes[MAX_GRID_SIZE][MAX_GRID_SIZE][2], int i, int j, int k ) { - float *p1, *p2; - vec3_t up; - int p; - - switch ( k ) { - case 0: // top border - p1 = grid->points[i][j]; - p2 = grid->points[i+1][j]; - p = CM_GridPlane( gridPlanes, i, j, 0 ); - VectorMA( p1, 4, planes[ p ].plane, up ); - return CM_FindPlane( p1, p2, up ); - - case 2: // bottom border - p1 = grid->points[i][j+1]; - p2 = grid->points[i+1][j+1]; - p = CM_GridPlane( gridPlanes, i, j, 1 ); - VectorMA( p1, 4, planes[ p ].plane, up ); - return CM_FindPlane( p2, p1, up ); - - case 3: // left border - p1 = grid->points[i][j]; - p2 = grid->points[i][j+1]; - p = CM_GridPlane( gridPlanes, i, j, 1 ); - VectorMA( p1, 4, planes[ p ].plane, up ); - return CM_FindPlane( p2, p1, up ); - - case 1: // right border - p1 = grid->points[i+1][j]; - p2 = grid->points[i+1][j+1]; - p = CM_GridPlane( gridPlanes, i, j, 0 ); - VectorMA( p1, 4, planes[ p ].plane, up ); - return CM_FindPlane( p1, p2, up ); - - case 4: // diagonal out of triangle 0 - p1 = grid->points[i+1][j+1]; - p2 = grid->points[i][j]; - p = CM_GridPlane( gridPlanes, i, j, 0 ); - VectorMA( p1, 4, planes[ p ].plane, up ); - return CM_FindPlane( p1, p2, up ); - - case 5: // diagonal out of triangle 1 - p1 = grid->points[i][j]; - p2 = grid->points[i+1][j+1]; - p = CM_GridPlane( gridPlanes, i, j, 1 ); - VectorMA( p1, 4, planes[ p ].plane, up ); - return CM_FindPlane( p1, p2, up ); - - } - - Com_Error( ERR_DROP, "CM_EdgePlaneNum: bad k" ); - return -1; -} - -/* -=================== -CM_SetBorderInward -=================== -*/ -static void CM_SetBorderInward( facet_t *facet, cGrid_t *grid, int gridPlanes[MAX_GRID_SIZE][MAX_GRID_SIZE][2], - int i, int j, int which ) { - int k, l; - float *points[4]; - int numPoints; - - switch ( which ) { - case -1: - points[0] = grid->points[i][j]; - points[1] = grid->points[i+1][j]; - points[2] = grid->points[i+1][j+1]; - points[3] = grid->points[i][j+1]; - numPoints = 4; - break; - case 0: - points[0] = grid->points[i][j]; - points[1] = grid->points[i+1][j]; - points[2] = grid->points[i+1][j+1]; - numPoints = 3; - break; - case 1: - points[0] = grid->points[i+1][j+1]; - points[1] = grid->points[i][j+1]; - points[2] = grid->points[i][j]; - numPoints = 3; - break; - default: - Com_Error( ERR_FATAL, "CM_SetBorderInward: bad parameter" ); - numPoints = 0; - break; - } - - for ( k = 0 ; k < facet->numBorders ; k++ ) { - int front, back; - - front = 0; - back = 0; - - for ( l = 0 ; l < numPoints ; l++ ) { - int side; - - side = CM_PointOnPlaneSide( points[l], facet->borderPlanes[k] ); - if ( side == SIDE_FRONT ) { - front++; - } if ( side == SIDE_BACK ) { - back++; - } - } - - if ( front && !back ) { - facet->borderInward[k] = qtrue; - } else if ( back && !front ) { - facet->borderInward[k] = qfalse; - } else if ( !front && !back ) { - // flat side border - facet->borderPlanes[k] = -1; - } else { - // bisecting side border - Com_DPrintf( "WARNING: CM_SetBorderInward: mixed plane sides\n" ); - facet->borderInward[k] = qfalse; - if ( !debugBlock ) { - debugBlock = qtrue; - VectorCopy( grid->points[i][j], debugBlockPoints[0] ); - VectorCopy( grid->points[i+1][j], debugBlockPoints[1] ); - VectorCopy( grid->points[i+1][j+1], debugBlockPoints[2] ); - VectorCopy( grid->points[i][j+1], debugBlockPoints[3] ); - } - } - } -} - -/* -================== -CM_ValidateFacet - -If the facet isn't bounded by its borders, we screwed up. -================== -*/ -static qboolean CM_ValidateFacet( facet_t *facet ) { - float plane[4]; - int j; - winding_t *w; - vec3_t bounds[2]; - - if ( facet->surfacePlane == -1 ) { - return qfalse; - } - - Vector4Copy( planes[ facet->surfacePlane ].plane, plane ); - w = BaseWindingForPlane( plane, plane[3] ); - for ( j = 0 ; j < facet->numBorders && w ; j++ ) { - if ( facet->borderPlanes[j] == -1 ) { - return qfalse; - } - Vector4Copy( planes[ facet->borderPlanes[j] ].plane, plane ); - if ( !facet->borderInward[j] ) { - VectorSubtract( vec3_origin, plane, plane ); - plane[3] = -plane[3]; - } - ChopWindingInPlace( &w, plane, plane[3], 0.1f ); - } - - if ( !w ) { - return qfalse; // winding was completely chopped away - } - - // see if the facet is unreasonably large - WindingBounds( w, bounds[0], bounds[1] ); - FreeWinding( w ); - - for ( j = 0 ; j < 3 ; j++ ) { - if ( bounds[1][j] - bounds[0][j] > MAX_MAP_BOUNDS ) { - return qfalse; // we must be missing a plane - } - if ( bounds[0][j] >= MAX_MAP_BOUNDS ) { - return qfalse; - } - if ( bounds[1][j] <= -MAX_MAP_BOUNDS ) { - return qfalse; - } - } - return qtrue; // winding is fine -} - -/* -================== -CM_AddFacetBevels -================== -*/ -void CM_AddFacetBevels( facet_t *facet ) { - - int i, j, k, l; - int axis, dir, order, flipped; - float plane[4], d, newplane[4]; - winding_t *w, *w2; - vec3_t mins, maxs, vec, vec2; - - Vector4Copy( planes[ facet->surfacePlane ].plane, plane ); - - w = BaseWindingForPlane( plane, plane[3] ); - for ( j = 0 ; j < facet->numBorders && w ; j++ ) { - if (facet->borderPlanes[j] == facet->surfacePlane) continue; - Vector4Copy( planes[ facet->borderPlanes[j] ].plane, plane ); - - if ( !facet->borderInward[j] ) { - VectorSubtract( vec3_origin, plane, plane ); - plane[3] = -plane[3]; - } - - ChopWindingInPlace( &w, plane, plane[3], 0.1f ); - } - if ( !w ) { - return; - } - - WindingBounds(w, mins, maxs); - - // add the axial planes - order = 0; - for ( axis = 0 ; axis < 3 ; axis++ ) - { - for ( dir = -1 ; dir <= 1 ; dir += 2, order++ ) - { - VectorClear(plane); - plane[axis] = dir; - if (dir == 1) { - plane[3] = maxs[axis]; - } - else { - plane[3] = -mins[axis]; - } - //if it's the surface plane - if (CM_PlaneEqual(&planes[facet->surfacePlane], plane, &flipped)) { - continue; - } - // see if the plane is allready present - for ( i = 0 ; i < facet->numBorders ; i++ ) { - if (CM_PlaneEqual(&planes[facet->borderPlanes[i]], plane, &flipped)) - break; - } - - if ( i == facet->numBorders ) { - if (facet->numBorders > 4 + 6 + 16) Com_Printf("ERROR: too many bevels\n"); - facet->borderPlanes[facet->numBorders] = CM_FindPlane2(plane, &flipped); - facet->borderNoAdjust[facet->numBorders] = 0; - facet->borderInward[facet->numBorders] = flipped; - facet->numBorders++; - } - } - } - // - // add the edge bevels - // - // test the non-axial plane edges - for ( j = 0 ; j < w->numpoints ; j++ ) - { - k = (j+1)%w->numpoints; - VectorSubtract (w->p[j], w->p[k], vec); - //if it's a degenerate edge - if (VectorNormalize (vec) < 0.5) - continue; - CM_SnapVector(vec); - for ( k = 0; k < 3 ; k++ ) - if ( vec[k] == -1 || vec[k] == 1 ) - break; // axial - if ( k < 3 ) - continue; // only test non-axial edges - - // try the six possible slanted axials from this edge - for ( axis = 0 ; axis < 3 ; axis++ ) - { - for ( dir = -1 ; dir <= 1 ; dir += 2 ) - { - // construct a plane - VectorClear (vec2); - vec2[axis] = dir; - CrossProduct (vec, vec2, plane); - if (VectorNormalize (plane) < 0.5) - continue; - plane[3] = DotProduct (w->p[j], plane); - - // if all the points of the facet winding are - // behind this plane, it is a proper edge bevel - for ( l = 0 ; l < w->numpoints ; l++ ) - { - d = DotProduct (w->p[l], plane) - plane[3]; - if (d > 0.1) - break; // point in front - } - if ( l < w->numpoints ) - continue; - - //if it's the surface plane - if (CM_PlaneEqual(&planes[facet->surfacePlane], plane, &flipped)) { - continue; - } - // see if the plane is allready present - for ( i = 0 ; i < facet->numBorders ; i++ ) { - if (CM_PlaneEqual(&planes[facet->borderPlanes[i]], plane, &flipped)) { - break; - } - } - - if ( i == facet->numBorders ) { - if (facet->numBorders > 4 + 6 + 16) Com_Printf("ERROR: too many bevels\n"); - facet->borderPlanes[facet->numBorders] = CM_FindPlane2(plane, &flipped); - - for ( k = 0 ; k < facet->numBorders ; k++ ) { - if (facet->borderPlanes[facet->numBorders] == - facet->borderPlanes[k]) Com_Printf("WARNING: bevel plane already used\n"); - } - - facet->borderNoAdjust[facet->numBorders] = 0; - facet->borderInward[facet->numBorders] = flipped; - // - w2 = CopyWinding(w); - Vector4Copy(planes[facet->borderPlanes[facet->numBorders]].plane, newplane); - if (!facet->borderInward[facet->numBorders]) - { - VectorNegate(newplane, newplane); - newplane[3] = -newplane[3]; - } //end if - ChopWindingInPlace( &w2, newplane, newplane[3], 0.1f ); - if (!w2) { - Com_DPrintf("WARNING: CM_AddFacetBevels... invalid bevel\n"); - continue; - } - else { - FreeWinding(w2); - } - // - facet->numBorders++; - //already got a bevel -// break; - } - } - } - } - FreeWinding( w ); - -#ifndef BSPC - //add opposite plane - facet->borderPlanes[facet->numBorders] = facet->surfacePlane; - facet->borderNoAdjust[facet->numBorders] = 0; - facet->borderInward[facet->numBorders] = qtrue; - facet->numBorders++; -#endif //BSPC - -} - -typedef enum { - EN_TOP, - EN_RIGHT, - EN_BOTTOM, - EN_LEFT -} edgeName_t; - -/* -================== -CM_PatchCollideFromGrid -================== -*/ -static void CM_PatchCollideFromGrid( cGrid_t *grid, patchCollide_t *pf ) { - int i, j; - float *p1, *p2, *p3; - //MAC_STATIC int gridPlanes[MAX_GRID_SIZE][MAX_GRID_SIZE][2]; - int gridPlanes[MAX_GRID_SIZE][MAX_GRID_SIZE][2]; - facet_t *facet; - int borders[4]; - int noAdjust[4]; - - numPlanes = 0; - numFacets = 0; - - // find the planes for each triangle of the grid - for ( i = 0 ; i < grid->width - 1 ; i++ ) { - for ( j = 0 ; j < grid->height - 1 ; j++ ) { - p1 = grid->points[i][j]; - p2 = grid->points[i+1][j]; - p3 = grid->points[i+1][j+1]; - gridPlanes[i][j][0] = CM_FindPlane( p1, p2, p3 ); - - p1 = grid->points[i+1][j+1]; - p2 = grid->points[i][j+1]; - p3 = grid->points[i][j]; - gridPlanes[i][j][1] = CM_FindPlane( p1, p2, p3 ); - } - } - - // create the borders for each facet - for ( i = 0 ; i < grid->width - 1 ; i++ ) { - for ( j = 0 ; j < grid->height - 1 ; j++ ) { - - borders[EN_TOP] = -1; - if ( j > 0 ) { - borders[EN_TOP] = gridPlanes[i][j-1][1]; - } else if ( grid->wrapHeight ) { - borders[EN_TOP] = gridPlanes[i][grid->height-2][1]; - } - noAdjust[EN_TOP] = ( borders[EN_TOP] == gridPlanes[i][j][0] ); - if ( borders[EN_TOP] == -1 || noAdjust[EN_TOP] ) { - borders[EN_TOP] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 0 ); - } - - borders[EN_BOTTOM] = -1; - if ( j < grid->height - 2 ) { - borders[EN_BOTTOM] = gridPlanes[i][j+1][0]; - } else if ( grid->wrapHeight ) { - borders[EN_BOTTOM] = gridPlanes[i][0][0]; - } - noAdjust[EN_BOTTOM] = ( borders[EN_BOTTOM] == gridPlanes[i][j][1] ); - if ( borders[EN_BOTTOM] == -1 || noAdjust[EN_BOTTOM] ) { - borders[EN_BOTTOM] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 2 ); - } - - borders[EN_LEFT] = -1; - if ( i > 0 ) { - borders[EN_LEFT] = gridPlanes[i-1][j][0]; - } else if ( grid->wrapWidth ) { - borders[EN_LEFT] = gridPlanes[grid->width-2][j][0]; - } - noAdjust[EN_LEFT] = ( borders[EN_LEFT] == gridPlanes[i][j][1] ); - if ( borders[EN_LEFT] == -1 || noAdjust[EN_LEFT] ) { - borders[EN_LEFT] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 3 ); - } - - borders[EN_RIGHT] = -1; - if ( i < grid->width - 2 ) { - borders[EN_RIGHT] = gridPlanes[i+1][j][1]; - } else if ( grid->wrapWidth ) { - borders[EN_RIGHT] = gridPlanes[0][j][1]; - } - noAdjust[EN_RIGHT] = ( borders[EN_RIGHT] == gridPlanes[i][j][0] ); - if ( borders[EN_RIGHT] == -1 || noAdjust[EN_RIGHT] ) { - borders[EN_RIGHT] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 1 ); - } - - if ( numFacets == MAX_FACETS ) { - Com_Error( ERR_DROP, "MAX_FACETS" ); - } - facet = &facets[numFacets]; - Com_Memset( facet, 0, sizeof( *facet ) ); - - if ( gridPlanes[i][j][0] == gridPlanes[i][j][1] ) { - if ( gridPlanes[i][j][0] == -1 ) { - continue; // degenrate - } - facet->surfacePlane = gridPlanes[i][j][0]; - facet->numBorders = 4; - facet->borderPlanes[0] = borders[EN_TOP]; - facet->borderNoAdjust[0] = noAdjust[EN_TOP]; - facet->borderPlanes[1] = borders[EN_RIGHT]; - facet->borderNoAdjust[1] = noAdjust[EN_RIGHT]; - facet->borderPlanes[2] = borders[EN_BOTTOM]; - facet->borderNoAdjust[2] = noAdjust[EN_BOTTOM]; - facet->borderPlanes[3] = borders[EN_LEFT]; - facet->borderNoAdjust[3] = noAdjust[EN_LEFT]; - CM_SetBorderInward( facet, grid, gridPlanes, i, j, -1 ); - if ( CM_ValidateFacet( facet ) ) { - CM_AddFacetBevels( facet ); - numFacets++; - } - } else { - // two seperate triangles - facet->surfacePlane = gridPlanes[i][j][0]; - facet->numBorders = 3; - facet->borderPlanes[0] = borders[EN_TOP]; - facet->borderNoAdjust[0] = noAdjust[EN_TOP]; - facet->borderPlanes[1] = borders[EN_RIGHT]; - facet->borderNoAdjust[1] = noAdjust[EN_RIGHT]; - facet->borderPlanes[2] = gridPlanes[i][j][1]; - if ( facet->borderPlanes[2] == -1 ) { - facet->borderPlanes[2] = borders[EN_BOTTOM]; - if ( facet->borderPlanes[2] == -1 ) { - facet->borderPlanes[2] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 4 ); - } - } - CM_SetBorderInward( facet, grid, gridPlanes, i, j, 0 ); - if ( CM_ValidateFacet( facet ) ) { - CM_AddFacetBevels( facet ); - numFacets++; - } - - if ( numFacets == MAX_FACETS ) { - Com_Error( ERR_DROP, "MAX_FACETS" ); - } - facet = &facets[numFacets]; - Com_Memset( facet, 0, sizeof( *facet ) ); - - facet->surfacePlane = gridPlanes[i][j][1]; - facet->numBorders = 3; - facet->borderPlanes[0] = borders[EN_BOTTOM]; - facet->borderNoAdjust[0] = noAdjust[EN_BOTTOM]; - facet->borderPlanes[1] = borders[EN_LEFT]; - facet->borderNoAdjust[1] = noAdjust[EN_LEFT]; - facet->borderPlanes[2] = gridPlanes[i][j][0]; - if ( facet->borderPlanes[2] == -1 ) { - facet->borderPlanes[2] = borders[EN_TOP]; - if ( facet->borderPlanes[2] == -1 ) { - facet->borderPlanes[2] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 5 ); - } - } - CM_SetBorderInward( facet, grid, gridPlanes, i, j, 1 ); - if ( CM_ValidateFacet( facet ) ) { - CM_AddFacetBevels( facet ); - numFacets++; - } - } - } - } - - // copy the results out - pf->numPlanes = numPlanes; - pf->numFacets = numFacets; - pf->facets = Hunk_Alloc( numFacets * sizeof( *pf->facets ), h_high ); - Com_Memcpy( pf->facets, facets, numFacets * sizeof( *pf->facets ) ); - pf->planes = Hunk_Alloc( numPlanes * sizeof( *pf->planes ), h_high ); - Com_Memcpy( pf->planes, planes, numPlanes * sizeof( *pf->planes ) ); -} - - -/* -=================== -CM_GeneratePatchCollide - -Creates an internal structure that will be used to perform -collision detection with a patch mesh. - -Points is packed as concatenated rows. -=================== -*/ -struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *points ) { - patchCollide_t *pf; - //MAC_STATIC cGrid_t grid; - cGrid_t grid; - int i, j; - - if ( width <= 2 || height <= 2 || !points ) { - Com_Error( ERR_DROP, "CM_GeneratePatchFacets: bad parameters: (%i, %i, %p)", - width, height, points ); - } - - if ( !(width & 1) || !(height & 1) ) { - Com_Error( ERR_DROP, "CM_GeneratePatchFacets: even sizes are invalid for quadratic meshes" ); - } - - if ( width > MAX_GRID_SIZE || height > MAX_GRID_SIZE ) { - Com_Error( ERR_DROP, "CM_GeneratePatchFacets: source is > MAX_GRID_SIZE" ); - } - - // build a grid - grid.width = width; - grid.height = height; - grid.wrapWidth = qfalse; - grid.wrapHeight = qfalse; - for ( i = 0 ; i < width ; i++ ) { - for ( j = 0 ; j < height ; j++ ) { - VectorCopy( points[j*width + i], grid.points[i][j] ); - } - } - - // subdivide the grid - CM_SetGridWrapWidth( &grid ); - CM_SubdivideGridColumns( &grid ); - CM_RemoveDegenerateColumns( &grid ); - - CM_TransposeGrid( &grid ); - - CM_SetGridWrapWidth( &grid ); - CM_SubdivideGridColumns( &grid ); - CM_RemoveDegenerateColumns( &grid ); - - // we now have a grid of points exactly on the curve - // the aproximate surface defined by these points will be - // collided against - pf = Hunk_Alloc( sizeof( *pf ), h_high ); - ClearBounds( pf->bounds[0], pf->bounds[1] ); - for ( i = 0 ; i < grid.width ; i++ ) { - for ( j = 0 ; j < grid.height ; j++ ) { - AddPointToBounds( grid.points[i][j], pf->bounds[0], pf->bounds[1] ); - } - } - - c_totalPatchBlocks += ( grid.width - 1 ) * ( grid.height - 1 ); - - // generate a bsp tree for the surface - CM_PatchCollideFromGrid( &grid, pf ); - - // expand by one unit for epsilon purposes - pf->bounds[0][0] -= 1; - pf->bounds[0][1] -= 1; - pf->bounds[0][2] -= 1; - - pf->bounds[1][0] += 1; - pf->bounds[1][1] += 1; - pf->bounds[1][2] += 1; - - return pf; -} - -/* -================================================================================ - -TRACE TESTING - -================================================================================ -*/ - -/* -==================== -CM_TracePointThroughPatchCollide - - special case for point traces because the patch collide "brushes" have no volume -==================== -*/ -void CM_TracePointThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ) { - qboolean frontFacing[MAX_PATCH_PLANES]; - float intersection[MAX_PATCH_PLANES]; - float intersect; - const patchPlane_t *planes; - const facet_t *facet; - int i, j, k; - float offset; - float d1, d2; -#ifndef BSPC - static cvar_t *cv; -#endif //BSPC - -#ifndef BSPC - if ( !cm_playerCurveClip->integer || !tw->isPoint ) { - return; - } -#endif - - // determine the trace's relationship to all planes - planes = pc->planes; - for ( i = 0 ; i < pc->numPlanes ; i++, planes++ ) { - offset = DotProduct( tw->offsets[ planes->signbits ], planes->plane ); - d1 = DotProduct( tw->start, planes->plane ) - planes->plane[3] + offset; - d2 = DotProduct( tw->end, planes->plane ) - planes->plane[3] + offset; - if ( d1 <= 0 ) { - frontFacing[i] = qfalse; - } else { - frontFacing[i] = qtrue; - } - if ( d1 == d2 ) { - intersection[i] = 99999; - } else { - intersection[i] = d1 / ( d1 - d2 ); - if ( intersection[i] <= 0 ) { - intersection[i] = 99999; - } - } - } - - - // see if any of the surface planes are intersected - facet = pc->facets; - for ( i = 0 ; i < pc->numFacets ; i++, facet++ ) { - if ( !frontFacing[facet->surfacePlane] ) { - continue; - } - intersect = intersection[facet->surfacePlane]; - if ( intersect < 0 ) { - continue; // surface is behind the starting point - } - if ( intersect > tw->trace.fraction ) { - continue; // already hit something closer - } - for ( j = 0 ; j < facet->numBorders ; j++ ) { - k = facet->borderPlanes[j]; - if ( frontFacing[k] ^ facet->borderInward[j] ) { - if ( intersection[k] > intersect ) { - break; - } - } else { - if ( intersection[k] < intersect ) { - break; - } - } - } - if ( j == facet->numBorders ) { - // we hit this facet -#ifndef BSPC - if (!cv) { - cv = Cvar_Get( "r_debugSurfaceUpdate", "1", 0 ); - } - if (cv->integer) { - debugPatchCollide = pc; - debugFacet = facet; - } -#endif //BSPC - planes = &pc->planes[facet->surfacePlane]; - - // calculate intersection with a slight pushoff - offset = DotProduct( tw->offsets[ planes->signbits ], planes->plane ); - d1 = DotProduct( tw->start, planes->plane ) - planes->plane[3] + offset; - d2 = DotProduct( tw->end, planes->plane ) - planes->plane[3] + offset; - tw->trace.fraction = ( d1 - SURFACE_CLIP_EPSILON ) / ( d1 - d2 ); - - if ( tw->trace.fraction < 0 ) { - tw->trace.fraction = 0; - } - - VectorCopy( planes->plane, tw->trace.plane.normal ); - tw->trace.plane.dist = planes->plane[3]; - } - } -} - -/* -==================== -CM_CheckFacetPlane -==================== -*/ -int CM_CheckFacetPlane(float *plane, vec3_t start, vec3_t end, float *enterFrac, float *leaveFrac, int *hit) { - float d1, d2, f; - - *hit = qfalse; - - d1 = DotProduct( start, plane ) - plane[3]; - d2 = DotProduct( end, plane ) - plane[3]; - - // if completely in front of face, no intersection with the entire facet - if (d1 > 0 && ( d2 >= SURFACE_CLIP_EPSILON || d2 >= d1 ) ) { - return qfalse; - } - - // if it doesn't cross the plane, the plane isn't relevent - if (d1 <= 0 && d2 <= 0 ) { - return qtrue; - } - - // crosses face - if (d1 > d2) { // enter - f = (d1-SURFACE_CLIP_EPSILON) / (d1-d2); - if ( f < 0 ) { - f = 0; - } - //always favor previous plane hits and thus also the surface plane hit - if (f > *enterFrac) { - *enterFrac = f; - *hit = qtrue; - } - } else { // leave - f = (d1+SURFACE_CLIP_EPSILON) / (d1-d2); - if ( f > 1 ) { - f = 1; - } - if (f < *leaveFrac) { - *leaveFrac = f; - } - } - return qtrue; -} - -/* -==================== -CM_TraceThroughPatchCollide -==================== -*/ -void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ) { - int i, j, hit, hitnum; - float offset, enterFrac, leaveFrac, t; - patchPlane_t *planes; - facet_t *facet; - float plane[4], bestplane[4]; - vec3_t startp, endp; -#ifndef BSPC - static cvar_t *cv; -#endif //BSPC - - if (tw->isPoint) { - CM_TracePointThroughPatchCollide( tw, pc ); - return; - } - - facet = pc->facets; - for ( i = 0 ; i < pc->numFacets ; i++, facet++ ) { - enterFrac = -1.0; - leaveFrac = 1.0; - hitnum = -1; - // - planes = &pc->planes[ facet->surfacePlane ]; - VectorCopy(planes->plane, plane); - plane[3] = planes->plane[3]; - if ( tw->sphere.use ) { - // adjust the plane distance apropriately for radius - plane[3] += tw->sphere.radius; - - // find the closest point on the capsule to the plane - t = DotProduct( plane, tw->sphere.offset ); - if ( t > 0.0f ) { - VectorSubtract( tw->start, tw->sphere.offset, startp ); - VectorSubtract( tw->end, tw->sphere.offset, endp ); - } - else { - VectorAdd( tw->start, tw->sphere.offset, startp ); - VectorAdd( tw->end, tw->sphere.offset, endp ); - } - } - else { - offset = DotProduct( tw->offsets[ planes->signbits ], plane); - plane[3] -= offset; - VectorCopy( tw->start, startp ); - VectorCopy( tw->end, endp ); - } - - if (!CM_CheckFacetPlane(plane, startp, endp, &enterFrac, &leaveFrac, &hit)) { - continue; - } - if (hit) { - Vector4Copy(plane, bestplane); - } - - for ( j = 0; j < facet->numBorders; j++ ) { - planes = &pc->planes[ facet->borderPlanes[j] ]; - if (facet->borderInward[j]) { - VectorNegate(planes->plane, plane); - plane[3] = -planes->plane[3]; - } - else { - VectorCopy(planes->plane, plane); - plane[3] = planes->plane[3]; - } - if ( tw->sphere.use ) { - // adjust the plane distance apropriately for radius - plane[3] += tw->sphere.radius; - - // find the closest point on the capsule to the plane - t = DotProduct( plane, tw->sphere.offset ); - if ( t > 0.0f ) { - VectorSubtract( tw->start, tw->sphere.offset, startp ); - VectorSubtract( tw->end, tw->sphere.offset, endp ); - } - else { - VectorAdd( tw->start, tw->sphere.offset, startp ); - VectorAdd( tw->end, tw->sphere.offset, endp ); - } - } - else { - // NOTE: this works even though the plane might be flipped because the bbox is centered - offset = DotProduct( tw->offsets[ planes->signbits ], plane); - plane[3] += fabs(offset); - VectorCopy( tw->start, startp ); - VectorCopy( tw->end, endp ); - } - - if (!CM_CheckFacetPlane(plane, startp, endp, &enterFrac, &leaveFrac, &hit)) { - break; - } - if (hit) { - hitnum = j; - Vector4Copy(plane, bestplane); - } - } - if (j < facet->numBorders) continue; - //never clip against the back side - if (hitnum == facet->numBorders - 1) continue; - - if (enterFrac < leaveFrac && enterFrac >= 0) { - if (enterFrac < tw->trace.fraction) { - if (enterFrac < 0) { - enterFrac = 0; - } -#ifndef BSPC - if (!cv) { - cv = Cvar_Get( "r_debugSurfaceUpdate", "1", 0 ); - } - if (cv && cv->integer) { - debugPatchCollide = pc; - debugFacet = facet; - } -#endif //BSPC - - tw->trace.fraction = enterFrac; - VectorCopy( bestplane, tw->trace.plane.normal ); - tw->trace.plane.dist = bestplane[3]; - } - } - } -} - - -/* -======================================================================= - -POSITION TEST - -======================================================================= -*/ - -/* -==================== -CM_PositionTestInPatchCollide -==================== -*/ -qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ) { - int i, j; - float offset, t; - patchPlane_t *planes; - facet_t *facet; - float plane[4]; - vec3_t startp; - - if (tw->isPoint) { - return qfalse; - } - // - facet = pc->facets; - for ( i = 0 ; i < pc->numFacets ; i++, facet++ ) { - planes = &pc->planes[ facet->surfacePlane ]; - VectorCopy(planes->plane, plane); - plane[3] = planes->plane[3]; - if ( tw->sphere.use ) { - // adjust the plane distance apropriately for radius - plane[3] += tw->sphere.radius; - - // find the closest point on the capsule to the plane - t = DotProduct( plane, tw->sphere.offset ); - if ( t > 0 ) { - VectorSubtract( tw->start, tw->sphere.offset, startp ); - } - else { - VectorAdd( tw->start, tw->sphere.offset, startp ); - } - } - else { - offset = DotProduct( tw->offsets[ planes->signbits ], plane); - plane[3] -= offset; - VectorCopy( tw->start, startp ); - } - - if ( DotProduct( plane, startp ) - plane[3] > 0.0f ) { - continue; - } - - for ( j = 0; j < facet->numBorders; j++ ) { - planes = &pc->planes[ facet->borderPlanes[j] ]; - if (facet->borderInward[j]) { - VectorNegate(planes->plane, plane); - plane[3] = -planes->plane[3]; - } - else { - VectorCopy(planes->plane, plane); - plane[3] = planes->plane[3]; - } - if ( tw->sphere.use ) { - // adjust the plane distance apropriately for radius - plane[3] += tw->sphere.radius; - - // find the closest point on the capsule to the plane - t = DotProduct( plane, tw->sphere.offset ); - if ( t > 0.0f ) { - VectorSubtract( tw->start, tw->sphere.offset, startp ); - } - else { - VectorAdd( tw->start, tw->sphere.offset, startp ); - } - } - else { - // NOTE: this works even though the plane might be flipped because the bbox is centered - offset = DotProduct( tw->offsets[ planes->signbits ], plane); - plane[3] += fabs(offset); - VectorCopy( tw->start, startp ); - } - - if ( DotProduct( plane, startp ) - plane[3] > 0.0f ) { - break; - } - } - if (j < facet->numBorders) { - continue; - } - // inside this patch facet - return qtrue; - } - return qfalse; -} - -/* -======================================================================= - -DEBUGGING - -======================================================================= -*/ - - -/* -================== -CM_DrawDebugSurface - -Called from the renderer -================== -*/ -#ifndef BSPC -void BotDrawDebugPolygons(void (*drawPoly)(int color, int numPoints, float *points), int value); -#endif - -void CM_DrawDebugSurface( void (*drawPoly)(int color, int numPoints, float *points) ) { - static cvar_t *cv; -#ifndef BSPC - static cvar_t *cv2; -#endif - const patchCollide_t *pc; - facet_t *facet; - winding_t *w; - int i, j, k, n; - int curplanenum, planenum, curinward, inward; - float plane[4]; - vec3_t mins = {-15, -15, -28}, maxs = {15, 15, 28}; - //vec3_t mins = {0, 0, 0}, maxs = {0, 0, 0}; - vec3_t v1, v2; - -#ifndef BSPC - if ( !cv2 ) - { - cv2 = Cvar_Get( "r_debugSurface", "0", 0 ); - } - - if (cv2->integer != 1) - { - BotDrawDebugPolygons(drawPoly, cv2->integer); - return; - } -#endif - - if ( !debugPatchCollide ) { - return; - } - -#ifndef BSPC - if ( !cv ) { - cv = Cvar_Get( "cm_debugSize", "2", 0 ); - } -#endif - pc = debugPatchCollide; - - for ( i = 0, facet = pc->facets ; i < pc->numFacets ; i++, facet++ ) { - - for ( k = 0 ; k < facet->numBorders + 1; k++ ) { - // - if (k < facet->numBorders) { - planenum = facet->borderPlanes[k]; - inward = facet->borderInward[k]; - } - else { - planenum = facet->surfacePlane; - inward = qfalse; - //continue; - } - - Vector4Copy( pc->planes[ planenum ].plane, plane ); - - //planenum = facet->surfacePlane; - if ( inward ) { - VectorSubtract( vec3_origin, plane, plane ); - plane[3] = -plane[3]; - } - - plane[3] += cv->value; - //* - for (n = 0; n < 3; n++) - { - if (plane[n] > 0) v1[n] = maxs[n]; - else v1[n] = mins[n]; - } //end for - VectorNegate(plane, v2); - plane[3] += fabs(DotProduct(v1, v2)); - //*/ - - w = BaseWindingForPlane( plane, plane[3] ); - for ( j = 0 ; j < facet->numBorders + 1 && w; j++ ) { - // - if (j < facet->numBorders) { - curplanenum = facet->borderPlanes[j]; - curinward = facet->borderInward[j]; - } - else { - curplanenum = facet->surfacePlane; - curinward = qfalse; - //continue; - } - // - if (curplanenum == planenum) continue; - - Vector4Copy( pc->planes[ curplanenum ].plane, plane ); - if ( !curinward ) { - VectorSubtract( vec3_origin, plane, plane ); - plane[3] = -plane[3]; - } - // if ( !facet->borderNoAdjust[j] ) { - plane[3] -= cv->value; - // } - for (n = 0; n < 3; n++) - { - if (plane[n] > 0) v1[n] = maxs[n]; - else v1[n] = mins[n]; - } //end for - VectorNegate(plane, v2); - plane[3] -= fabs(DotProduct(v1, v2)); - - ChopWindingInPlace( &w, plane, plane[3], 0.1f ); - } - if ( w ) { - if ( facet == debugFacet ) { - drawPoly( 4, w->numpoints, w->p[0] ); - //Com_Printf("blue facet has %d border planes\n", facet->numBorders); - } else { - drawPoly( 1, w->numpoints, w->p[0] ); - } - FreeWinding( w ); - } - else - Com_Printf("winding chopped away by border planes\n"); - } - } - - // draw the debug block - { - vec3_t v[3]; - - VectorCopy( debugBlockPoints[0], v[0] ); - VectorCopy( debugBlockPoints[1], v[1] ); - VectorCopy( debugBlockPoints[2], v[2] ); - drawPoly( 2, 3, v[0] ); - - VectorCopy( debugBlockPoints[2], v[0] ); - VectorCopy( debugBlockPoints[3], v[1] ); - VectorCopy( debugBlockPoints[0], v[2] ); - drawPoly( 2, 3, v[0] ); - } - -#if 0 - vec3_t v[4]; - - v[0][0] = pc->bounds[1][0]; - v[0][1] = pc->bounds[1][1]; - v[0][2] = pc->bounds[1][2]; - - v[1][0] = pc->bounds[1][0]; - v[1][1] = pc->bounds[0][1]; - v[1][2] = pc->bounds[1][2]; - - v[2][0] = pc->bounds[0][0]; - v[2][1] = pc->bounds[0][1]; - v[2][2] = pc->bounds[1][2]; - - v[3][0] = pc->bounds[0][0]; - v[3][1] = pc->bounds[1][1]; - v[3][2] = pc->bounds[1][2]; - - drawPoly( 4, v[0] ); -#endif -} diff --git a/tools/quake3/bspc/deps/qcommon/cm_patch.h b/tools/quake3/bspc/deps/qcommon/cm_patch.h deleted file mode 100644 index f5ba8b8b..00000000 --- a/tools/quake3/bspc/deps/qcommon/cm_patch.h +++ /dev/null @@ -1,103 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -//#define CULL_BBOX - -/* - -This file does not reference any globals, and has these entry points: - -void CM_ClearLevelPatches( void ); -struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, const vec3_t *points ); -void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); -qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); -void CM_DrawDebugSurface( void (*drawPoly)(int color, int numPoints, flaot *points) ); - - -Issues for collision against curved surfaces: - -Surface edges need to be handled differently than surface planes - -Plane expansion causes raw surfaces to expand past expanded bounding box - -Position test of a volume against a surface is tricky. - -Position test of a point against a surface is not well defined, because the surface has no volume. - - -Tracing leading edge points instead of volumes? -Position test by tracing corner to corner? (8*7 traces -- ouch) - -coplanar edges -triangulated patches -degenerate patches - - endcaps - degenerate - -WARNING: this may misbehave with meshes that have rows or columns that only -degenerate a few triangles. Completely degenerate rows and columns are handled -properly. -*/ - - -#define MAX_FACETS 1024 -#define MAX_PATCH_PLANES 2048 - -typedef struct { - float plane[4]; - int signbits; // signx + (signy<<1) + (signz<<2), used as lookup during collision -} patchPlane_t; - -typedef struct { - int surfacePlane; - int numBorders; // 3 or four + 6 axial bevels + 4 or 3 * 4 edge bevels - int borderPlanes[4+6+16]; - int borderInward[4+6+16]; - qboolean borderNoAdjust[4+6+16]; -} facet_t; - -typedef struct patchCollide_s { - vec3_t bounds[2]; - int numPlanes; // surface planes plus edge planes - patchPlane_t *planes; - int numFacets; - facet_t *facets; -} patchCollide_t; - - -#define MAX_GRID_SIZE 129 - -typedef struct { - int width; - int height; - qboolean wrapWidth; - qboolean wrapHeight; - vec3_t points[MAX_GRID_SIZE][MAX_GRID_SIZE]; // [width][height] -} cGrid_t; - -#define SUBDIVIDE_DISTANCE 16 //4 // never more than this units away from curve -#define PLANE_TRI_EPSILON 0.1 -#define WRAP_POINT_EPSILON 0.1 - - -struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *points ); diff --git a/tools/quake3/bspc/deps/qcommon/cm_polylib.c b/tools/quake3/bspc/deps/qcommon/cm_polylib.c deleted file mode 100644 index e96c7132..00000000 --- a/tools/quake3/bspc/deps/qcommon/cm_polylib.c +++ /dev/null @@ -1,737 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -// this is only used for visualization tools in cm_ debug functions - - -#include "cm_local.h" - - -// counters are only bumped when running single threaded, -// because they are an awefull coherence problem -int c_active_windings; -int c_peak_windings; -int c_winding_allocs; -int c_winding_points; - -void pw(winding_t *w) -{ - int i; - for (i=0 ; inumpoints ; i++) - printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]); -} - - -/* -============= -AllocWinding -============= -*/ -winding_t *AllocWinding (int points) -{ - winding_t *w; - int s; - - c_winding_allocs++; - c_winding_points += points; - c_active_windings++; - if (c_active_windings > c_peak_windings) - c_peak_windings = c_active_windings; - - s = sizeof(vec_t)*3*points + sizeof(int); - w = Z_Malloc (s); - Com_Memset (w, 0, s); - return w; -} - -void FreeWinding (winding_t *w) -{ - if (*(unsigned *)w == 0xdeaddead) - Com_Error (ERR_FATAL, "FreeWinding: freed a freed winding"); - *(unsigned *)w = 0xdeaddead; - - c_active_windings--; - Z_Free (w); -} - -/* -============ -RemoveColinearPoints -============ -*/ -int c_removed; - -void RemoveColinearPoints (winding_t *w) -{ - int i, j, k; - vec3_t v1, v2; - int nump; - vec3_t p[MAX_POINTS_ON_WINDING]; - - nump = 0; - for (i=0 ; inumpoints ; i++) - { - j = (i+1)%w->numpoints; - k = (i+w->numpoints-1)%w->numpoints; - VectorSubtract (w->p[j], w->p[i], v1); - VectorSubtract (w->p[i], w->p[k], v2); - VectorNormalize2(v1,v1); - VectorNormalize2(v2,v2); - if (DotProduct(v1, v2) < 0.999) - { - VectorCopy (w->p[i], p[nump]); - nump++; - } - } - - if (nump == w->numpoints) - return; - - c_removed += w->numpoints - nump; - w->numpoints = nump; - Com_Memcpy (w->p, p, nump*sizeof(p[0])); -} - -/* -============ -WindingPlane -============ -*/ -void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist) -{ - vec3_t v1, v2; - - VectorSubtract (w->p[1], w->p[0], v1); - VectorSubtract (w->p[2], w->p[0], v2); - CrossProduct (v2, v1, normal); - VectorNormalize2(normal, normal); - *dist = DotProduct (w->p[0], normal); - -} - -/* -============= -WindingArea -============= -*/ -vec_t WindingArea (winding_t *w) -{ - int i; - vec3_t d1, d2, cross; - vec_t total; - - total = 0; - for (i=2 ; inumpoints ; i++) - { - VectorSubtract (w->p[i-1], w->p[0], d1); - VectorSubtract (w->p[i], w->p[0], d2); - CrossProduct (d1, d2, cross); - total += 0.5 * VectorLength ( cross ); - } - return total; -} - -/* -============= -WindingBounds -============= -*/ -void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs) -{ - vec_t v; - int i,j; - - mins[0] = mins[1] = mins[2] = MAX_MAP_BOUNDS; - maxs[0] = maxs[1] = maxs[2] = -MAX_MAP_BOUNDS; - - for (i=0 ; inumpoints ; i++) - { - for (j=0 ; j<3 ; j++) - { - v = w->p[i][j]; - if (v < mins[j]) - mins[j] = v; - if (v > maxs[j]) - maxs[j] = v; - } - } -} - -/* -============= -WindingCenter -============= -*/ -void WindingCenter (winding_t *w, vec3_t center) -{ - int i; - float scale; - - VectorCopy (vec3_origin, center); - for (i=0 ; inumpoints ; i++) - VectorAdd (w->p[i], center, center); - - scale = 1.0/w->numpoints; - VectorScale (center, scale, center); -} - -/* -================= -BaseWindingForPlane -================= -*/ -winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist) -{ - int i, x; - vec_t max, v; - vec3_t org, vright, vup; - winding_t *w; - -// find the major axis - - max = -MAX_MAP_BOUNDS; - x = -1; - for (i=0 ; i<3; i++) - { - v = fabs(normal[i]); - if (v > max) - { - x = i; - max = v; - } - } - if (x==-1) - Com_Error (ERR_DROP, "BaseWindingForPlane: no axis found"); - - VectorCopy (vec3_origin, vup); - switch (x) - { - case 0: - case 1: - vup[2] = 1; - break; - case 2: - vup[0] = 1; - break; - } - - v = DotProduct (vup, normal); - VectorMA (vup, -v, normal, vup); - VectorNormalize2(vup, vup); - - VectorScale (normal, dist, org); - - CrossProduct (vup, normal, vright); - - VectorScale (vup, MAX_MAP_BOUNDS, vup); - VectorScale (vright, MAX_MAP_BOUNDS, vright); - -// project a really big axis aligned box onto the plane - w = AllocWinding (4); - - VectorSubtract (org, vright, w->p[0]); - VectorAdd (w->p[0], vup, w->p[0]); - - VectorAdd (org, vright, w->p[1]); - VectorAdd (w->p[1], vup, w->p[1]); - - VectorAdd (org, vright, w->p[2]); - VectorSubtract (w->p[2], vup, w->p[2]); - - VectorSubtract (org, vright, w->p[3]); - VectorSubtract (w->p[3], vup, w->p[3]); - - w->numpoints = 4; - - return w; -} - -/* -================== -CopyWinding -================== -*/ -winding_t *CopyWinding (winding_t *w) -{ - int size; - winding_t *c; - - c = AllocWinding (w->numpoints); - size = (int)((winding_t *)0)->p[w->numpoints]; - Com_Memcpy (c, w, size); - return c; -} - -/* -================== -ReverseWinding -================== -*/ -winding_t *ReverseWinding (winding_t *w) -{ - int i; - winding_t *c; - - c = AllocWinding (w->numpoints); - for (i=0 ; inumpoints ; i++) - { - VectorCopy (w->p[w->numpoints-1-i], c->p[i]); - } - c->numpoints = w->numpoints; - return c; -} - - -/* -============= -ClipWindingEpsilon -============= -*/ -void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, - vec_t epsilon, winding_t **front, winding_t **back) -{ - vec_t dists[MAX_POINTS_ON_WINDING+4]; - int sides[MAX_POINTS_ON_WINDING+4]; - int counts[3]; - static vec_t dot; // VC 4.2 optimizer bug if not static - int i, j; - vec_t *p1, *p2; - vec3_t mid; - winding_t *f, *b; - int maxpts; - - counts[0] = counts[1] = counts[2] = 0; - -// determine sides for each point - for (i=0 ; inumpoints ; i++) - { - dot = DotProduct (in->p[i], normal); - dot -= dist; - dists[i] = dot; - if (dot > epsilon) - sides[i] = SIDE_FRONT; - else if (dot < -epsilon) - sides[i] = SIDE_BACK; - else - { - sides[i] = SIDE_ON; - } - counts[sides[i]]++; - } - sides[i] = sides[0]; - dists[i] = dists[0]; - - *front = *back = NULL; - - if (!counts[0]) - { - *back = CopyWinding (in); - return; - } - if (!counts[1]) - { - *front = CopyWinding (in); - return; - } - - maxpts = in->numpoints+4; // cant use counts[0]+2 because - // of fp grouping errors - - *front = f = AllocWinding (maxpts); - *back = b = AllocWinding (maxpts); - - for (i=0 ; inumpoints ; i++) - { - p1 = in->p[i]; - - if (sides[i] == SIDE_ON) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - VectorCopy (p1, b->p[b->numpoints]); - b->numpoints++; - continue; - } - - if (sides[i] == SIDE_FRONT) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - } - if (sides[i] == SIDE_BACK) - { - VectorCopy (p1, b->p[b->numpoints]); - b->numpoints++; - } - - if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) - continue; - - // generate a split point - p2 = in->p[(i+1)%in->numpoints]; - - dot = dists[i] / (dists[i]-dists[i+1]); - for (j=0 ; j<3 ; j++) - { // avoid round off error when possible - if (normal[j] == 1) - mid[j] = dist; - else if (normal[j] == -1) - mid[j] = -dist; - else - mid[j] = p1[j] + dot*(p2[j]-p1[j]); - } - - VectorCopy (mid, f->p[f->numpoints]); - f->numpoints++; - VectorCopy (mid, b->p[b->numpoints]); - b->numpoints++; - } - - if (f->numpoints > maxpts || b->numpoints > maxpts) - Com_Error (ERR_DROP, "ClipWinding: points exceeded estimate"); - if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING) - Com_Error (ERR_DROP, "ClipWinding: MAX_POINTS_ON_WINDING"); -} - - -/* -============= -ChopWindingInPlace -============= -*/ -void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon) -{ - winding_t *in; - vec_t dists[MAX_POINTS_ON_WINDING+4]; - int sides[MAX_POINTS_ON_WINDING+4]; - int counts[3]; - static vec_t dot; // VC 4.2 optimizer bug if not static - int i, j; - vec_t *p1, *p2; - vec3_t mid; - winding_t *f; - int maxpts; - - in = *inout; - counts[0] = counts[1] = counts[2] = 0; - -// determine sides for each point - for (i=0 ; inumpoints ; i++) - { - dot = DotProduct (in->p[i], normal); - dot -= dist; - dists[i] = dot; - if (dot > epsilon) - sides[i] = SIDE_FRONT; - else if (dot < -epsilon) - sides[i] = SIDE_BACK; - else - { - sides[i] = SIDE_ON; - } - counts[sides[i]]++; - } - sides[i] = sides[0]; - dists[i] = dists[0]; - - if (!counts[0]) - { - FreeWinding (in); - *inout = NULL; - return; - } - if (!counts[1]) - return; // inout stays the same - - maxpts = in->numpoints+4; // cant use counts[0]+2 because - // of fp grouping errors - - f = AllocWinding (maxpts); - - for (i=0 ; inumpoints ; i++) - { - p1 = in->p[i]; - - if (sides[i] == SIDE_ON) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - continue; - } - - if (sides[i] == SIDE_FRONT) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - } - - if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) - continue; - - // generate a split point - p2 = in->p[(i+1)%in->numpoints]; - - dot = dists[i] / (dists[i]-dists[i+1]); - for (j=0 ; j<3 ; j++) - { // avoid round off error when possible - if (normal[j] == 1) - mid[j] = dist; - else if (normal[j] == -1) - mid[j] = -dist; - else - mid[j] = p1[j] + dot*(p2[j]-p1[j]); - } - - VectorCopy (mid, f->p[f->numpoints]); - f->numpoints++; - } - - if (f->numpoints > maxpts) - Com_Error (ERR_DROP, "ClipWinding: points exceeded estimate"); - if (f->numpoints > MAX_POINTS_ON_WINDING) - Com_Error (ERR_DROP, "ClipWinding: MAX_POINTS_ON_WINDING"); - - FreeWinding (in); - *inout = f; -} - - -/* -================= -ChopWinding - -Returns the fragment of in that is on the front side -of the cliping plane. The original is freed. -================= -*/ -winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist) -{ - winding_t *f, *b; - - ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b); - FreeWinding (in); - if (b) - FreeWinding (b); - return f; -} - - -/* -================= -CheckWinding - -================= -*/ -void CheckWinding (winding_t *w) -{ - int i, j; - vec_t *p1, *p2; - vec_t d, edgedist; - vec3_t dir, edgenormal, facenormal; - vec_t area; - vec_t facedist; - - if (w->numpoints < 3) - Com_Error (ERR_DROP, "CheckWinding: %i points",w->numpoints); - - area = WindingArea(w); - if (area < 1) - Com_Error (ERR_DROP, "CheckWinding: %f area", area); - - WindingPlane (w, facenormal, &facedist); - - for (i=0 ; inumpoints ; i++) - { - p1 = w->p[i]; - - for (j=0 ; j<3 ; j++) - if (p1[j] > MAX_MAP_BOUNDS || p1[j] < -MAX_MAP_BOUNDS) - Com_Error (ERR_DROP, "CheckFace: BUGUS_RANGE: %f",p1[j]); - - j = i+1 == w->numpoints ? 0 : i+1; - - // check the point is on the face plane - d = DotProduct (p1, facenormal) - facedist; - if (d < -ON_EPSILON || d > ON_EPSILON) - Com_Error (ERR_DROP, "CheckWinding: point off plane"); - - // check the edge isnt degenerate - p2 = w->p[j]; - VectorSubtract (p2, p1, dir); - - if (VectorLength (dir) < ON_EPSILON) - Com_Error (ERR_DROP, "CheckWinding: degenerate edge"); - - CrossProduct (facenormal, dir, edgenormal); - VectorNormalize2 (edgenormal, edgenormal); - edgedist = DotProduct (p1, edgenormal); - edgedist += ON_EPSILON; - - // all other points must be on front side - for (j=0 ; jnumpoints ; j++) - { - if (j == i) - continue; - d = DotProduct (w->p[j], edgenormal); - if (d > edgedist) - Com_Error (ERR_DROP, "CheckWinding: non-convex"); - } - } -} - - -/* -============ -WindingOnPlaneSide -============ -*/ -int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist) -{ - qboolean front, back; - int i; - vec_t d; - - front = qfalse; - back = qfalse; - for (i=0 ; inumpoints ; i++) - { - d = DotProduct (w->p[i], normal) - dist; - if (d < -ON_EPSILON) - { - if (front) - return SIDE_CROSS; - back = qtrue; - continue; - } - if (d > ON_EPSILON) - { - if (back) - return SIDE_CROSS; - front = qtrue; - continue; - } - } - - if (back) - return SIDE_BACK; - if (front) - return SIDE_FRONT; - return SIDE_ON; -} - - -/* -================= -AddWindingToConvexHull - -Both w and *hull are on the same plane -================= -*/ -#define MAX_HULL_POINTS 128 -void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ) { - int i, j, k; - float *p, *copy; - vec3_t dir; - float d; - int numHullPoints, numNew; - vec3_t hullPoints[MAX_HULL_POINTS]; - vec3_t newHullPoints[MAX_HULL_POINTS]; - vec3_t hullDirs[MAX_HULL_POINTS]; - qboolean hullSide[MAX_HULL_POINTS]; - qboolean outside; - - if ( !*hull ) { - *hull = CopyWinding( w ); - return; - } - - numHullPoints = (*hull)->numpoints; - Com_Memcpy( hullPoints, (*hull)->p, numHullPoints * sizeof(vec3_t) ); - - for ( i = 0 ; i < w->numpoints ; i++ ) { - p = w->p[i]; - - // calculate hull side vectors - for ( j = 0 ; j < numHullPoints ; j++ ) { - k = ( j + 1 ) % numHullPoints; - - VectorSubtract( hullPoints[k], hullPoints[j], dir ); - VectorNormalize2( dir, dir ); - CrossProduct( normal, dir, hullDirs[j] ); - } - - outside = qfalse; - for ( j = 0 ; j < numHullPoints ; j++ ) { - VectorSubtract( p, hullPoints[j], dir ); - d = DotProduct( dir, hullDirs[j] ); - if ( d >= ON_EPSILON ) { - outside = qtrue; - } - if ( d >= -ON_EPSILON ) { - hullSide[j] = qtrue; - } else { - hullSide[j] = qfalse; - } - } - - // if the point is effectively inside, do nothing - if ( !outside ) { - continue; - } - - // find the back side to front side transition - for ( j = 0 ; j < numHullPoints ; j++ ) { - if ( !hullSide[ j % numHullPoints ] && hullSide[ (j + 1) % numHullPoints ] ) { - break; - } - } - if ( j == numHullPoints ) { - continue; - } - - // insert the point here - VectorCopy( p, newHullPoints[0] ); - numNew = 1; - - // copy over all points that aren't double fronts - j = (j+1)%numHullPoints; - for ( k = 0 ; k < numHullPoints ; k++ ) { - if ( hullSide[ (j+k) % numHullPoints ] && hullSide[ (j+k+1) % numHullPoints ] ) { - continue; - } - copy = hullPoints[ (j+k+1) % numHullPoints ]; - VectorCopy( copy, newHullPoints[numNew] ); - numNew++; - } - - numHullPoints = numNew; - Com_Memcpy( hullPoints, newHullPoints, numHullPoints * sizeof(vec3_t) ); - } - - FreeWinding( *hull ); - w = AllocWinding( numHullPoints ); - w->numpoints = numHullPoints; - *hull = w; - Com_Memcpy( w->p, hullPoints, numHullPoints * sizeof(vec3_t) ); -} - - diff --git a/tools/quake3/bspc/deps/qcommon/cm_polylib.h b/tools/quake3/bspc/deps/qcommon/cm_polylib.h deleted file mode 100644 index 66d58d9e..00000000 --- a/tools/quake3/bspc/deps/qcommon/cm_polylib.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -// this is only used for visualization tools in cm_ debug functions - -typedef struct -{ - int numpoints; - vec3_t p[4]; // variable sized -} winding_t; - -#define MAX_POINTS_ON_WINDING 64 - -#define SIDE_FRONT 0 -#define SIDE_BACK 1 -#define SIDE_ON 2 -#define SIDE_CROSS 3 - -#define CLIP_EPSILON 0.1f - -#define MAX_MAP_BOUNDS 65535 - -// you can define on_epsilon in the makefile as tighter -#ifndef ON_EPSILON -#define ON_EPSILON 0.1f -#endif - -winding_t *AllocWinding (int points); -vec_t WindingArea (winding_t *w); -void WindingCenter (winding_t *w, vec3_t center); -void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, - vec_t epsilon, winding_t **front, winding_t **back); -winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist); -winding_t *CopyWinding (winding_t *w); -winding_t *ReverseWinding (winding_t *w); -winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist); -void CheckWinding (winding_t *w); -void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist); -void RemoveColinearPoints (winding_t *w); -int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist); -void FreeWinding (winding_t *w); -void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs); - -void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ); - -void ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon); -// frees the original if clipped - -void pw(winding_t *w); diff --git a/tools/quake3/bspc/deps/qcommon/cm_public.h b/tools/quake3/bspc/deps/qcommon/cm_public.h deleted file mode 100644 index f59fb291..00000000 --- a/tools/quake3/bspc/deps/qcommon/cm_public.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qfiles.h" - - -void CM_LoadMap( const char *name, qboolean clientload, int *checksum); -void CM_ClearMap( void ); -clipHandle_t CM_InlineModel( int index ); // 0 = world, 1 + are bmodels -clipHandle_t CM_TempBoxModel( const vec3_t mins, const vec3_t maxs, int capsule ); - -void CM_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ); - -int CM_NumClusters (void); -int CM_NumInlineModels( void ); -char *CM_EntityString (void); - -// returns an ORed contents mask -int CM_PointContents( const vec3_t p, clipHandle_t model ); -int CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ); - -void CM_BoxTrace ( trace_t *results, const vec3_t start, const vec3_t end, - vec3_t mins, vec3_t maxs, - clipHandle_t model, int brushmask, int capsule ); -void CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end, - vec3_t mins, vec3_t maxs, - clipHandle_t model, int brushmask, - const vec3_t origin, const vec3_t angles, int capsule ); - -byte *CM_ClusterPVS (int cluster); - -int CM_PointLeafnum( const vec3_t p ); - -// only returns non-solid leafs -// overflow if return listsize and if *lastLeaf != list[listsize-1] -int CM_BoxLeafnums( const vec3_t mins, const vec3_t maxs, int *list, - int listsize, int *lastLeaf ); - -int CM_LeafCluster (int leafnum); -int CM_LeafArea (int leafnum); - -void CM_AdjustAreaPortalState( int area1, int area2, qboolean open ); -qboolean CM_AreasConnected( int area1, int area2 ); - -int CM_WriteAreaBits( byte *buffer, int area ); - -// cm_tag.c -int CM_LerpTag( orientation_t *tag, clipHandle_t model, int startFrame, int endFrame, - float frac, const char *tagName ); - - -// cm_marks.c -int CM_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, - int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); - -// cm_patch.c -void CM_DrawDebugSurface( void (*drawPoly)(int color, int numPoints, float *points) ); diff --git a/tools/quake3/bspc/deps/qcommon/cm_test.c b/tools/quake3/bspc/deps/qcommon/cm_test.c deleted file mode 100644 index 10ba3298..00000000 --- a/tools/quake3/bspc/deps/qcommon/cm_test.c +++ /dev/null @@ -1,478 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "cm_local.h" - - -/* -================== -CM_PointLeafnum_r - -================== -*/ -int CM_PointLeafnum_r( const vec3_t p, int num ) { - float d; - cNode_t *node; - cplane_t *plane; - - while (num >= 0) - { - node = cm.nodes + num; - plane = node->plane; - - if (plane->type < 3) - d = p[plane->type] - plane->dist; - else - d = DotProduct (plane->normal, p) - plane->dist; - if (d < 0) - num = node->children[1]; - else - num = node->children[0]; - } - - c_pointcontents++; // optimize counter - - return -1 - num; -} - -int CM_PointLeafnum( const vec3_t p ) { - if ( !cm.numNodes ) { // map not loaded - return 0; - } - return CM_PointLeafnum_r (p, 0); -} - - -/* -====================================================================== - -LEAF LISTING - -====================================================================== -*/ - - -void CM_StoreLeafs( leafList_t *ll, int nodenum ) { - int leafNum; - - leafNum = -1 - nodenum; - - // store the lastLeaf even if the list is overflowed - if ( cm.leafs[ leafNum ].cluster != -1 ) { - ll->lastLeaf = leafNum; - } - - if ( ll->count >= ll->maxcount) { - ll->overflowed = qtrue; - return; - } - ll->list[ ll->count++ ] = leafNum; -} - -void CM_StoreBrushes( leafList_t *ll, int nodenum ) { - int i, k; - int leafnum; - int brushnum; - cLeaf_t *leaf; - cbrush_t *b; - - leafnum = -1 - nodenum; - - leaf = &cm.leafs[leafnum]; - - for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) { - brushnum = cm.leafbrushes[leaf->firstLeafBrush+k]; - b = &cm.brushes[brushnum]; - if ( b->checkcount == cm.checkcount ) { - continue; // already checked this brush in another leaf - } - b->checkcount = cm.checkcount; - for ( i = 0 ; i < 3 ; i++ ) { - if ( b->bounds[0][i] >= ll->bounds[1][i] || b->bounds[1][i] <= ll->bounds[0][i] ) { - break; - } - } - if ( i != 3 ) { - continue; - } - if ( ll->count >= ll->maxcount) { - ll->overflowed = qtrue; - return; - } - ((cbrush_t **)ll->list)[ ll->count++ ] = b; - } -#if 0 - // store patches? - for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) { - patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstleafsurface + k ] ]; - if ( !patch ) { - continue; - } - } -#endif -} - -/* -============= -CM_BoxLeafnums - -Fills in a list of all the leafs touched -============= -*/ -void CM_BoxLeafnums_r( leafList_t *ll, int nodenum ) { - cplane_t *plane; - cNode_t *node; - int s; - - while (1) { - if (nodenum < 0) { - ll->storeLeafs( ll, nodenum ); - return; - } - - node = &cm.nodes[nodenum]; - plane = node->plane; - s = BoxOnPlaneSide( ll->bounds[0], ll->bounds[1], plane ); - if (s == 1) { - nodenum = node->children[0]; - } else if (s == 2) { - nodenum = node->children[1]; - } else { - // go down both - CM_BoxLeafnums_r( ll, node->children[0] ); - nodenum = node->children[1]; - } - - } -} - -/* -================== -CM_BoxLeafnums -================== -*/ -int CM_BoxLeafnums( const vec3_t mins, const vec3_t maxs, int *list, int listsize, int *lastLeaf) { - leafList_t ll; - - cm.checkcount++; - - VectorCopy( mins, ll.bounds[0] ); - VectorCopy( maxs, ll.bounds[1] ); - ll.count = 0; - ll.maxcount = listsize; - ll.list = list; - ll.storeLeafs = CM_StoreLeafs; - ll.lastLeaf = 0; - ll.overflowed = qfalse; - - CM_BoxLeafnums_r( &ll, 0 ); - - *lastLeaf = ll.lastLeaf; - return ll.count; -} - -/* -================== -CM_BoxBrushes -================== -*/ -int CM_BoxBrushes( const vec3_t mins, const vec3_t maxs, cbrush_t **list, int listsize ) { - leafList_t ll; - - cm.checkcount++; - - VectorCopy( mins, ll.bounds[0] ); - VectorCopy( maxs, ll.bounds[1] ); - ll.count = 0; - ll.maxcount = listsize; - ll.list = (void *)list; - ll.storeLeafs = CM_StoreBrushes; - ll.lastLeaf = 0; - ll.overflowed = qfalse; - - CM_BoxLeafnums_r( &ll, 0 ); - - return ll.count; -} - - -//==================================================================== - - -/* -================== -CM_PointContents - -================== -*/ -int CM_PointContents( const vec3_t p, clipHandle_t model ) { - int leafnum; - int i, k; - int brushnum; - cLeaf_t *leaf; - cbrush_t *b; - int contents; - float d; - cmodel_t *clipm; - - if (!cm.numNodes) { // map not loaded - return 0; - } - - if ( model ) { - clipm = CM_ClipHandleToModel( model ); - leaf = &clipm->leaf; - } else { - leafnum = CM_PointLeafnum_r (p, 0); - leaf = &cm.leafs[leafnum]; - } - - contents = 0; - for (k=0 ; knumLeafBrushes ; k++) { - brushnum = cm.leafbrushes[leaf->firstLeafBrush+k]; - b = &cm.brushes[brushnum]; - - // see if the point is in the brush - for ( i = 0 ; i < b->numsides ; i++ ) { - d = DotProduct( p, b->sides[i].plane->normal ); -// FIXME test for Cash -// if ( d >= b->sides[i].plane->dist ) { - if ( d > b->sides[i].plane->dist ) { - break; - } - } - - if ( i == b->numsides ) { - contents |= b->contents; - } - } - - return contents; -} - -/* -================== -CM_TransformedPointContents - -Handles offseting and rotation of the end points for moving and -rotating entities -================== -*/ -int CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles) { - vec3_t p_l; - vec3_t temp; - vec3_t forward, right, up; - - // subtract origin offset - VectorSubtract (p, origin, p_l); - - // rotate start and end into the models frame of reference - if ( model != BOX_MODEL_HANDLE && - (angles[0] || angles[1] || angles[2]) ) - { - AngleVectors (angles, forward, right, up); - - VectorCopy (p_l, temp); - p_l[0] = DotProduct (temp, forward); - p_l[1] = -DotProduct (temp, right); - p_l[2] = DotProduct (temp, up); - } - - return CM_PointContents( p_l, model ); -} - - - -/* -=============================================================================== - -PVS - -=============================================================================== -*/ - -byte *CM_ClusterPVS (int cluster) { - if (cluster < 0 || cluster >= cm.numClusters || !cm.vised ) { - return cm.visibility; - } - - return cm.visibility + cluster * cm.clusterBytes; -} - - - -/* -=============================================================================== - -AREAPORTALS - -=============================================================================== -*/ - -void CM_FloodArea_r( int areaNum, int floodnum) { - int i; - cArea_t *area; - int *con; - - area = &cm.areas[ areaNum ]; - - if ( area->floodvalid == cm.floodvalid ) { - if (area->floodnum == floodnum) - return; - Com_Error (ERR_DROP, "FloodArea_r: reflooded"); - } - - area->floodnum = floodnum; - area->floodvalid = cm.floodvalid; - con = cm.areaPortals + areaNum * cm.numAreas; - for ( i=0 ; i < cm.numAreas ; i++ ) { - if ( con[i] > 0 ) { - CM_FloodArea_r( i, floodnum ); - } - } -} - -/* -==================== -CM_FloodAreaConnections - -==================== -*/ -void CM_FloodAreaConnections( void ) { - int i; - cArea_t *area; - int floodnum; - - // all current floods are now invalid - cm.floodvalid++; - floodnum = 0; - - for (i = 0 ; i < cm.numAreas ; i++) { - area = &cm.areas[i]; - if (area->floodvalid == cm.floodvalid) { - continue; // already flooded into - } - floodnum++; - CM_FloodArea_r (i, floodnum); - } - -} - -/* -==================== -CM_AdjustAreaPortalState - -==================== -*/ -void CM_AdjustAreaPortalState( int area1, int area2, qboolean open ) { - if ( area1 < 0 || area2 < 0 ) { - return; - } - - if ( area1 >= cm.numAreas || area2 >= cm.numAreas ) { - Com_Error (ERR_DROP, "CM_ChangeAreaPortalState: bad area number"); - } - - if ( open ) { - cm.areaPortals[ area1 * cm.numAreas + area2 ]++; - cm.areaPortals[ area2 * cm.numAreas + area1 ]++; - } else { - cm.areaPortals[ area1 * cm.numAreas + area2 ]--; - cm.areaPortals[ area2 * cm.numAreas + area1 ]--; - if ( cm.areaPortals[ area2 * cm.numAreas + area1 ] < 0 ) { - Com_Error (ERR_DROP, "CM_AdjustAreaPortalState: negative reference count"); - } - } - - CM_FloodAreaConnections (); -} - -/* -==================== -CM_AreasConnected - -==================== -*/ -qboolean CM_AreasConnected( int area1, int area2 ) { -#ifndef BSPC - if ( cm_noAreas->integer ) { - return qtrue; - } -#endif - - if ( area1 < 0 || area2 < 0 ) { - return qfalse; - } - - if (area1 >= cm.numAreas || area2 >= cm.numAreas) { - Com_Error (ERR_DROP, "area >= cm.numAreas"); - } - - if (cm.areas[area1].floodnum == cm.areas[area2].floodnum) { - return qtrue; - } - return qfalse; -} - - -/* -================= -CM_WriteAreaBits - -Writes a bit vector of all the areas -that are in the same flood as the area parameter -Returns the number of bytes needed to hold all the bits. - -The bits are OR'd in, so you can CM_WriteAreaBits from multiple -viewpoints and get the union of all visible areas. - -This is used to cull non-visible entities from snapshots -================= -*/ -int CM_WriteAreaBits (byte *buffer, int area) -{ - int i; - int floodnum; - int bytes; - - bytes = (cm.numAreas+7)>>3; - -#ifndef BSPC - if (cm_noAreas->integer || area == -1) -#else - if ( area == -1) -#endif - { // for debugging, send everything - Com_Memset (buffer, 255, bytes); - } - else - { - floodnum = cm.areas[area].floodnum; - for (i=0 ; i>3] |= 1<<(i&7); - } - } - - return bytes; -} - diff --git a/tools/quake3/bspc/deps/qcommon/cm_trace.c b/tools/quake3/bspc/deps/qcommon/cm_trace.c deleted file mode 100644 index 6eb99313..00000000 --- a/tools/quake3/bspc/deps/qcommon/cm_trace.c +++ /dev/null @@ -1,1471 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "cm_local.h" - -// always use bbox vs. bbox collision and never capsule vs. bbox or vice versa -//#define ALWAYS_BBOX_VS_BBOX -// always use capsule vs. capsule collision and never capsule vs. bbox or vice versa -//#define ALWAYS_CAPSULE_VS_CAPSULE - -//#define CAPSULE_DEBUG - -/* -=============================================================================== - -BASIC MATH - -=============================================================================== -*/ - -/* -================ -RotatePoint -================ -*/ -void RotatePoint(vec3_t point, /*const*/ vec3_t matrix[3]) { // bk: FIXME - vec3_t tvec; - - VectorCopy(point, tvec); - point[0] = DotProduct(matrix[0], tvec); - point[1] = DotProduct(matrix[1], tvec); - point[2] = DotProduct(matrix[2], tvec); -} - -/* -================ -TransposeMatrix -================ -*/ -void TransposeMatrix(/*const*/ vec3_t matrix[3], vec3_t transpose[3]) { // bk: FIXME - int i, j; - for (i = 0; i < 3; i++) { - for (j = 0; j < 3; j++) { - transpose[i][j] = matrix[j][i]; - } - } -} - -/* -================ -CreateRotationMatrix -================ -*/ -void CreateRotationMatrix(const vec3_t angles, vec3_t matrix[3]) { - AngleVectors(angles, matrix[0], matrix[1], matrix[2]); - VectorInverse(matrix[1]); -} - -/* -================ -CM_ProjectPointOntoVector -================ -*/ -void CM_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vDir, vec3_t vProj ) -{ - vec3_t pVec; - - VectorSubtract( point, vStart, pVec ); - // project onto the directional vector for this segment - VectorMA( vStart, DotProduct( pVec, vDir ), vDir, vProj ); -} - -/* -================ -CM_DistanceFromLineSquared -================ -*/ -float CM_DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2, vec3_t dir) { - vec3_t proj, t; - int j; - - CM_ProjectPointOntoVector(p, lp1, dir, proj); - for (j = 0; j < 3; j++) - if ((proj[j] > lp1[j] && proj[j] > lp2[j]) || - (proj[j] < lp1[j] && proj[j] < lp2[j])) - break; - if (j < 3) { - if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j])) - VectorSubtract(p, lp1, t); - else - VectorSubtract(p, lp2, t); - return VectorLengthSquared(t); - } - VectorSubtract(p, proj, t); - return VectorLengthSquared(t); -} - -/* -================ -CM_VectorDistanceSquared -================ -*/ -float CM_VectorDistanceSquared(vec3_t p1, vec3_t p2) { - vec3_t dir; - - VectorSubtract(p2, p1, dir); - return VectorLengthSquared(dir); -} - -/* -================ -SquareRootFloat -================ -*/ -float SquareRootFloat(float number) { - long i; - float x, y; - const float f = 1.5F; - - x = number * 0.5F; - y = number; - i = * ( long * ) &y; - i = 0x5f3759df - ( i >> 1 ); - y = * ( float * ) &i; - y = y * ( f - ( x * y * y ) ); - y = y * ( f - ( x * y * y ) ); - return number * y; -} - - -/* -=============================================================================== - -POSITION TESTING - -=============================================================================== -*/ - -/* -================ -CM_TestBoxInBrush -================ -*/ -void CM_TestBoxInBrush( traceWork_t *tw, cbrush_t *brush ) { - int i; - cplane_t *plane; - float dist; - float d1; - cbrushside_t *side; - float t; - vec3_t startp; - - if (!brush->numsides) { - return; - } - - // special test for axial - if ( tw->bounds[0][0] > brush->bounds[1][0] - || tw->bounds[0][1] > brush->bounds[1][1] - || tw->bounds[0][2] > brush->bounds[1][2] - || tw->bounds[1][0] < brush->bounds[0][0] - || tw->bounds[1][1] < brush->bounds[0][1] - || tw->bounds[1][2] < brush->bounds[0][2] - ) { - return; - } - - if ( tw->sphere.use ) { - // the first six planes are the axial planes, so we only - // need to test the remainder - for ( i = 6 ; i < brush->numsides ; i++ ) { - side = brush->sides + i; - plane = side->plane; - - // adjust the plane distance apropriately for radius - dist = plane->dist + tw->sphere.radius; - // find the closest point on the capsule to the plane - t = DotProduct( plane->normal, tw->sphere.offset ); - if ( t > 0 ) - { - VectorSubtract( tw->start, tw->sphere.offset, startp ); - } - else - { - VectorAdd( tw->start, tw->sphere.offset, startp ); - } - d1 = DotProduct( startp, plane->normal ) - dist; - // if completely in front of face, no intersection - if ( d1 > 0 ) { - return; - } - } - } else { - // the first six planes are the axial planes, so we only - // need to test the remainder - for ( i = 6 ; i < brush->numsides ; i++ ) { - side = brush->sides + i; - plane = side->plane; - - // adjust the plane distance apropriately for mins/maxs - dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal ); - - d1 = DotProduct( tw->start, plane->normal ) - dist; - - // if completely in front of face, no intersection - if ( d1 > 0 ) { - return; - } - } - } - - // inside this brush - tw->trace.startsolid = tw->trace.allsolid = qtrue; - tw->trace.fraction = 0; - tw->trace.contents = brush->contents; -} - - - -/* -================ -CM_TestInLeaf -================ -*/ -void CM_TestInLeaf( traceWork_t *tw, cLeaf_t *leaf ) { - int k; - int brushnum; - cbrush_t *b; - cPatch_t *patch; - - // test box position against all brushes in the leaf - for (k=0 ; knumLeafBrushes ; k++) { - brushnum = cm.leafbrushes[leaf->firstLeafBrush+k]; - b = &cm.brushes[brushnum]; - if (b->checkcount == cm.checkcount) { - continue; // already checked this brush in another leaf - } - b->checkcount = cm.checkcount; - - if ( !(b->contents & tw->contents)) { - continue; - } - - CM_TestBoxInBrush( tw, b ); - if ( tw->trace.allsolid ) { - return; - } - } - - // test against all patches -#ifdef BSPC - if (1) { -#else - if ( !cm_noCurves->integer ) { -#endif //BSPC - for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) { - patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstLeafSurface + k ] ]; - if ( !patch ) { - continue; - } - if ( patch->checkcount == cm.checkcount ) { - continue; // already checked this brush in another leaf - } - patch->checkcount = cm.checkcount; - - if ( !(patch->contents & tw->contents)) { - continue; - } - - if ( CM_PositionTestInPatchCollide( tw, patch->pc ) ) { - tw->trace.startsolid = tw->trace.allsolid = qtrue; - tw->trace.fraction = 0; - tw->trace.contents = patch->contents; - return; - } - } - } -} - -/* -================== -CM_TestCapsuleInCapsule - -capsule inside capsule check -================== -*/ -void CM_TestCapsuleInCapsule( traceWork_t *tw, clipHandle_t model ) { - int i; - vec3_t mins, maxs; - vec3_t top, bottom; - vec3_t p1, p2, tmp; - vec3_t offset, symetricSize[2]; - float radius, halfwidth, halfheight, offs, r; - - CM_ModelBounds(model, mins, maxs); - - VectorAdd(tw->start, tw->sphere.offset, top); - VectorSubtract(tw->start, tw->sphere.offset, bottom); - for ( i = 0 ; i < 3 ; i++ ) { - offset[i] = ( mins[i] + maxs[i] ) * 0.5; - symetricSize[0][i] = mins[i] - offset[i]; - symetricSize[1][i] = maxs[i] - offset[i]; - } - halfwidth = symetricSize[ 1 ][ 0 ]; - halfheight = symetricSize[ 1 ][ 2 ]; - radius = ( halfwidth > halfheight ) ? halfheight : halfwidth; - offs = halfheight - radius; - - r = Square(tw->sphere.radius + radius); - // check if any of the spheres overlap - VectorCopy(offset, p1); - p1[2] += offs; - VectorSubtract(p1, top, tmp); - if ( VectorLengthSquared(tmp) < r ) { - tw->trace.startsolid = tw->trace.allsolid = qtrue; - tw->trace.fraction = 0; - } - VectorSubtract(p1, bottom, tmp); - if ( VectorLengthSquared(tmp) < r ) { - tw->trace.startsolid = tw->trace.allsolid = qtrue; - tw->trace.fraction = 0; - } - VectorCopy(offset, p2); - p2[2] -= offs; - VectorSubtract(p2, top, tmp); - if ( VectorLengthSquared(tmp) < r ) { - tw->trace.startsolid = tw->trace.allsolid = qtrue; - tw->trace.fraction = 0; - } - VectorSubtract(p2, bottom, tmp); - if ( VectorLengthSquared(tmp) < r ) { - tw->trace.startsolid = tw->trace.allsolid = qtrue; - tw->trace.fraction = 0; - } - // if between cylinder up and lower bounds - if ( (top[2] >= p1[2] && top[2] <= p2[2]) || - (bottom[2] >= p1[2] && bottom[2] <= p2[2]) ) { - // 2d coordinates - top[2] = p1[2] = 0; - // if the cylinders overlap - VectorSubtract(top, p1, tmp); - if ( VectorLengthSquared(tmp) < r ) { - tw->trace.startsolid = tw->trace.allsolid = qtrue; - tw->trace.fraction = 0; - } - } -} - -/* -================== -CM_TestBoundingBoxInCapsule - -bounding box inside capsule check -================== -*/ -void CM_TestBoundingBoxInCapsule( traceWork_t *tw, clipHandle_t model ) { - vec3_t mins, maxs, offset, size[2]; - clipHandle_t h; - cmodel_t *cmod; - int i; - - // mins maxs of the capsule - CM_ModelBounds(model, mins, maxs); - - // offset for capsule center - for ( i = 0 ; i < 3 ; i++ ) { - offset[i] = ( mins[i] + maxs[i] ) * 0.5; - size[0][i] = mins[i] - offset[i]; - size[1][i] = maxs[i] - offset[i]; - tw->start[i] -= offset[i]; - tw->end[i] -= offset[i]; - } - - // replace the bounding box with the capsule - tw->sphere.use = qtrue; - tw->sphere.radius = ( size[1][0] > size[1][2] ) ? size[1][2]: size[1][0]; - tw->sphere.halfheight = size[1][2]; - VectorSet( tw->sphere.offset, 0, 0, size[1][2] - tw->sphere.radius ); - - // replace the capsule with the bounding box - h = CM_TempBoxModel(tw->size[0], tw->size[1], qfalse); - // calculate collision - cmod = CM_ClipHandleToModel( h ); - CM_TestInLeaf( tw, &cmod->leaf ); -} - -/* -================== -CM_PositionTest -================== -*/ -#define MAX_POSITION_LEAFS 1024 -void CM_PositionTest( traceWork_t *tw ) { - int leafs[MAX_POSITION_LEAFS]; - int i; - leafList_t ll; - - // identify the leafs we are touching - VectorAdd( tw->start, tw->size[0], ll.bounds[0] ); - VectorAdd( tw->start, tw->size[1], ll.bounds[1] ); - - for (i=0 ; i<3 ; i++) { - ll.bounds[0][i] -= 1; - ll.bounds[1][i] += 1; - } - - ll.count = 0; - ll.maxcount = MAX_POSITION_LEAFS; - ll.list = leafs; - ll.storeLeafs = CM_StoreLeafs; - ll.lastLeaf = 0; - ll.overflowed = qfalse; - - cm.checkcount++; - - CM_BoxLeafnums_r( &ll, 0 ); - - - cm.checkcount++; - - // test the contents of the leafs - for (i=0 ; i < ll.count ; i++) { - CM_TestInLeaf( tw, &cm.leafs[leafs[i]] ); - if ( tw->trace.allsolid ) { - break; - } - } -} - -/* -=============================================================================== - -TRACING - -=============================================================================== -*/ - - -/* -================ -CM_TraceThroughPatch -================ -*/ - -void CM_TraceThroughPatch( traceWork_t *tw, cPatch_t *patch ) { - float oldFrac; - - c_patch_traces++; - - oldFrac = tw->trace.fraction; - - CM_TraceThroughPatchCollide( tw, patch->pc ); - - if ( tw->trace.fraction < oldFrac ) { - tw->trace.surfaceFlags = patch->surfaceFlags; - tw->trace.contents = patch->contents; - } -} - -/* -================ -CM_TraceThroughBrush -================ -*/ -void CM_TraceThroughBrush( traceWork_t *tw, cbrush_t *brush ) { - int i; - cplane_t *plane, *clipplane; - float dist; - float enterFrac, leaveFrac; - float d1, d2; - qboolean getout, startout; - float f; - cbrushside_t *side, *leadside; - float t; - vec3_t startp; - vec3_t endp; - - enterFrac = -1.0; - leaveFrac = 1.0; - clipplane = NULL; - - if ( !brush->numsides ) { - return; - } - - c_brush_traces++; - - getout = qfalse; - startout = qfalse; - - leadside = NULL; - - if ( tw->sphere.use ) { - // - // compare the trace against all planes of the brush - // find the latest time the trace crosses a plane towards the interior - // and the earliest time the trace crosses a plane towards the exterior - // - for (i = 0; i < brush->numsides; i++) { - side = brush->sides + i; - plane = side->plane; - - // adjust the plane distance apropriately for radius - dist = plane->dist + tw->sphere.radius; - - // find the closest point on the capsule to the plane - t = DotProduct( plane->normal, tw->sphere.offset ); - if ( t > 0 ) - { - VectorSubtract( tw->start, tw->sphere.offset, startp ); - VectorSubtract( tw->end, tw->sphere.offset, endp ); - } - else - { - VectorAdd( tw->start, tw->sphere.offset, startp ); - VectorAdd( tw->end, tw->sphere.offset, endp ); - } - - d1 = DotProduct( startp, plane->normal ) - dist; - d2 = DotProduct( endp, plane->normal ) - dist; - - if (d2 > 0) { - getout = qtrue; // endpoint is not in solid - } - if (d1 > 0) { - startout = qtrue; - } - - // if completely in front of face, no intersection with the entire brush - if (d1 > 0 && ( d2 >= SURFACE_CLIP_EPSILON || d2 >= d1 ) ) { - return; - } - - // if it doesn't cross the plane, the plane isn't relevent - if (d1 <= 0 && d2 <= 0 ) { - continue; - } - - // crosses face - if (d1 > d2) { // enter - f = (d1-SURFACE_CLIP_EPSILON) / (d1-d2); - if ( f < 0 ) { - f = 0; - } - if (f > enterFrac) { - enterFrac = f; - clipplane = plane; - leadside = side; - } - } else { // leave - f = (d1+SURFACE_CLIP_EPSILON) / (d1-d2); - if ( f > 1 ) { - f = 1; - } - if (f < leaveFrac) { - leaveFrac = f; - } - } - } - } else { - // - // compare the trace against all planes of the brush - // find the latest time the trace crosses a plane towards the interior - // and the earliest time the trace crosses a plane towards the exterior - // - for (i = 0; i < brush->numsides; i++) { - side = brush->sides + i; - plane = side->plane; - - // adjust the plane distance apropriately for mins/maxs - dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal ); - - d1 = DotProduct( tw->start, plane->normal ) - dist; - d2 = DotProduct( tw->end, plane->normal ) - dist; - - if (d2 > 0) { - getout = qtrue; // endpoint is not in solid - } - if (d1 > 0) { - startout = qtrue; - } - - // if completely in front of face, no intersection with the entire brush - if (d1 > 0 && ( d2 >= SURFACE_CLIP_EPSILON || d2 >= d1 ) ) { - return; - } - - // if it doesn't cross the plane, the plane isn't relevent - if (d1 <= 0 && d2 <= 0 ) { - continue; - } - - // crosses face - if (d1 > d2) { // enter - f = (d1-SURFACE_CLIP_EPSILON) / (d1-d2); - if ( f < 0 ) { - f = 0; - } - if (f > enterFrac) { - enterFrac = f; - clipplane = plane; - leadside = side; - } - } else { // leave - f = (d1+SURFACE_CLIP_EPSILON) / (d1-d2); - if ( f > 1 ) { - f = 1; - } - if (f < leaveFrac) { - leaveFrac = f; - } - } - } - } - - // - // all planes have been checked, and the trace was not - // completely outside the brush - // - if (!startout) { // original point was inside brush - tw->trace.startsolid = qtrue; - if (!getout) { - tw->trace.allsolid = qtrue; - tw->trace.fraction = 0; - tw->trace.contents = brush->contents; - } - return; - } - - if (enterFrac < leaveFrac) { - if (enterFrac > -1 && enterFrac < tw->trace.fraction) { - if (enterFrac < 0) { - enterFrac = 0; - } - tw->trace.fraction = enterFrac; - tw->trace.plane = *clipplane; - tw->trace.surfaceFlags = leadside->surfaceFlags; - tw->trace.contents = brush->contents; - } - } -} - -/* -================ -CM_TraceThroughLeaf -================ -*/ -void CM_TraceThroughLeaf( traceWork_t *tw, cLeaf_t *leaf ) { - int k; - int brushnum; - cbrush_t *b; - cPatch_t *patch; - - // trace line against all brushes in the leaf - for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) { - brushnum = cm.leafbrushes[leaf->firstLeafBrush+k]; - - b = &cm.brushes[brushnum]; - if ( b->checkcount == cm.checkcount ) { - continue; // already checked this brush in another leaf - } - b->checkcount = cm.checkcount; - - if ( !(b->contents & tw->contents) ) { - continue; - } - - CM_TraceThroughBrush( tw, b ); - if ( !tw->trace.fraction ) { - return; - } - } - - // trace line against all patches in the leaf -#ifdef BSPC - if (1) { -#else - if ( !cm_noCurves->integer ) { -#endif - for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) { - patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstLeafSurface + k ] ]; - if ( !patch ) { - continue; - } - if ( patch->checkcount == cm.checkcount ) { - continue; // already checked this patch in another leaf - } - patch->checkcount = cm.checkcount; - - if ( !(patch->contents & tw->contents) ) { - continue; - } - - CM_TraceThroughPatch( tw, patch ); - if ( !tw->trace.fraction ) { - return; - } - } - } -} - -#define RADIUS_EPSILON 1.0f - -/* -================ -CM_TraceThroughSphere - -get the first intersection of the ray with the sphere -================ -*/ -void CM_TraceThroughSphere( traceWork_t *tw, vec3_t origin, float radius, vec3_t start, vec3_t end ) { - float l1, l2, length, scale, fraction; - float a, b, c, d, sqrtd; - vec3_t v1, dir, intersection; - - // if inside the sphere - VectorSubtract(start, origin, dir); - l1 = VectorLengthSquared(dir); - if (l1 < Square(radius)) { - tw->trace.fraction = 0; - tw->trace.startsolid = qtrue; - // test for allsolid - VectorSubtract(end, origin, dir); - l1 = VectorLengthSquared(dir); - if (l1 < Square(radius)) { - tw->trace.allsolid = qtrue; - } - return; - } - // - VectorSubtract(end, start, dir); - length = VectorNormalize(dir); - // - l1 = CM_DistanceFromLineSquared(origin, start, end, dir); - VectorSubtract(end, origin, v1); - l2 = VectorLengthSquared(v1); - // if no intersection with the sphere and the end point is at least an epsilon away - if (l1 >= Square(radius) && l2 > Square(radius+SURFACE_CLIP_EPSILON)) { - return; - } - // - // | origin - (start + t * dir) | = radius - // a = dir[0]^2 + dir[1]^2 + dir[2]^2; - // b = 2 * (dir[0] * (start[0] - origin[0]) + dir[1] * (start[1] - origin[1]) + dir[2] * (start[2] - origin[2])); - // c = (start[0] - origin[0])^2 + (start[1] - origin[1])^2 + (start[2] - origin[2])^2 - radius^2; - // - VectorSubtract(start, origin, v1); - // dir is normalized so a = 1 - a = 1.0f;//dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]; - b = 2.0f * (dir[0] * v1[0] + dir[1] * v1[1] + dir[2] * v1[2]); - c = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] - (radius+RADIUS_EPSILON) * (radius+RADIUS_EPSILON); - - d = b * b - 4.0f * c;// * a; - if (d > 0) { - sqrtd = SquareRootFloat(d); - // = (- b + sqrtd) * 0.5f; // / (2.0f * a); - fraction = (- b - sqrtd) * 0.5f; // / (2.0f * a); - // - if (fraction < 0) { - fraction = 0; - } - else { - fraction /= length; - } - if ( fraction < tw->trace.fraction ) { - tw->trace.fraction = fraction; - VectorSubtract(end, start, dir); - VectorMA(start, fraction, dir, intersection); - VectorSubtract(intersection, origin, dir); - #ifdef CAPSULE_DEBUG - l2 = VectorLength(dir); - if (l2 < radius) { - int bah = 1; - } - #endif - scale = 1 / (radius+RADIUS_EPSILON); - VectorScale(dir, scale, dir); - VectorCopy(dir, tw->trace.plane.normal); - VectorAdd( tw->modelOrigin, intersection, intersection); - tw->trace.plane.dist = DotProduct(tw->trace.plane.normal, intersection); - tw->trace.contents = CONTENTS_BODY; - } - } - else if (d == 0) { - //t1 = (- b ) / 2; - // slide along the sphere - } - // no intersection at all -} - -/* -================ -CM_TraceThroughVerticalCylinder - -get the first intersection of the ray with the cylinder -the cylinder extends halfheight above and below the origin -================ -*/ -void CM_TraceThroughVerticalCylinder( traceWork_t *tw, vec3_t origin, float radius, float halfheight, vec3_t start, vec3_t end) { - float length, scale, fraction, l1, l2; - float a, b, c, d, sqrtd; - vec3_t v1, dir, start2d, end2d, org2d, intersection; - - // 2d coordinates - VectorSet(start2d, start[0], start[1], 0); - VectorSet(end2d, end[0], end[1], 0); - VectorSet(org2d, origin[0], origin[1], 0); - // if between lower and upper cylinder bounds - if (start[2] <= origin[2] + halfheight && - start[2] >= origin[2] - halfheight) { - // if inside the cylinder - VectorSubtract(start2d, org2d, dir); - l1 = VectorLengthSquared(dir); - if (l1 < Square(radius)) { - tw->trace.fraction = 0; - tw->trace.startsolid = qtrue; - VectorSubtract(end2d, org2d, dir); - l1 = VectorLengthSquared(dir); - if (l1 < Square(radius)) { - tw->trace.allsolid = qtrue; - } - return; - } - } - // - VectorSubtract(end2d, start2d, dir); - length = VectorNormalize(dir); - // - l1 = CM_DistanceFromLineSquared(org2d, start2d, end2d, dir); - VectorSubtract(end2d, org2d, v1); - l2 = VectorLengthSquared(v1); - // if no intersection with the cylinder and the end point is at least an epsilon away - if (l1 >= Square(radius) && l2 > Square(radius+SURFACE_CLIP_EPSILON)) { - return; - } - // - // - // (start[0] - origin[0] - t * dir[0]) ^ 2 + (start[1] - origin[1] - t * dir[1]) ^ 2 = radius ^ 2 - // (v1[0] + t * dir[0]) ^ 2 + (v1[1] + t * dir[1]) ^ 2 = radius ^ 2; - // v1[0] ^ 2 + 2 * v1[0] * t * dir[0] + (t * dir[0]) ^ 2 + - // v1[1] ^ 2 + 2 * v1[1] * t * dir[1] + (t * dir[1]) ^ 2 = radius ^ 2 - // t ^ 2 * (dir[0] ^ 2 + dir[1] ^ 2) + t * (2 * v1[0] * dir[0] + 2 * v1[1] * dir[1]) + - // v1[0] ^ 2 + v1[1] ^ 2 - radius ^ 2 = 0 - // - VectorSubtract(start, origin, v1); - // dir is normalized so we can use a = 1 - a = 1.0f;// * (dir[0] * dir[0] + dir[1] * dir[1]); - b = 2.0f * (v1[0] * dir[0] + v1[1] * dir[1]); - c = v1[0] * v1[0] + v1[1] * v1[1] - (radius+RADIUS_EPSILON) * (radius+RADIUS_EPSILON); - - d = b * b - 4.0f * c;// * a; - if (d > 0) { - sqrtd = SquareRootFloat(d); - // = (- b + sqrtd) * 0.5f;// / (2.0f * a); - fraction = (- b - sqrtd) * 0.5f;// / (2.0f * a); - // - if (fraction < 0) { - fraction = 0; - } - else { - fraction /= length; - } - if ( fraction < tw->trace.fraction ) { - VectorSubtract(end, start, dir); - VectorMA(start, fraction, dir, intersection); - // if the intersection is between the cylinder lower and upper bound - if (intersection[2] <= origin[2] + halfheight && - intersection[2] >= origin[2] - halfheight) { - // - tw->trace.fraction = fraction; - VectorSubtract(intersection, origin, dir); - dir[2] = 0; - #ifdef CAPSULE_DEBUG - l2 = VectorLength(dir); - if (l2 <= radius) { - int bah = 1; - } - #endif - scale = 1 / (radius+RADIUS_EPSILON); - VectorScale(dir, scale, dir); - VectorCopy(dir, tw->trace.plane.normal); - VectorAdd( tw->modelOrigin, intersection, intersection); - tw->trace.plane.dist = DotProduct(tw->trace.plane.normal, intersection); - tw->trace.contents = CONTENTS_BODY; - } - } - } - else if (d == 0) { - //t[0] = (- b ) / 2 * a; - // slide along the cylinder - } - // no intersection at all -} - -/* -================ -CM_TraceCapsuleThroughCapsule - -capsule vs. capsule collision (not rotated) -================ -*/ -void CM_TraceCapsuleThroughCapsule( traceWork_t *tw, clipHandle_t model ) { - int i; - vec3_t mins, maxs; - vec3_t top, bottom, starttop, startbottom, endtop, endbottom; - vec3_t offset, symetricSize[2]; - float radius, halfwidth, halfheight, offs, h; - - CM_ModelBounds(model, mins, maxs); - // test trace bounds vs. capsule bounds - if ( tw->bounds[0][0] > maxs[0] + RADIUS_EPSILON - || tw->bounds[0][1] > maxs[1] + RADIUS_EPSILON - || tw->bounds[0][2] > maxs[2] + RADIUS_EPSILON - || tw->bounds[1][0] < mins[0] - RADIUS_EPSILON - || tw->bounds[1][1] < mins[1] - RADIUS_EPSILON - || tw->bounds[1][2] < mins[2] - RADIUS_EPSILON - ) { - return; - } - // top origin and bottom origin of each sphere at start and end of trace - VectorAdd(tw->start, tw->sphere.offset, starttop); - VectorSubtract(tw->start, tw->sphere.offset, startbottom); - VectorAdd(tw->end, tw->sphere.offset, endtop); - VectorSubtract(tw->end, tw->sphere.offset, endbottom); - - // calculate top and bottom of the capsule spheres to collide with - for ( i = 0 ; i < 3 ; i++ ) { - offset[i] = ( mins[i] + maxs[i] ) * 0.5; - symetricSize[0][i] = mins[i] - offset[i]; - symetricSize[1][i] = maxs[i] - offset[i]; - } - halfwidth = symetricSize[ 1 ][ 0 ]; - halfheight = symetricSize[ 1 ][ 2 ]; - radius = ( halfwidth > halfheight ) ? halfheight : halfwidth; - offs = halfheight - radius; - VectorCopy(offset, top); - top[2] += offs; - VectorCopy(offset, bottom); - bottom[2] -= offs; - // expand radius of spheres - radius += tw->sphere.radius; - // if there is horizontal movement - if ( tw->start[0] != tw->end[0] || tw->start[1] != tw->end[1] ) { - // height of the expanded cylinder is the height of both cylinders minus the radius of both spheres - h = halfheight + tw->sphere.halfheight - radius; - // if the cylinder has a height - if ( h > 0 ) { - // test for collisions between the cylinders - CM_TraceThroughVerticalCylinder(tw, offset, radius, h, tw->start, tw->end); - } - } - // test for collision between the spheres - CM_TraceThroughSphere(tw, top, radius, startbottom, endbottom); - CM_TraceThroughSphere(tw, bottom, radius, starttop, endtop); -} - -/* -================ -CM_TraceBoundingBoxThroughCapsule - -bounding box vs. capsule collision -================ -*/ -void CM_TraceBoundingBoxThroughCapsule( traceWork_t *tw, clipHandle_t model ) { - vec3_t mins, maxs, offset, size[2]; - clipHandle_t h; - cmodel_t *cmod; - int i; - - // mins maxs of the capsule - CM_ModelBounds(model, mins, maxs); - - // offset for capsule center - for ( i = 0 ; i < 3 ; i++ ) { - offset[i] = ( mins[i] + maxs[i] ) * 0.5; - size[0][i] = mins[i] - offset[i]; - size[1][i] = maxs[i] - offset[i]; - tw->start[i] -= offset[i]; - tw->end[i] -= offset[i]; - } - - // replace the bounding box with the capsule - tw->sphere.use = qtrue; - tw->sphere.radius = ( size[1][0] > size[1][2] ) ? size[1][2]: size[1][0]; - tw->sphere.halfheight = size[1][2]; - VectorSet( tw->sphere.offset, 0, 0, size[1][2] - tw->sphere.radius ); - - // replace the capsule with the bounding box - h = CM_TempBoxModel(tw->size[0], tw->size[1], qfalse); - // calculate collision - cmod = CM_ClipHandleToModel( h ); - CM_TraceThroughLeaf( tw, &cmod->leaf ); -} - -//========================================================================================= - -/* -================== -CM_TraceThroughTree - -Traverse all the contacted leafs from the start to the end position. -If the trace is a point, they will be exactly in order, but for larger -trace volumes it is possible to hit something in a later leaf with -a smaller intercept fraction. -================== -*/ -void CM_TraceThroughTree( traceWork_t *tw, int num, float p1f, float p2f, vec3_t p1, vec3_t p2) { - cNode_t *node; - cplane_t *plane; - float t1, t2, offset; - float frac, frac2; - float idist; - vec3_t mid; - int side; - float midf; - - if (tw->trace.fraction <= p1f) { - return; // already hit something nearer - } - - // if < 0, we are in a leaf node - if (num < 0) { - CM_TraceThroughLeaf( tw, &cm.leafs[-1-num] ); - return; - } - - // - // find the point distances to the seperating plane - // and the offset for the size of the box - // - node = cm.nodes + num; - plane = node->plane; - - // adjust the plane distance apropriately for mins/maxs - if ( plane->type < 3 ) { - t1 = p1[plane->type] - plane->dist; - t2 = p2[plane->type] - plane->dist; - offset = tw->extents[plane->type]; - } else { - t1 = DotProduct (plane->normal, p1) - plane->dist; - t2 = DotProduct (plane->normal, p2) - plane->dist; - if ( tw->isPoint ) { - offset = 0; - } else { -#if 0 // bk010201 - DEAD - // an axial brush right behind a slanted bsp plane - // will poke through when expanded, so adjust - // by sqrt(3) - offset = fabs(tw->extents[0]*plane->normal[0]) + - fabs(tw->extents[1]*plane->normal[1]) + - fabs(tw->extents[2]*plane->normal[2]); - - offset *= 2; - offset = tw->maxOffset; -#endif - // this is silly - offset = 2048; - } - } - - // see which sides we need to consider - if ( t1 >= offset + 1 && t2 >= offset + 1 ) { - CM_TraceThroughTree( tw, node->children[0], p1f, p2f, p1, p2 ); - return; - } - if ( t1 < -offset - 1 && t2 < -offset - 1 ) { - CM_TraceThroughTree( tw, node->children[1], p1f, p2f, p1, p2 ); - return; - } - - // put the crosspoint SURFACE_CLIP_EPSILON pixels on the near side - if ( t1 < t2 ) { - idist = 1.0/(t1-t2); - side = 1; - frac2 = (t1 + offset + SURFACE_CLIP_EPSILON)*idist; - frac = (t1 - offset + SURFACE_CLIP_EPSILON)*idist; - } else if (t1 > t2) { - idist = 1.0/(t1-t2); - side = 0; - frac2 = (t1 - offset - SURFACE_CLIP_EPSILON)*idist; - frac = (t1 + offset + SURFACE_CLIP_EPSILON)*idist; - } else { - side = 0; - frac = 1; - frac2 = 0; - } - - // move up to the node - if ( frac < 0 ) { - frac = 0; - } - if ( frac > 1 ) { - frac = 1; - } - - midf = p1f + (p2f - p1f)*frac; - - mid[0] = p1[0] + frac*(p2[0] - p1[0]); - mid[1] = p1[1] + frac*(p2[1] - p1[1]); - mid[2] = p1[2] + frac*(p2[2] - p1[2]); - - CM_TraceThroughTree( tw, node->children[side], p1f, midf, p1, mid ); - - - // go past the node - if ( frac2 < 0 ) { - frac2 = 0; - } - if ( frac2 > 1 ) { - frac2 = 1; - } - - midf = p1f + (p2f - p1f)*frac2; - - mid[0] = p1[0] + frac2*(p2[0] - p1[0]); - mid[1] = p1[1] + frac2*(p2[1] - p1[1]); - mid[2] = p1[2] + frac2*(p2[2] - p1[2]); - - CM_TraceThroughTree( tw, node->children[side^1], midf, p2f, mid, p2 ); -} - - -//====================================================================== - - -/* -================== -CM_Trace -================== -*/ -void CM_Trace( trace_t *results, const vec3_t start, const vec3_t end, vec3_t mins, vec3_t maxs, - clipHandle_t model, const vec3_t origin, int brushmask, int capsule, sphere_t *sphere ) { - int i; - traceWork_t tw; - vec3_t offset; - cmodel_t *cmod; - - cmod = CM_ClipHandleToModel( model ); - - cm.checkcount++; // for multi-check avoidance - - c_traces++; // for statistics, may be zeroed - - // fill in a default trace - Com_Memset( &tw, 0, sizeof(tw) ); - tw.trace.fraction = 1; // assume it goes the entire distance until shown otherwise - VectorCopy(origin, tw.modelOrigin); - - if (!cm.numNodes) { - *results = tw.trace; - - return; // map not loaded, shouldn't happen - } - - // allow NULL to be passed in for 0,0,0 - if ( !mins ) { - mins = vec3_origin; - } - if ( !maxs ) { - maxs = vec3_origin; - } - - // set basic parms - tw.contents = brushmask; - - // adjust so that mins and maxs are always symetric, which - // avoids some complications with plane expanding of rotated - // bmodels - for ( i = 0 ; i < 3 ; i++ ) { - offset[i] = ( mins[i] + maxs[i] ) * 0.5; - tw.size[0][i] = mins[i] - offset[i]; - tw.size[1][i] = maxs[i] - offset[i]; - tw.start[i] = start[i] + offset[i]; - tw.end[i] = end[i] + offset[i]; - } - - // if a sphere is already specified - if ( sphere ) { - tw.sphere = *sphere; - } - else { - tw.sphere.use = capsule; - tw.sphere.radius = ( tw.size[1][0] > tw.size[1][2] ) ? tw.size[1][2]: tw.size[1][0]; - tw.sphere.halfheight = tw.size[1][2]; - VectorSet( tw.sphere.offset, 0, 0, tw.size[1][2] - tw.sphere.radius ); - } - - tw.maxOffset = tw.size[1][0] + tw.size[1][1] + tw.size[1][2]; - - // tw.offsets[signbits] = vector to apropriate corner from origin - tw.offsets[0][0] = tw.size[0][0]; - tw.offsets[0][1] = tw.size[0][1]; - tw.offsets[0][2] = tw.size[0][2]; - - tw.offsets[1][0] = tw.size[1][0]; - tw.offsets[1][1] = tw.size[0][1]; - tw.offsets[1][2] = tw.size[0][2]; - - tw.offsets[2][0] = tw.size[0][0]; - tw.offsets[2][1] = tw.size[1][1]; - tw.offsets[2][2] = tw.size[0][2]; - - tw.offsets[3][0] = tw.size[1][0]; - tw.offsets[3][1] = tw.size[1][1]; - tw.offsets[3][2] = tw.size[0][2]; - - tw.offsets[4][0] = tw.size[0][0]; - tw.offsets[4][1] = tw.size[0][1]; - tw.offsets[4][2] = tw.size[1][2]; - - tw.offsets[5][0] = tw.size[1][0]; - tw.offsets[5][1] = tw.size[0][1]; - tw.offsets[5][2] = tw.size[1][2]; - - tw.offsets[6][0] = tw.size[0][0]; - tw.offsets[6][1] = tw.size[1][1]; - tw.offsets[6][2] = tw.size[1][2]; - - tw.offsets[7][0] = tw.size[1][0]; - tw.offsets[7][1] = tw.size[1][1]; - tw.offsets[7][2] = tw.size[1][2]; - - // - // calculate bounds - // - if ( tw.sphere.use ) { - for ( i = 0 ; i < 3 ; i++ ) { - if ( tw.start[i] < tw.end[i] ) { - tw.bounds[0][i] = tw.start[i] - fabs(tw.sphere.offset[i]) - tw.sphere.radius; - tw.bounds[1][i] = tw.end[i] + fabs(tw.sphere.offset[i]) + tw.sphere.radius; - } else { - tw.bounds[0][i] = tw.end[i] - fabs(tw.sphere.offset[i]) - tw.sphere.radius; - tw.bounds[1][i] = tw.start[i] + fabs(tw.sphere.offset[i]) + tw.sphere.radius; - } - } - } - else { - for ( i = 0 ; i < 3 ; i++ ) { - if ( tw.start[i] < tw.end[i] ) { - tw.bounds[0][i] = tw.start[i] + tw.size[0][i]; - tw.bounds[1][i] = tw.end[i] + tw.size[1][i]; - } else { - tw.bounds[0][i] = tw.end[i] + tw.size[0][i]; - tw.bounds[1][i] = tw.start[i] + tw.size[1][i]; - } - } - } - - // - // check for position test special case - // - if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) { - if ( model ) { -#ifdef ALWAYS_BBOX_VS_BBOX // bk010201 - FIXME - compile time flag? - if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { - tw.sphere.use = qfalse; - CM_TestInLeaf( &tw, &cmod->leaf ); - } - else -#elif defined(ALWAYS_CAPSULE_VS_CAPSULE) - if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { - CM_TestCapsuleInCapsule( &tw, model ); - } - else -#endif - if ( model == CAPSULE_MODEL_HANDLE ) { - if ( tw.sphere.use ) { - CM_TestCapsuleInCapsule( &tw, model ); - } - else { - CM_TestBoundingBoxInCapsule( &tw, model ); - } - } - else { - CM_TestInLeaf( &tw, &cmod->leaf ); - } - } else { - CM_PositionTest( &tw ); - } - } else { - // - // check for point special case - // - if ( tw.size[0][0] == 0 && tw.size[0][1] == 0 && tw.size[0][2] == 0 ) { - tw.isPoint = qtrue; - VectorClear( tw.extents ); - } else { - tw.isPoint = qfalse; - tw.extents[0] = tw.size[1][0]; - tw.extents[1] = tw.size[1][1]; - tw.extents[2] = tw.size[1][2]; - } - - // - // general sweeping through world - // - if ( model ) { -#ifdef ALWAYS_BBOX_VS_BBOX - if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { - tw.sphere.use = qfalse; - CM_TraceThroughLeaf( &tw, &cmod->leaf ); - } - else -#elif defined(ALWAYS_CAPSULE_VS_CAPSULE) - if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { - CM_TraceCapsuleThroughCapsule( &tw, model ); - } - else -#endif - if ( model == CAPSULE_MODEL_HANDLE ) { - if ( tw.sphere.use ) { - CM_TraceCapsuleThroughCapsule( &tw, model ); - } - else { - CM_TraceBoundingBoxThroughCapsule( &tw, model ); - } - } - else { - CM_TraceThroughLeaf( &tw, &cmod->leaf ); - } - } else { - CM_TraceThroughTree( &tw, 0, 0, 1, tw.start, tw.end ); - } - } - - // generate endpos from the original, unmodified start/end - if ( tw.trace.fraction == 1 ) { - VectorCopy (end, tw.trace.endpos); - } else { - for ( i=0 ; i<3 ; i++ ) { - tw.trace.endpos[i] = start[i] + tw.trace.fraction * (end[i] - start[i]); - } - } - - // If allsolid is set (was entirely inside something solid), the plane is not valid. - // If fraction == 1.0, we never hit anything, and thus the plane is not valid. - // Otherwise, the normal on the plane should have unit length - assert(tw.trace.allsolid || - tw.trace.fraction == 1.0 || - VectorLengthSquared(tw.trace.plane.normal) > 0.9999); - *results = tw.trace; -} - -/* -================== -CM_BoxTrace -================== -*/ -void CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, - vec3_t mins, vec3_t maxs, - clipHandle_t model, int brushmask, int capsule ) { - CM_Trace( results, start, end, mins, maxs, model, vec3_origin, brushmask, capsule, NULL ); -} - -/* -================== -CM_TransformedBoxTrace - -Handles offseting and rotation of the end points for moving and -rotating entities -================== -*/ -void CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end, - vec3_t mins, vec3_t maxs, - clipHandle_t model, int brushmask, - const vec3_t origin, const vec3_t angles, int capsule ) { - trace_t trace; - vec3_t start_l, end_l; - qboolean rotated; - vec3_t offset; - vec3_t symetricSize[2]; - vec3_t matrix[3], transpose[3]; - int i; - float halfwidth; - float halfheight; - float t; - sphere_t sphere; - - if ( !mins ) { - mins = vec3_origin; - } - if ( !maxs ) { - maxs = vec3_origin; - } - - // adjust so that mins and maxs are always symetric, which - // avoids some complications with plane expanding of rotated - // bmodels - for ( i = 0 ; i < 3 ; i++ ) { - offset[i] = ( mins[i] + maxs[i] ) * 0.5; - symetricSize[0][i] = mins[i] - offset[i]; - symetricSize[1][i] = maxs[i] - offset[i]; - start_l[i] = start[i] + offset[i]; - end_l[i] = end[i] + offset[i]; - } - - // subtract origin offset - VectorSubtract( start_l, origin, start_l ); - VectorSubtract( end_l, origin, end_l ); - - // rotate start and end into the models frame of reference - if ( model != BOX_MODEL_HANDLE && - (angles[0] || angles[1] || angles[2]) ) { - rotated = qtrue; - } else { - rotated = qfalse; - } - - halfwidth = symetricSize[ 1 ][ 0 ]; - halfheight = symetricSize[ 1 ][ 2 ]; - - sphere.use = capsule; - sphere.radius = ( halfwidth > halfheight ) ? halfheight : halfwidth; - sphere.halfheight = halfheight; - t = halfheight - sphere.radius; - - if (rotated) { - // rotation on trace line (start-end) instead of rotating the bmodel - // NOTE: This is still incorrect for bounding boxes because the actual bounding - // box that is swept through the model is not rotated. We cannot rotate - // the bounding box or the bmodel because that would make all the brush - // bevels invalid. - // However this is correct for capsules since a capsule itself is rotated too. - CreateRotationMatrix(angles, matrix); - RotatePoint(start_l, matrix); - RotatePoint(end_l, matrix); - // rotated sphere offset for capsule - sphere.offset[0] = matrix[0][ 2 ] * t; - sphere.offset[1] = -matrix[1][ 2 ] * t; - sphere.offset[2] = matrix[2][ 2 ] * t; - } - else { - VectorSet( sphere.offset, 0, 0, t ); - } - - // sweep the box through the model - CM_Trace( &trace, start_l, end_l, symetricSize[0], symetricSize[1], model, origin, brushmask, capsule, &sphere ); - - // if the bmodel was rotated and there was a collision - if ( rotated && trace.fraction != 1.0 ) { - // rotation of bmodel collision plane - TransposeMatrix(matrix, transpose); - RotatePoint(trace.plane.normal, transpose); - } - - // re-calculate the end position of the trace because the trace.endpos - // calculated by CM_Trace could be rotated and have an offset - trace.endpos[0] = start[0] + trace.fraction * (end[0] - start[0]); - trace.endpos[1] = start[1] + trace.fraction * (end[1] - start[1]); - trace.endpos[2] = start[2] + trace.fraction * (end[2] - start[2]); - - *results = trace; -} diff --git a/tools/quake3/bspc/deps/qcommon/cmd.c b/tools/quake3/bspc/deps/qcommon/cmd.c deleted file mode 100644 index f24208b4..00000000 --- a/tools/quake3/bspc/deps/qcommon/cmd.c +++ /dev/null @@ -1,715 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// cmd.c -- Quake script command processing module - -#include "q_shared.h" -#include "qcommon.h" - -#define MAX_CMD_BUFFER 16384 -#define MAX_CMD_LINE 1024 - -typedef struct { - byte *data; - int maxsize; - int cursize; -} cmd_t; - -int cmd_wait; -cmd_t cmd_text; -byte cmd_text_buf[MAX_CMD_BUFFER]; - - -//============================================================================= - -/* -============ -Cmd_Wait_f - -Causes execution of the remainder of the command buffer to be delayed until -next frame. This allows commands like: -bind g "cmd use rocket ; +attack ; wait ; -attack ; cmd use blaster" -============ -*/ -void Cmd_Wait_f( void ) { - if ( Cmd_Argc() == 2 ) { - cmd_wait = atoi( Cmd_Argv( 1 ) ); - } else { - cmd_wait = 1; - } -} - - -/* -============================================================================= - - COMMAND BUFFER - -============================================================================= -*/ - -/* -============ -Cbuf_Init -============ -*/ -void Cbuf_Init (void) -{ - cmd_text.data = cmd_text_buf; - cmd_text.maxsize = MAX_CMD_BUFFER; - cmd_text.cursize = 0; -} - -/* -============ -Cbuf_AddText - -Adds command text at the end of the buffer, does NOT add a final \n -============ -*/ -void Cbuf_AddText( const char *text ) { - int l; - - l = strlen (text); - - if (cmd_text.cursize + l >= cmd_text.maxsize) - { - Com_Printf ("Cbuf_AddText: overflow\n"); - return; - } - Com_Memcpy(&cmd_text.data[cmd_text.cursize], text, l); - cmd_text.cursize += l; -} - - -/* -============ -Cbuf_InsertText - -Adds command text immediately after the current command -Adds a \n to the text -============ -*/ -void Cbuf_InsertText( const char *text ) { - int len; - int i; - - len = strlen( text ) + 1; - if ( len + cmd_text.cursize > cmd_text.maxsize ) { - Com_Printf( "Cbuf_InsertText overflowed\n" ); - return; - } - - // move the existing command text - for ( i = cmd_text.cursize - 1 ; i >= 0 ; i-- ) { - cmd_text.data[ i + len ] = cmd_text.data[ i ]; - } - - // copy the new text in - Com_Memcpy( cmd_text.data, text, len - 1 ); - - // add a \n - cmd_text.data[ len - 1 ] = '\n'; - - cmd_text.cursize += len; -} - - -/* -============ -Cbuf_ExecuteText -============ -*/ -void Cbuf_ExecuteText (int exec_when, const char *text) -{ - switch (exec_when) - { - case EXEC_NOW: - if (text && strlen(text) > 0) { - Cmd_ExecuteString (text); - } else { - Cbuf_Execute(); - } - break; - case EXEC_INSERT: - Cbuf_InsertText (text); - break; - case EXEC_APPEND: - Cbuf_AddText (text); - break; - default: - Com_Error (ERR_FATAL, "Cbuf_ExecuteText: bad exec_when"); - } -} - -/* -============ -Cbuf_Execute -============ -*/ -void Cbuf_Execute (void) -{ - int i; - char *text; - char line[MAX_CMD_LINE]; - int quotes; - - while (cmd_text.cursize) - { - if ( cmd_wait ) { - // skip out while text still remains in buffer, leaving it - // for next frame - cmd_wait--; - break; - } - - // find a \n or ; line break - text = (char *)cmd_text.data; - - quotes = 0; - for (i=0 ; i< cmd_text.cursize ; i++) - { - if (text[i] == '"') - quotes++; - if ( !(quotes&1) && text[i] == ';') - break; // don't break if inside a quoted string - if (text[i] == '\n' || text[i] == '\r' ) - break; - } - - if( i >= (MAX_CMD_LINE - 1)) { - i = MAX_CMD_LINE - 1; - } - - Com_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) can insert data at the -// beginning of the text buffer - - if (i == cmd_text.cursize) - cmd_text.cursize = 0; - else - { - i++; - cmd_text.cursize -= i; - memmove (text, text+i, cmd_text.cursize); - } - -// execute the command line - - Cmd_ExecuteString (line); - } -} - - -/* -============================================================================== - - SCRIPT COMMANDS - -============================================================================== -*/ - - -/* -=============== -Cmd_Exec_f -=============== -*/ -void Cmd_Exec_f( void ) { - char *f; - int len; - char filename[MAX_QPATH]; - - if (Cmd_Argc () != 2) { - Com_Printf ("exec : execute a script file\n"); - return; - } - - Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) ); - COM_DefaultExtension( filename, sizeof( filename ), ".cfg" ); - len = FS_ReadFile( filename, (void **)&f); - if (!f) { - Com_Printf ("couldn't exec %s\n",Cmd_Argv(1)); - return; - } - Com_Printf ("execing %s\n",Cmd_Argv(1)); - - Cbuf_InsertText (f); - - FS_FreeFile (f); -} - - -/* -=============== -Cmd_Vstr_f - -Inserts the current value of a variable as command text -=============== -*/ -void Cmd_Vstr_f( void ) { - char *v; - - if (Cmd_Argc () != 2) { - Com_Printf ("vstr : execute a variable command\n"); - return; - } - - v = Cvar_VariableString( Cmd_Argv( 1 ) ); - Cbuf_InsertText( va("%s\n", v ) ); -} - - -/* -=============== -Cmd_Echo_f - -Just prints the rest of the line to the console -=============== -*/ -void Cmd_Echo_f (void) -{ - int i; - - for (i=1 ; i= cmd_argc ) { - return ""; - } - return cmd_argv[arg]; -} - -/* -============ -Cmd_ArgvBuffer - -The interpreted versions use this because -they can't have pointers returned to them -============ -*/ -void Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ) { - Q_strncpyz( buffer, Cmd_Argv( arg ), bufferLength ); -} - - -/* -============ -Cmd_Args - -Returns a single string containing argv(1) to argv(argc()-1) -============ -*/ -char *Cmd_Args( void ) { - static char cmd_args[MAX_STRING_CHARS]; - int i; - - cmd_args[0] = 0; - for ( i = 1 ; i < cmd_argc ; i++ ) { - strcat( cmd_args, cmd_argv[i] ); - if ( i != cmd_argc-1 ) { - strcat( cmd_args, " " ); - } - } - - return cmd_args; -} - -/* -============ -Cmd_Args - -Returns a single string containing argv(arg) to argv(argc()-1) -============ -*/ -char *Cmd_ArgsFrom( int arg ) { - static char cmd_args[BIG_INFO_STRING]; - int i; - - cmd_args[0] = 0; - if (arg < 0) - arg = 0; - for ( i = arg ; i < cmd_argc ; i++ ) { - strcat( cmd_args, cmd_argv[i] ); - if ( i != cmd_argc-1 ) { - strcat( cmd_args, " " ); - } - } - - return cmd_args; -} - -/* -============ -Cmd_ArgsBuffer - -The interpreted versions use this because -they can't have pointers returned to them -============ -*/ -void Cmd_ArgsBuffer( char *buffer, int bufferLength ) { - Q_strncpyz( buffer, Cmd_Args(), bufferLength ); -} - -/* -============ -Cmd_Cmd - -Retrieve the unmodified command string -For rcon use when you want to transmit without altering quoting -https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543 -============ -*/ -char *Cmd_Cmd() -{ - return cmd_cmd; -} - -/* -============ -Cmd_TokenizeString - -Parses the given string into command line tokens. -The text is copied to a seperate buffer and 0 characters -are inserted in the apropriate place, The argv array -will point into this temporary buffer. -============ -*/ -// NOTE TTimo define that to track tokenization issues -//#define TKN_DBG -void Cmd_TokenizeString( const char *text_in ) { - const char *text; - char *textOut; - -#ifdef TKN_DBG - // FIXME TTimo blunt hook to try to find the tokenization of userinfo - Com_DPrintf("Cmd_TokenizeString: %s\n", text_in); -#endif - - // clear previous args - cmd_argc = 0; - - if ( !text_in ) { - return; - } - - Q_strncpyz( cmd_cmd, text_in, sizeof(cmd_cmd) ); - - text = text_in; - textOut = cmd_tokenized; - - while ( 1 ) { - if ( cmd_argc == MAX_STRING_TOKENS ) { - return; // this is usually something malicious - } - - while ( 1 ) { - // skip whitespace - while ( *text && *text <= ' ' ) { - text++; - } - if ( !*text ) { - return; // all tokens parsed - } - - // skip // comments - if ( text[0] == '/' && text[1] == '/' ) { - return; // all tokens parsed - } - - // skip /* */ comments - if ( text[0] == '/' && text[1] =='*' ) { - while ( *text && ( text[0] != '*' || text[1] != '/' ) ) { - text++; - } - if ( !*text ) { - return; // all tokens parsed - } - text += 2; - } else { - break; // we are ready to parse a token - } - } - - // handle quoted strings - // NOTE TTimo this doesn't handle \" escaping - if ( *text == '"' ) { - cmd_argv[cmd_argc] = textOut; - cmd_argc++; - text++; - while ( *text && *text != '"' ) { - *textOut++ = *text++; - } - *textOut++ = 0; - if ( !*text ) { - return; // all tokens parsed - } - text++; - continue; - } - - // regular token - cmd_argv[cmd_argc] = textOut; - cmd_argc++; - - // skip until whitespace, quote, or command - while ( *text > ' ' ) { - if ( text[0] == '"' ) { - break; - } - - if ( text[0] == '/' && text[1] == '/' ) { - break; - } - - // skip /* */ comments - if ( text[0] == '/' && text[1] =='*' ) { - break; - } - - *textOut++ = *text++; - } - - *textOut++ = 0; - - if ( !*text ) { - return; // all tokens parsed - } - } - -} - - -/* -============ -Cmd_AddCommand -============ -*/ -void Cmd_AddCommand( const char *cmd_name, xcommand_t function ) { - cmd_function_t *cmd; - - // fail if the command already exists - for ( cmd = cmd_functions ; cmd ; cmd=cmd->next ) { - if ( !strcmp( cmd_name, cmd->name ) ) { - // allow completion-only commands to be silently doubled - if ( function != NULL ) { - Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); - } - return; - } - } - - // use a small malloc to avoid zone fragmentation - cmd = S_Malloc (sizeof(cmd_function_t)); - cmd->name = CopyString( cmd_name ); - cmd->function = function; - cmd->next = cmd_functions; - cmd_functions = cmd; -} - -/* -============ -Cmd_RemoveCommand -============ -*/ -void Cmd_RemoveCommand( const char *cmd_name ) { - cmd_function_t *cmd, **back; - - back = &cmd_functions; - while( 1 ) { - cmd = *back; - if ( !cmd ) { - // command wasn't active - return; - } - if ( !strcmp( cmd_name, cmd->name ) ) { - *back = cmd->next; - if (cmd->name) { - Z_Free(cmd->name); - } - Z_Free (cmd); - return; - } - back = &cmd->next; - } -} - - -/* -============ -Cmd_CommandCompletion -============ -*/ -void Cmd_CommandCompletion( void(*callback)(const char *s) ) { - cmd_function_t *cmd; - - for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { - callback( cmd->name ); - } -} - - -/* -============ -Cmd_ExecuteString - -A complete command line has been parsed, so try to execute it -============ -*/ -void Cmd_ExecuteString( const char *text ) { - cmd_function_t *cmd, **prev; - - // execute the command line - Cmd_TokenizeString( text ); - if ( !Cmd_Argc() ) { - return; // no tokens - } - - // check registered command functions - for ( prev = &cmd_functions ; *prev ; prev = &cmd->next ) { - cmd = *prev; - if ( !Q_stricmp( cmd_argv[0],cmd->name ) ) { - // rearrange the links so that the command will be - // near the head of the list next time it is used - *prev = cmd->next; - cmd->next = cmd_functions; - cmd_functions = cmd; - - // perform the action - if ( !cmd->function ) { - // let the cgame or game handle it - break; - } else { - cmd->function (); - } - return; - } - } - - // check cvars - if ( Cvar_Command() ) { - return; - } - - // check client game commands - if ( com_cl_running && com_cl_running->integer && CL_GameCommand() ) { - return; - } - - // check server game commands - if ( com_sv_running && com_sv_running->integer && SV_GameCommand() ) { - return; - } - - // check ui commands - if ( com_cl_running && com_cl_running->integer && UI_GameCommand() ) { - return; - } - - // send it as a server command if we are connected - // this will usually result in a chat message - CL_ForwardCommandToServer ( text ); -} - -/* -============ -Cmd_List_f -============ -*/ -void Cmd_List_f (void) -{ - cmd_function_t *cmd; - int i; - char *match; - - if ( Cmd_Argc() > 1 ) { - match = Cmd_Argv( 1 ); - } else { - match = NULL; - } - - i = 0; - for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { - if (match && !Com_Filter(match, cmd->name, qfalse)) continue; - - Com_Printf ("%s\n", cmd->name); - i++; - } - Com_Printf ("%i commands\n", i); -} - -/* -============ -Cmd_Init -============ -*/ -void Cmd_Init (void) { - Cmd_AddCommand ("cmdlist",Cmd_List_f); - Cmd_AddCommand ("exec",Cmd_Exec_f); - Cmd_AddCommand ("vstr",Cmd_Vstr_f); - Cmd_AddCommand ("echo",Cmd_Echo_f); - Cmd_AddCommand ("wait", Cmd_Wait_f); -} - diff --git a/tools/quake3/bspc/deps/qcommon/common.c b/tools/quake3/bspc/deps/qcommon/common.c deleted file mode 100644 index 553dcf44..00000000 --- a/tools/quake3/bspc/deps/qcommon/common.c +++ /dev/null @@ -1,3317 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// common.c -- misc functions used in client and server - -#include "q_shared.h" -#include "qcommon.h" -#include -#ifdef __linux__ -#include -#else -#if defined(MACOS_X) -#include -#else -#include -#endif -#endif - -int demo_protocols[] = -{ 66, 67, 68, 0 }; - -#define MAX_NUM_ARGVS 50 - -#define MIN_DEDICATED_COMHUNKMEGS 1 -#define MIN_COMHUNKMEGS 56 -#ifdef MACOS_X -#define DEF_COMHUNKMEGS "64" -#define DEF_COMZONEMEGS "24" -#else -#define DEF_COMHUNKMEGS "56" -#define DEF_COMZONEMEGS "16" -#endif - -int com_argc; -char *com_argv[MAX_NUM_ARGVS+1]; - -jmp_buf abortframe; // an ERR_DROP occured, exit the entire frame - - -FILE *debuglogfile; -static fileHandle_t logfile; -fileHandle_t com_journalFile; // events are written here -fileHandle_t com_journalDataFile; // config files are written here - -cvar_t *com_viewlog; -cvar_t *com_speeds; -cvar_t *com_developer; -cvar_t *com_dedicated; -cvar_t *com_timescale; -cvar_t *com_fixedtime; -cvar_t *com_dropsim; // 0.0 to 1.0, simulated packet drops -cvar_t *com_journal; -cvar_t *com_maxfps; -cvar_t *com_timedemo; -cvar_t *com_sv_running; -cvar_t *com_cl_running; -cvar_t *com_logfile; // 1 = buffer log, 2 = flush after each print -cvar_t *com_showtrace; -cvar_t *com_version; -cvar_t *com_blood; -cvar_t *com_buildScript; // for automated data building scripts -cvar_t *com_introPlayed; -cvar_t *cl_paused; -cvar_t *sv_paused; -cvar_t *com_cameraMode; -#if defined(_WIN32) && defined(_DEBUG) -cvar_t *com_noErrorInterrupt; -#endif - -// com_speeds times -int time_game; -int time_frontend; // renderer frontend time -int time_backend; // renderer backend time - -int com_frameTime; -int com_frameMsec; -int com_frameNumber; - -qboolean com_errorEntered; -qboolean com_fullyInitialized; - -char com_errorMessage[MAXPRINTMSG]; - -void Com_WriteConfig_f( void ); -void CIN_CloseAllVideos(); - -//============================================================================ - -static char *rd_buffer; -static int rd_buffersize; -static void (*rd_flush)( char *buffer ); - -void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)( char *) ) -{ - if (!buffer || !buffersize || !flush) - return; - rd_buffer = buffer; - rd_buffersize = buffersize; - rd_flush = flush; - - *rd_buffer = 0; -} - -void Com_EndRedirect (void) -{ - if ( rd_flush ) { - rd_flush(rd_buffer); - } - - rd_buffer = NULL; - rd_buffersize = 0; - rd_flush = NULL; -} - -/* -============= -Com_Printf - -Both client and server can use this, and it will output -to the apropriate place. - -A raw string should NEVER be passed as fmt, because of "%f" type crashers. -============= -*/ -void QDECL Com_Printf( const char *fmt, ... ) { - va_list argptr; - char msg[MAXPRINTMSG]; - static qboolean opening_qconsole = qfalse; - - va_start (argptr,fmt); - Q_vsnprintf (msg, sizeof(msg), fmt, argptr); - va_end (argptr); - - if ( rd_buffer ) { - if ((strlen (msg) + strlen(rd_buffer)) > (rd_buffersize - 1)) { - rd_flush(rd_buffer); - *rd_buffer = 0; - } - Q_strcat(rd_buffer, rd_buffersize, msg); - // TTimo nooo .. that would defeat the purpose - //rd_flush(rd_buffer); - //*rd_buffer = 0; - return; - } - - // echo to console if we're not a dedicated server - if ( com_dedicated && !com_dedicated->integer ) { - CL_ConsolePrint( msg ); - } - - // echo to dedicated console and early console - Sys_Print( msg ); - - // logfile - if ( com_logfile && com_logfile->integer ) { - // TTimo: only open the qconsole.log if the filesystem is in an initialized state - // also, avoid recursing in the qconsole.log opening (i.e. if fs_debug is on) - if ( !logfile && FS_Initialized() && !opening_qconsole) { - struct tm *newtime; - time_t aclock; - - opening_qconsole = qtrue; - - time( &aclock ); - newtime = localtime( &aclock ); - - logfile = FS_FOpenFileWrite( "qconsole.log" ); - Com_Printf( "logfile opened on %s\n", asctime( newtime ) ); - if ( com_logfile->integer > 1 ) { - // force it to not buffer so we get valid - // data even if we are crashing - FS_ForceFlush(logfile); - } - - opening_qconsole = qfalse; - } - if ( logfile && FS_Initialized()) { - FS_Write(msg, strlen(msg), logfile); - } - } -} - - -/* -================ -Com_DPrintf - -A Com_Printf that only shows up if the "developer" cvar is set -================ -*/ -void QDECL Com_DPrintf( const char *fmt, ...) { - va_list argptr; - char msg[MAXPRINTMSG]; - - if ( !com_developer || !com_developer->integer ) { - return; // don't confuse non-developers with techie stuff... - } - - va_start (argptr,fmt); - Q_vsnprintf (msg, sizeof(msg), fmt, argptr); - va_end (argptr); - - Com_Printf ("%s", msg); -} - -/* -============= -Com_Error - -Both client and server can use this, and it will -do the apropriate things. -============= -*/ -void QDECL Com_Error( int code, const char *fmt, ... ) { - va_list argptr; - static int lastErrorTime; - static int errorCount; - int currentTime; - -#if defined(_WIN32) && defined(_DEBUG) - if ( code != ERR_DISCONNECT && code != ERR_NEED_CD ) { - if (!com_noErrorInterrupt->integer) { - __asm { - int 0x03 - } - } - } -#endif - - // when we are running automated scripts, make sure we - // know if anything failed - if ( com_buildScript && com_buildScript->integer ) { - code = ERR_FATAL; - } - - // make sure we can get at our local stuff - FS_PureServerSetLoadedPaks( "", "" ); - - // if we are getting a solid stream of ERR_DROP, do an ERR_FATAL - currentTime = Sys_Milliseconds(); - if ( currentTime - lastErrorTime < 100 ) { - if ( ++errorCount > 3 ) { - code = ERR_FATAL; - } - } else { - errorCount = 0; - } - lastErrorTime = currentTime; - - if ( com_errorEntered ) { - Sys_Error( "recursive error after: %s", com_errorMessage ); - } - com_errorEntered = qtrue; - - va_start (argptr,fmt); - vsprintf (com_errorMessage,fmt,argptr); - va_end (argptr); - - if ( code != ERR_DISCONNECT && code != ERR_NEED_CD ) { - Cvar_Set("com_errorMessage", com_errorMessage); - } - - if ( code == ERR_SERVERDISCONNECT ) { - CL_Disconnect( qtrue ); - CL_FlushMemory( ); - com_errorEntered = qfalse; - longjmp (abortframe, -1); - } else if ( code == ERR_DROP || code == ERR_DISCONNECT ) { - Com_Printf ("********************\nERROR: %s\n********************\n", com_errorMessage); - SV_Shutdown (va("Server crashed: %s\n", com_errorMessage)); - CL_Disconnect( qtrue ); - CL_FlushMemory( ); - com_errorEntered = qfalse; - longjmp (abortframe, -1); - } else if ( code == ERR_NEED_CD ) { - SV_Shutdown( "Server didn't have CD\n" ); - if ( com_cl_running && com_cl_running->integer ) { - CL_Disconnect( qtrue ); - CL_FlushMemory( ); - com_errorEntered = qfalse; - CL_CDDialog(); - } else { - Com_Printf("Server didn't have CD\n" ); - } - longjmp (abortframe, -1); - } else { - CL_Shutdown (); - SV_Shutdown (va("Server fatal crashed: %s\n", com_errorMessage)); - } - - Com_Shutdown (); - - Sys_Error ("%s", com_errorMessage); -} - - -/* -============= -Com_Quit_f - -Both client and server can use this, and it will -do the apropriate things. -============= -*/ -void Com_Quit_f( void ) { - // don't try to shutdown if we are in a recursive error - if ( !com_errorEntered ) { - SV_Shutdown ("Server quit\n"); - CL_Shutdown (); - Com_Shutdown (); - FS_Shutdown(qtrue); - } - Sys_Quit (); -} - - - -/* -============================================================================ - -COMMAND LINE FUNCTIONS - -+ characters seperate the commandLine string into multiple console -command lines. - -All of these are valid: - -quake3 +set test blah +map test -quake3 set test blah+map test -quake3 set test blah + map test - -============================================================================ -*/ - -#define MAX_CONSOLE_LINES 32 -int com_numConsoleLines; -char *com_consoleLines[MAX_CONSOLE_LINES]; - -/* -================== -Com_ParseCommandLine - -Break it up into multiple console lines -================== -*/ -void Com_ParseCommandLine( char *commandLine ) { - int inq = 0; - com_consoleLines[0] = commandLine; - com_numConsoleLines = 1; - - while ( *commandLine ) { - if (*commandLine == '"') { - inq = !inq; - } - // look for a + seperating character - // if commandLine came from a file, we might have real line seperators - if ( (*commandLine == '+' && !inq) || *commandLine == '\n' || *commandLine == '\r' ) { - if ( com_numConsoleLines == MAX_CONSOLE_LINES ) { - return; - } - com_consoleLines[com_numConsoleLines] = commandLine + 1; - com_numConsoleLines++; - *commandLine = 0; - } - commandLine++; - } -} - - -/* -=================== -Com_SafeMode - -Check for "safe" on the command line, which will -skip loading of q3config.cfg -=================== -*/ -qboolean Com_SafeMode( void ) { - int i; - - for ( i = 0 ; i < com_numConsoleLines ; i++ ) { - Cmd_TokenizeString( com_consoleLines[i] ); - if ( !Q_stricmp( Cmd_Argv(0), "safe" ) - || !Q_stricmp( Cmd_Argv(0), "cvar_restart" ) ) { - com_consoleLines[i][0] = 0; - return qtrue; - } - } - return qfalse; -} - - -/* -=============== -Com_StartupVariable - -Searches for command line parameters that are set commands. -If match is not NULL, only that cvar will be looked for. -That is necessary because cddir and basedir need to be set -before the filesystem is started, but all other sets shouls -be after execing the config and default. -=============== -*/ -void Com_StartupVariable( const char *match ) { - int i; - char *s; - cvar_t *cv; - - for (i=0 ; i < com_numConsoleLines ; i++) { - Cmd_TokenizeString( com_consoleLines[i] ); - if ( strcmp( Cmd_Argv(0), "set" ) ) { - continue; - } - - s = Cmd_Argv(1); - if ( !match || !strcmp( s, match ) ) { - Cvar_Set( s, Cmd_Argv(2) ); - cv = Cvar_Get( s, "", 0 ); - cv->flags |= CVAR_USER_CREATED; -// com_consoleLines[i] = 0; - } - } -} - - -/* -================= -Com_AddStartupCommands - -Adds command line parameters as script statements -Commands are seperated by + signs - -Returns qtrue if any late commands were added, which -will keep the demoloop from immediately starting -================= -*/ -qboolean Com_AddStartupCommands( void ) { - int i; - qboolean added; - - added = qfalse; - // quote every token, so args with semicolons can work - for (i=0 ; i < com_numConsoleLines ; i++) { - if ( !com_consoleLines[i] || !com_consoleLines[i][0] ) { - continue; - } - - // set commands won't override menu startup - if ( Q_stricmpn( com_consoleLines[i], "set", 3 ) ) { - added = qtrue; - } - Cbuf_AddText( com_consoleLines[i] ); - Cbuf_AddText( "\n" ); - } - - return added; -} - - -//============================================================================ - -void Info_Print( const char *s ) { - char key[512]; - char value[512]; - char *o; - int l; - - if (*s == '\\') - s++; - while (*s) - { - o = key; - while (*s && *s != '\\') - *o++ = *s++; - - l = o - key; - if (l < 20) - { - Com_Memset (o, ' ', 20-l); - key[20] = 0; - } - else - *o = 0; - Com_Printf ("%s", key); - - if (!*s) - { - Com_Printf ("MISSING VALUE\n"); - return; - } - - o = value; - s++; - while (*s && *s != '\\') - *o++ = *s++; - *o = 0; - - if (*s) - s++; - Com_Printf ("%s\n", value); - } -} - -/* -============ -Com_StringContains -============ -*/ -char *Com_StringContains(char *str1, char *str2, int casesensitive) { - int len, i, j; - - len = strlen(str1) - strlen(str2); - for (i = 0; i <= len; i++, str1++) { - for (j = 0; str2[j]; j++) { - if (casesensitive) { - if (str1[j] != str2[j]) { - break; - } - } - else { - if (toupper(str1[j]) != toupper(str2[j])) { - break; - } - } - } - if (!str2[j]) { - return str1; - } - } - return NULL; -} - -/* -============ -Com_Filter -============ -*/ -int Com_Filter(char *filter, char *name, int casesensitive) -{ - char buf[MAX_TOKEN_CHARS]; - char *ptr; - int i, found; - - while(*filter) { - if (*filter == '*') { - filter++; - for (i = 0; *filter; i++) { - if (*filter == '*' || *filter == '?') break; - buf[i] = *filter; - filter++; - } - buf[i] = '\0'; - if (strlen(buf)) { - ptr = Com_StringContains(name, buf, casesensitive); - if (!ptr) return qfalse; - name = ptr + strlen(buf); - } - } - else if (*filter == '?') { - filter++; - name++; - } - else if (*filter == '[' && *(filter+1) == '[') { - filter++; - } - else if (*filter == '[') { - filter++; - found = qfalse; - while(*filter && !found) { - if (*filter == ']' && *(filter+1) != ']') break; - if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) { - if (casesensitive) { - if (*name >= *filter && *name <= *(filter+2)) found = qtrue; - } - else { - if (toupper(*name) >= toupper(*filter) && - toupper(*name) <= toupper(*(filter+2))) found = qtrue; - } - filter += 3; - } - else { - if (casesensitive) { - if (*filter == *name) found = qtrue; - } - else { - if (toupper(*filter) == toupper(*name)) found = qtrue; - } - filter++; - } - } - if (!found) return qfalse; - while(*filter) { - if (*filter == ']' && *(filter+1) != ']') break; - filter++; - } - filter++; - name++; - } - else { - if (casesensitive) { - if (*filter != *name) return qfalse; - } - else { - if (toupper(*filter) != toupper(*name)) return qfalse; - } - filter++; - name++; - } - } - return qtrue; -} - -/* -============ -Com_FilterPath -============ -*/ -int Com_FilterPath(char *filter, char *name, int casesensitive) -{ - int i; - char new_filter[MAX_QPATH]; - char new_name[MAX_QPATH]; - - for (i = 0; i < MAX_QPATH-1 && filter[i]; i++) { - if ( filter[i] == '\\' || filter[i] == ':' ) { - new_filter[i] = '/'; - } - else { - new_filter[i] = filter[i]; - } - } - new_filter[i] = '\0'; - for (i = 0; i < MAX_QPATH-1 && name[i]; i++) { - if ( name[i] == '\\' || name[i] == ':' ) { - new_name[i] = '/'; - } - else { - new_name[i] = name[i]; - } - } - new_name[i] = '\0'; - return Com_Filter(new_filter, new_name, casesensitive); -} - -/* -============ -Com_HashKey -============ -*/ -int Com_HashKey(char *string, int maxlen) { - int register hash, i; - - hash = 0; - for (i = 0; i < maxlen && string[i] != '\0'; i++) { - hash += string[i] * (119 + i); - } - hash = (hash ^ (hash >> 10) ^ (hash >> 20)); - return hash; -} - -/* -================ -Com_RealTime -================ -*/ -int Com_RealTime(qtime_t *qtime) { - time_t t; - struct tm *tms; - - t = time(NULL); - if (!qtime) - return t; - tms = localtime(&t); - if (tms) { - qtime->tm_sec = tms->tm_sec; - qtime->tm_min = tms->tm_min; - qtime->tm_hour = tms->tm_hour; - qtime->tm_mday = tms->tm_mday; - qtime->tm_mon = tms->tm_mon; - qtime->tm_year = tms->tm_year; - qtime->tm_wday = tms->tm_wday; - qtime->tm_yday = tms->tm_yday; - qtime->tm_isdst = tms->tm_isdst; - } - return t; -} - - -/* -============================================================================== - - ZONE MEMORY ALLOCATION - -There is never any space between memblocks, and there will never be two -contiguous free memblocks. - -The rover can be left pointing at a non-empty block - -The zone calls are pretty much only used for small strings and structures, -all big things are allocated on the hunk. -============================================================================== -*/ - -#define ZONEID 0x1d4a11 -#define MINFRAGMENT 64 - -typedef struct zonedebug_s { - char *label; - char *file; - int line; - int allocSize; -} zonedebug_t; - -typedef struct memblock_s { - int size; // including the header and possibly tiny fragments - int tag; // a tag of 0 is a free block - struct memblock_s *next, *prev; - int id; // should be ZONEID -#ifdef ZONE_DEBUG - zonedebug_t d; -#endif -} memblock_t; - -typedef struct { - int size; // total bytes malloced, including header - int used; // total bytes used - memblock_t blocklist; // start / end cap for linked list - memblock_t *rover; -} memzone_t; - -// main zone for all "dynamic" memory allocation -memzone_t *mainzone; -// we also have a small zone for small allocations that would only -// fragment the main zone (think of cvar and cmd strings) -memzone_t *smallzone; - -void Z_CheckHeap( void ); - -/* -======================== -Z_ClearZone -======================== -*/ -void Z_ClearZone( memzone_t *zone, int size ) { - memblock_t *block; - - // set the entire zone to one free block - - zone->blocklist.next = zone->blocklist.prev = block = - (memblock_t *)( (byte *)zone + sizeof(memzone_t) ); - zone->blocklist.tag = 1; // in use block - zone->blocklist.id = 0; - zone->blocklist.size = 0; - zone->rover = block; - zone->size = size; - zone->used = 0; - - block->prev = block->next = &zone->blocklist; - block->tag = 0; // free block - block->id = ZONEID; - block->size = size - sizeof(memzone_t); -} - -/* -======================== -Z_AvailableZoneMemory -======================== -*/ -int Z_AvailableZoneMemory( memzone_t *zone ) { - return zone->size - zone->used; -} - -/* -======================== -Z_AvailableMemory -======================== -*/ -int Z_AvailableMemory( void ) { - return Z_AvailableZoneMemory( mainzone ); -} - -/* -======================== -Z_Free -======================== -*/ -void Z_Free( void *ptr ) { - memblock_t *block, *other; - memzone_t *zone; - - if (!ptr) { - Com_Error( ERR_DROP, "Z_Free: NULL pointer" ); - } - - block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); - if (block->id != ZONEID) { - Com_Error( ERR_FATAL, "Z_Free: freed a pointer without ZONEID" ); - } - if (block->tag == 0) { - Com_Error( ERR_FATAL, "Z_Free: freed a freed pointer" ); - } - // if static memory - if (block->tag == TAG_STATIC) { - return; - } - - // check the memory trash tester - if ( *(int *)((byte *)block + block->size - 4 ) != ZONEID ) { - Com_Error( ERR_FATAL, "Z_Free: memory block wrote past end" ); - } - - if (block->tag == TAG_SMALL) { - zone = smallzone; - } - else { - zone = mainzone; - } - - zone->used -= block->size; - // set the block to something that should cause problems - // if it is referenced... - Com_Memset( ptr, 0xaa, block->size - sizeof( *block ) ); - - block->tag = 0; // mark as free - - other = block->prev; - if (!other->tag) { - // merge with previous free block - other->size += block->size; - other->next = block->next; - other->next->prev = other; - if (block == zone->rover) { - zone->rover = other; - } - block = other; - } - - zone->rover = block; - - other = block->next; - if ( !other->tag ) { - // merge the next free block onto the end - block->size += other->size; - block->next = other->next; - block->next->prev = block; - if (other == zone->rover) { - zone->rover = block; - } - } -} - - -/* -================ -Z_FreeTags -================ -*/ -void Z_FreeTags( int tag ) { - int count; - memzone_t *zone; - - if ( tag == TAG_SMALL ) { - zone = smallzone; - } - else { - zone = mainzone; - } - count = 0; - // use the rover as our pointer, because - // Z_Free automatically adjusts it - zone->rover = zone->blocklist.next; - do { - if ( zone->rover->tag == tag ) { - count++; - Z_Free( (void *)(zone->rover + 1) ); - continue; - } - zone->rover = zone->rover->next; - } while ( zone->rover != &zone->blocklist ); -} - - -/* -================ -Z_TagMalloc -================ -*/ -#ifdef ZONE_DEBUG -void *Z_TagMallocDebug( int size, int tag, char *label, char *file, int line ) { -#else -void *Z_TagMalloc( int size, int tag ) { -#endif - int extra, allocSize; - memblock_t *start, *rover, *new, *base; - memzone_t *zone; - - if (!tag) { - Com_Error( ERR_FATAL, "Z_TagMalloc: tried to use a 0 tag" ); - } - - if ( tag == TAG_SMALL ) { - zone = smallzone; - } - else { - zone = mainzone; - } - - allocSize = size; - // - // scan through the block list looking for the first free block - // of sufficient size - // - size += sizeof(memblock_t); // account for size of block header - size += 4; // space for memory trash tester - size = (size + 3) & ~3; // align to 32 bit boundary - - base = rover = zone->rover; - start = base->prev; - - do { - if (rover == start) { -#ifdef ZONE_DEBUG - Z_LogHeap(); -#endif - // scaned all the way around the list - Com_Error( ERR_FATAL, "Z_Malloc: failed on allocation of %i bytes from the %s zone", - size, zone == smallzone ? "small" : "main"); - return NULL; - } - if (rover->tag) { - base = rover = rover->next; - } else { - rover = rover->next; - } - } while (base->tag || base->size < size); - - // - // found a block big enough - // - extra = base->size - size; - if (extra > MINFRAGMENT) { - // there will be a free fragment after the allocated block - new = (memblock_t *) ((byte *)base + size ); - new->size = extra; - new->tag = 0; // free block - new->prev = base; - new->id = ZONEID; - new->next = base->next; - new->next->prev = new; - base->next = new; - base->size = size; - } - - base->tag = tag; // no longer a free block - - zone->rover = base->next; // next allocation will start looking here - zone->used += base->size; // - - base->id = ZONEID; - -#ifdef ZONE_DEBUG - base->d.label = label; - base->d.file = file; - base->d.line = line; - base->d.allocSize = allocSize; -#endif - - // marker for memory trash testing - *(int *)((byte *)base + base->size - 4) = ZONEID; - - return (void *) ((byte *)base + sizeof(memblock_t)); -} - -/* -======================== -Z_Malloc -======================== -*/ -#ifdef ZONE_DEBUG -void *Z_MallocDebug( int size, char *label, char *file, int line ) { -#else -void *Z_Malloc( int size ) { -#endif - void *buf; - - //Z_CheckHeap (); // DEBUG - -#ifdef ZONE_DEBUG - buf = Z_TagMallocDebug( size, TAG_GENERAL, label, file, line ); -#else - buf = Z_TagMalloc( size, TAG_GENERAL ); -#endif - Com_Memset( buf, 0, size ); - - return buf; -} - -#ifdef ZONE_DEBUG -void *S_MallocDebug( int size, char *label, char *file, int line ) { - return Z_TagMallocDebug( size, TAG_SMALL, label, file, line ); -} -#else -void *S_Malloc( int size ) { - return Z_TagMalloc( size, TAG_SMALL ); -} -#endif - -/* -======================== -Z_CheckHeap -======================== -*/ -void Z_CheckHeap( void ) { - memblock_t *block; - - for (block = mainzone->blocklist.next ; ; block = block->next) { - if (block->next == &mainzone->blocklist) { - break; // all blocks have been hit - } - if ( (byte *)block + block->size != (byte *)block->next) - Com_Error( ERR_FATAL, "Z_CheckHeap: block size does not touch the next block\n" ); - if ( block->next->prev != block) { - Com_Error( ERR_FATAL, "Z_CheckHeap: next block doesn't have proper back link\n" ); - } - if ( !block->tag && !block->next->tag ) { - Com_Error( ERR_FATAL, "Z_CheckHeap: two consecutive free blocks\n" ); - } - } -} - -/* -======================== -Z_LogZoneHeap -======================== -*/ -void Z_LogZoneHeap( memzone_t *zone, char *name ) { -#ifdef ZONE_DEBUG - char dump[32], *ptr; - int i, j; -#endif - memblock_t *block; - char buf[4096]; - int size, allocSize, numBlocks; - - if (!logfile || !FS_Initialized()) - return; - size = allocSize = numBlocks = 0; - Com_sprintf(buf, sizeof(buf), "\r\n================\r\n%s log\r\n================\r\n", name); - FS_Write(buf, strlen(buf), logfile); - for (block = zone->blocklist.next ; block->next != &zone->blocklist; block = block->next) { - if (block->tag) { -#ifdef ZONE_DEBUG - ptr = ((char *) block) + sizeof(memblock_t); - j = 0; - for (i = 0; i < 20 && i < block->d.allocSize; i++) { - if (ptr[i] >= 32 && ptr[i] < 127) { - dump[j++] = ptr[i]; - } - else { - dump[j++] = '_'; - } - } - dump[j] = '\0'; - Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s) [%s]\r\n", block->d.allocSize, block->d.file, block->d.line, block->d.label, dump); - FS_Write(buf, strlen(buf), logfile); - allocSize += block->d.allocSize; -#endif - size += block->size; - numBlocks++; - } - } -#ifdef ZONE_DEBUG - // subtract debug memory - size -= numBlocks * sizeof(zonedebug_t); -#else - allocSize = numBlocks * sizeof(memblock_t); // + 32 bit alignment -#endif - Com_sprintf(buf, sizeof(buf), "%d %s memory in %d blocks\r\n", size, name, numBlocks); - FS_Write(buf, strlen(buf), logfile); - Com_sprintf(buf, sizeof(buf), "%d %s memory overhead\r\n", size - allocSize, name); - FS_Write(buf, strlen(buf), logfile); -} - -/* -======================== -Z_LogHeap -======================== -*/ -void Z_LogHeap( void ) { - Z_LogZoneHeap( mainzone, "MAIN" ); - Z_LogZoneHeap( smallzone, "SMALL" ); -} - -// static mem blocks to reduce a lot of small zone overhead -typedef struct memstatic_s { - memblock_t b; - byte mem[2]; -} memstatic_t; - -// bk001204 - initializer brackets -memstatic_t emptystring = - { {(sizeof(memblock_t)+2 + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'\0', '\0'} }; -memstatic_t numberstring[] = { - { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'0', '\0'} }, - { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'1', '\0'} }, - { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'2', '\0'} }, - { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'3', '\0'} }, - { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'4', '\0'} }, - { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'5', '\0'} }, - { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'6', '\0'} }, - { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'7', '\0'} }, - { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'8', '\0'} }, - { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'9', '\0'} } -}; - -/* -======================== -CopyString - - NOTE: never write over the memory CopyString returns because - memory from a memstatic_t might be returned -======================== -*/ -char *CopyString( const char *in ) { - char *out; - - if (!in[0]) { - return ((char *)&emptystring) + sizeof(memblock_t); - } - else if (!in[1]) { - if (in[0] >= '0' && in[0] <= '9') { - return ((char *)&numberstring[in[0]-'0']) + sizeof(memblock_t); - } - } - out = S_Malloc (strlen(in)+1); - strcpy (out, in); - return out; -} - -/* -============================================================================== - -Goals: - reproducable without history effects -- no out of memory errors on weird map to map changes - allow restarting of the client without fragmentation - minimize total pages in use at run time - minimize total pages needed during load time - - Single block of memory with stack allocators coming from both ends towards the middle. - - One side is designated the temporary memory allocator. - - Temporary memory can be allocated and freed in any order. - - A highwater mark is kept of the most in use at any time. - - When there is no temporary memory allocated, the permanent and temp sides - can be switched, allowing the already touched temp memory to be used for - permanent storage. - - Temp memory must never be allocated on two ends at once, or fragmentation - could occur. - - If we have any in-use temp memory, additional temp allocations must come from - that side. - - If not, we can choose to make either side the new temp side and push future - permanent allocations to the other side. Permanent allocations should be - kept on the side that has the current greatest wasted highwater mark. - -============================================================================== -*/ - - -#define HUNK_MAGIC 0x89537892 -#define HUNK_FREE_MAGIC 0x89537893 - -typedef struct { - int magic; - int size; -} hunkHeader_t; - -typedef struct { - int mark; - int permanent; - int temp; - int tempHighwater; -} hunkUsed_t; - -typedef struct hunkblock_s { - int size; - byte printed; - struct hunkblock_s *next; - char *label; - char *file; - int line; -} hunkblock_t; - -static hunkblock_t *hunkblocks; - -static hunkUsed_t hunk_low, hunk_high; -static hunkUsed_t *hunk_permanent, *hunk_temp; - -static byte *s_hunkData = NULL; -static int s_hunkTotal; - -static int s_zoneTotal; -static int s_smallZoneTotal; - - -/* -================= -Com_Meminfo_f -================= -*/ -void Com_Meminfo_f( void ) { - memblock_t *block; - int zoneBytes, zoneBlocks; - int smallZoneBytes, smallZoneBlocks; - int botlibBytes, rendererBytes; - int unused; - - zoneBytes = 0; - botlibBytes = 0; - rendererBytes = 0; - zoneBlocks = 0; - for (block = mainzone->blocklist.next ; ; block = block->next) { - if ( Cmd_Argc() != 1 ) { - Com_Printf ("block:%p size:%7i tag:%3i\n", - block, block->size, block->tag); - } - if ( block->tag ) { - zoneBytes += block->size; - zoneBlocks++; - if ( block->tag == TAG_BOTLIB ) { - botlibBytes += block->size; - } else if ( block->tag == TAG_RENDERER ) { - rendererBytes += block->size; - } - } - - if (block->next == &mainzone->blocklist) { - break; // all blocks have been hit - } - if ( (byte *)block + block->size != (byte *)block->next) { - Com_Printf ("ERROR: block size does not touch the next block\n"); - } - if ( block->next->prev != block) { - Com_Printf ("ERROR: next block doesn't have proper back link\n"); - } - if ( !block->tag && !block->next->tag ) { - Com_Printf ("ERROR: two consecutive free blocks\n"); - } - } - - smallZoneBytes = 0; - smallZoneBlocks = 0; - for (block = smallzone->blocklist.next ; ; block = block->next) { - if ( block->tag ) { - smallZoneBytes += block->size; - smallZoneBlocks++; - } - - if (block->next == &smallzone->blocklist) { - break; // all blocks have been hit - } - } - - Com_Printf( "%8i bytes total hunk\n", s_hunkTotal ); - Com_Printf( "%8i bytes total zone\n", s_zoneTotal ); - Com_Printf( "\n" ); - Com_Printf( "%8i low mark\n", hunk_low.mark ); - Com_Printf( "%8i low permanent\n", hunk_low.permanent ); - if ( hunk_low.temp != hunk_low.permanent ) { - Com_Printf( "%8i low temp\n", hunk_low.temp ); - } - Com_Printf( "%8i low tempHighwater\n", hunk_low.tempHighwater ); - Com_Printf( "\n" ); - Com_Printf( "%8i high mark\n", hunk_high.mark ); - Com_Printf( "%8i high permanent\n", hunk_high.permanent ); - if ( hunk_high.temp != hunk_high.permanent ) { - Com_Printf( "%8i high temp\n", hunk_high.temp ); - } - Com_Printf( "%8i high tempHighwater\n", hunk_high.tempHighwater ); - Com_Printf( "\n" ); - Com_Printf( "%8i total hunk in use\n", hunk_low.permanent + hunk_high.permanent ); - unused = 0; - if ( hunk_low.tempHighwater > hunk_low.permanent ) { - unused += hunk_low.tempHighwater - hunk_low.permanent; - } - if ( hunk_high.tempHighwater > hunk_high.permanent ) { - unused += hunk_high.tempHighwater - hunk_high.permanent; - } - Com_Printf( "%8i unused highwater\n", unused ); - Com_Printf( "\n" ); - Com_Printf( "%8i bytes in %i zone blocks\n", zoneBytes, zoneBlocks ); - Com_Printf( " %8i bytes in dynamic botlib\n", botlibBytes ); - Com_Printf( " %8i bytes in dynamic renderer\n", rendererBytes ); - Com_Printf( " %8i bytes in dynamic other\n", zoneBytes - ( botlibBytes + rendererBytes ) ); - Com_Printf( " %8i bytes in small Zone memory\n", smallZoneBytes ); -} - -/* -=============== -Com_TouchMemory - -Touch all known used data to make sure it is paged in -=============== -*/ -void Com_TouchMemory( void ) { - int start, end; - int i, j; - int sum; - memblock_t *block; - - Z_CheckHeap(); - - start = Sys_Milliseconds(); - - sum = 0; - - j = hunk_low.permanent >> 2; - for ( i = 0 ; i < j ; i+=64 ) { // only need to touch each page - sum += ((int *)s_hunkData)[i]; - } - - i = ( s_hunkTotal - hunk_high.permanent ) >> 2; - j = hunk_high.permanent >> 2; - for ( ; i < j ; i+=64 ) { // only need to touch each page - sum += ((int *)s_hunkData)[i]; - } - - for (block = mainzone->blocklist.next ; ; block = block->next) { - if ( block->tag ) { - j = block->size >> 2; - for ( i = 0 ; i < j ; i+=64 ) { // only need to touch each page - sum += ((int *)block)[i]; - } - } - if ( block->next == &mainzone->blocklist ) { - break; // all blocks have been hit - } - } - - end = Sys_Milliseconds(); - - Com_Printf( "Com_TouchMemory: %i msec\n", end - start ); -} - - - -/* -================= -Com_InitZoneMemory -================= -*/ -void Com_InitSmallZoneMemory( void ) { - s_smallZoneTotal = 512 * 1024; - // bk001205 - was malloc - smallzone = calloc( s_smallZoneTotal, 1 ); - if ( !smallzone ) { - Com_Error( ERR_FATAL, "Small zone data failed to allocate %1.1f megs", (float)s_smallZoneTotal / (1024*1024) ); - } - Z_ClearZone( smallzone, s_smallZoneTotal ); - - return; -} - -void Com_InitZoneMemory( void ) { - cvar_t *cv; - // allocate the random block zone - cv = Cvar_Get( "com_zoneMegs", DEF_COMZONEMEGS, CVAR_LATCH | CVAR_ARCHIVE ); - - if ( cv->integer < 20 ) { - s_zoneTotal = 1024 * 1024 * 16; - } else { - s_zoneTotal = cv->integer * 1024 * 1024; - } - - // bk001205 - was malloc - mainzone = calloc( s_zoneTotal, 1 ); - if ( !mainzone ) { - Com_Error( ERR_FATAL, "Zone data failed to allocate %i megs", s_zoneTotal / (1024*1024) ); - } - Z_ClearZone( mainzone, s_zoneTotal ); - -} - -/* -================= -Hunk_Log -================= -*/ -void Hunk_Log( void) { - hunkblock_t *block; - char buf[4096]; - int size, numBlocks; - - if (!logfile || !FS_Initialized()) - return; - size = 0; - numBlocks = 0; - Com_sprintf(buf, sizeof(buf), "\r\n================\r\nHunk log\r\n================\r\n"); - FS_Write(buf, strlen(buf), logfile); - for (block = hunkblocks ; block; block = block->next) { -#ifdef HUNK_DEBUG - Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s)\r\n", block->size, block->file, block->line, block->label); - FS_Write(buf, strlen(buf), logfile); -#endif - size += block->size; - numBlocks++; - } - Com_sprintf(buf, sizeof(buf), "%d Hunk memory\r\n", size); - FS_Write(buf, strlen(buf), logfile); - Com_sprintf(buf, sizeof(buf), "%d hunk blocks\r\n", numBlocks); - FS_Write(buf, strlen(buf), logfile); -} - -/* -================= -Hunk_SmallLog -================= -*/ -void Hunk_SmallLog( void) { - hunkblock_t *block, *block2; - char buf[4096]; - int size, locsize, numBlocks; - - if (!logfile || !FS_Initialized()) - return; - for (block = hunkblocks ; block; block = block->next) { - block->printed = qfalse; - } - size = 0; - numBlocks = 0; - Com_sprintf(buf, sizeof(buf), "\r\n================\r\nHunk Small log\r\n================\r\n"); - FS_Write(buf, strlen(buf), logfile); - for (block = hunkblocks; block; block = block->next) { - if (block->printed) { - continue; - } - locsize = block->size; - for (block2 = block->next; block2; block2 = block2->next) { - if (block->line != block2->line) { - continue; - } - if (Q_stricmp(block->file, block2->file)) { - continue; - } - size += block2->size; - locsize += block2->size; - block2->printed = qtrue; - } -#ifdef HUNK_DEBUG - Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s)\r\n", locsize, block->file, block->line, block->label); - FS_Write(buf, strlen(buf), logfile); -#endif - size += block->size; - numBlocks++; - } - Com_sprintf(buf, sizeof(buf), "%d Hunk memory\r\n", size); - FS_Write(buf, strlen(buf), logfile); - Com_sprintf(buf, sizeof(buf), "%d hunk blocks\r\n", numBlocks); - FS_Write(buf, strlen(buf), logfile); -} - -/* -================= -Com_InitZoneMemory -================= -*/ -void Com_InitHunkMemory( void ) { - cvar_t *cv; - int nMinAlloc; - char *pMsg = NULL; - - // make sure the file system has allocated and "not" freed any temp blocks - // this allows the config and product id files ( journal files too ) to be loaded - // by the file system without redunant routines in the file system utilizing different - // memory systems - if (FS_LoadStack() != 0) { - Com_Error( ERR_FATAL, "Hunk initialization failed. File system load stack not zero"); - } - - // allocate the stack based hunk allocator - cv = Cvar_Get( "com_hunkMegs", DEF_COMHUNKMEGS, CVAR_LATCH | CVAR_ARCHIVE ); - - // if we are not dedicated min allocation is 56, otherwise min is 1 - if (com_dedicated && com_dedicated->integer) { - nMinAlloc = MIN_DEDICATED_COMHUNKMEGS; - pMsg = "Minimum com_hunkMegs for a dedicated server is %i, allocating %i megs.\n"; - } - else { - nMinAlloc = MIN_COMHUNKMEGS; - pMsg = "Minimum com_hunkMegs is %i, allocating %i megs.\n"; - } - - if ( cv->integer < nMinAlloc ) { - s_hunkTotal = 1024 * 1024 * nMinAlloc; - Com_Printf(pMsg, nMinAlloc, s_hunkTotal / (1024 * 1024)); - } else { - s_hunkTotal = cv->integer * 1024 * 1024; - } - - - // bk001205 - was malloc - s_hunkData = calloc( s_hunkTotal + 31, 1 ); - if ( !s_hunkData ) { - Com_Error( ERR_FATAL, "Hunk data failed to allocate %i megs", s_hunkTotal / (1024*1024) ); - } - // cacheline align - s_hunkData = (byte *) ( ( (int)s_hunkData + 31 ) & ~31 ); - Hunk_Clear(); - - Cmd_AddCommand( "meminfo", Com_Meminfo_f ); -#ifdef ZONE_DEBUG - Cmd_AddCommand( "zonelog", Z_LogHeap ); -#endif -#ifdef HUNK_DEBUG - Cmd_AddCommand( "hunklog", Hunk_Log ); - Cmd_AddCommand( "hunksmalllog", Hunk_SmallLog ); -#endif -} - -/* -==================== -Hunk_MemoryRemaining -==================== -*/ -int Hunk_MemoryRemaining( void ) { - int low, high; - - low = hunk_low.permanent > hunk_low.temp ? hunk_low.permanent : hunk_low.temp; - high = hunk_high.permanent > hunk_high.temp ? hunk_high.permanent : hunk_high.temp; - - return s_hunkTotal - ( low + high ); -} - -/* -=================== -Hunk_SetMark - -The server calls this after the level and game VM have been loaded -=================== -*/ -void Hunk_SetMark( void ) { - hunk_low.mark = hunk_low.permanent; - hunk_high.mark = hunk_high.permanent; -} - -/* -================= -Hunk_ClearToMark - -The client calls this before starting a vid_restart or snd_restart -================= -*/ -void Hunk_ClearToMark( void ) { - hunk_low.permanent = hunk_low.temp = hunk_low.mark; - hunk_high.permanent = hunk_high.temp = hunk_high.mark; -} - -/* -================= -Hunk_CheckMark -================= -*/ -qboolean Hunk_CheckMark( void ) { - if( hunk_low.mark || hunk_high.mark ) { - return qtrue; - } - return qfalse; -} - -void CL_ShutdownCGame( void ); -void CL_ShutdownUI( void ); -void SV_ShutdownGameProgs( void ); - -/* -================= -Hunk_Clear - -The server calls this before shutting down or loading a new map -================= -*/ -void Hunk_Clear( void ) { - -#ifndef DEDICATED - CL_ShutdownCGame(); - CL_ShutdownUI(); -#endif - SV_ShutdownGameProgs(); -#ifndef DEDICATED - CIN_CloseAllVideos(); -#endif - hunk_low.mark = 0; - hunk_low.permanent = 0; - hunk_low.temp = 0; - hunk_low.tempHighwater = 0; - - hunk_high.mark = 0; - hunk_high.permanent = 0; - hunk_high.temp = 0; - hunk_high.tempHighwater = 0; - - hunk_permanent = &hunk_low; - hunk_temp = &hunk_high; - - Com_Printf( "Hunk_Clear: reset the hunk ok\n" ); - VM_Clear(); -#ifdef HUNK_DEBUG - hunkblocks = NULL; -#endif -} - -static void Hunk_SwapBanks( void ) { - hunkUsed_t *swap; - - // can't swap banks if there is any temp already allocated - if ( hunk_temp->temp != hunk_temp->permanent ) { - return; - } - - // if we have a larger highwater mark on this side, start making - // our permanent allocations here and use the other side for temp - if ( hunk_temp->tempHighwater - hunk_temp->permanent > - hunk_permanent->tempHighwater - hunk_permanent->permanent ) { - swap = hunk_temp; - hunk_temp = hunk_permanent; - hunk_permanent = swap; - } -} - -/* -================= -Hunk_Alloc - -Allocate permanent (until the hunk is cleared) memory -================= -*/ -#ifdef HUNK_DEBUG -void *Hunk_AllocDebug( int size, ha_pref preference, char *label, char *file, int line ) { -#else -void *Hunk_Alloc( int size, ha_pref preference ) { -#endif - void *buf; - - if ( s_hunkData == NULL) - { - Com_Error( ERR_FATAL, "Hunk_Alloc: Hunk memory system not initialized" ); - } - - // can't do preference if there is any temp allocated - if (preference == h_dontcare || hunk_temp->temp != hunk_temp->permanent) { - Hunk_SwapBanks(); - } else { - if (preference == h_low && hunk_permanent != &hunk_low) { - Hunk_SwapBanks(); - } else if (preference == h_high && hunk_permanent != &hunk_high) { - Hunk_SwapBanks(); - } - } - -#ifdef HUNK_DEBUG - size += sizeof(hunkblock_t); -#endif - - // round to cacheline - size = (size+31)&~31; - - if ( hunk_low.temp + hunk_high.temp + size > s_hunkTotal ) { -#ifdef HUNK_DEBUG - Hunk_Log(); - Hunk_SmallLog(); -#endif - Com_Error( ERR_DROP, "Hunk_Alloc failed on %i", size ); - } - - if ( hunk_permanent == &hunk_low ) { - buf = (void *)(s_hunkData + hunk_permanent->permanent); - hunk_permanent->permanent += size; - } else { - hunk_permanent->permanent += size; - buf = (void *)(s_hunkData + s_hunkTotal - hunk_permanent->permanent ); - } - - hunk_permanent->temp = hunk_permanent->permanent; - - Com_Memset( buf, 0, size ); - -#ifdef HUNK_DEBUG - { - hunkblock_t *block; - - block = (hunkblock_t *) buf; - block->size = size - sizeof(hunkblock_t); - block->file = file; - block->label = label; - block->line = line; - block->next = hunkblocks; - hunkblocks = block; - buf = ((byte *) buf) + sizeof(hunkblock_t); - } -#endif - return buf; -} - -/* -================= -Hunk_AllocateTempMemory - -This is used by the file loading system. -Multiple files can be loaded in temporary memory. -When the files-in-use count reaches zero, all temp memory will be deleted -================= -*/ -void *Hunk_AllocateTempMemory( int size ) { - void *buf; - hunkHeader_t *hdr; - - // return a Z_Malloc'd block if the hunk has not been initialized - // this allows the config and product id files ( journal files too ) to be loaded - // by the file system without redunant routines in the file system utilizing different - // memory systems - if ( s_hunkData == NULL ) - { - return Z_Malloc(size); - } - - Hunk_SwapBanks(); - - size = ( (size+3)&~3 ) + sizeof( hunkHeader_t ); - - if ( hunk_temp->temp + hunk_permanent->permanent + size > s_hunkTotal ) { - Com_Error( ERR_DROP, "Hunk_AllocateTempMemory: failed on %i", size ); - } - - if ( hunk_temp == &hunk_low ) { - buf = (void *)(s_hunkData + hunk_temp->temp); - hunk_temp->temp += size; - } else { - hunk_temp->temp += size; - buf = (void *)(s_hunkData + s_hunkTotal - hunk_temp->temp ); - } - - if ( hunk_temp->temp > hunk_temp->tempHighwater ) { - hunk_temp->tempHighwater = hunk_temp->temp; - } - - hdr = (hunkHeader_t *)buf; - buf = (void *)(hdr+1); - - hdr->magic = HUNK_MAGIC; - hdr->size = size; - - // don't bother clearing, because we are going to load a file over it - return buf; -} - - -/* -================== -Hunk_FreeTempMemory -================== -*/ -void Hunk_FreeTempMemory( void *buf ) { - hunkHeader_t *hdr; - - // free with Z_Free if the hunk has not been initialized - // this allows the config and product id files ( journal files too ) to be loaded - // by the file system without redunant routines in the file system utilizing different - // memory systems - if ( s_hunkData == NULL ) - { - Z_Free(buf); - return; - } - - - hdr = ( (hunkHeader_t *)buf ) - 1; - if ( hdr->magic != HUNK_MAGIC ) { - Com_Error( ERR_FATAL, "Hunk_FreeTempMemory: bad magic" ); - } - - hdr->magic = HUNK_FREE_MAGIC; - - // this only works if the files are freed in stack order, - // otherwise the memory will stay around until Hunk_ClearTempMemory - if ( hunk_temp == &hunk_low ) { - if ( hdr == (void *)(s_hunkData + hunk_temp->temp - hdr->size ) ) { - hunk_temp->temp -= hdr->size; - } else { - Com_Printf( "Hunk_FreeTempMemory: not the final block\n" ); - } - } else { - if ( hdr == (void *)(s_hunkData + s_hunkTotal - hunk_temp->temp ) ) { - hunk_temp->temp -= hdr->size; - } else { - Com_Printf( "Hunk_FreeTempMemory: not the final block\n" ); - } - } -} - - -/* -================= -Hunk_ClearTempMemory - -The temp space is no longer needed. If we have left more -touched but unused memory on this side, have future -permanent allocs use this side. -================= -*/ -void Hunk_ClearTempMemory( void ) { - if ( s_hunkData != NULL ) { - hunk_temp->temp = hunk_temp->permanent; - } -} - -/* -================= -Hunk_Trash -================= -*/ -void Hunk_Trash( void ) { - int length, i, rnd; - char *buf, value; - - return; - - if ( s_hunkData == NULL ) - return; - -#ifdef _DEBUG - Com_Error(ERR_DROP, "hunk trashed\n"); - return; -#endif - - Cvar_Set("com_jp", "1"); - Hunk_SwapBanks(); - - if ( hunk_permanent == &hunk_low ) { - buf = (void *)(s_hunkData + hunk_permanent->permanent); - } else { - buf = (void *)(s_hunkData + s_hunkTotal - hunk_permanent->permanent ); - } - length = hunk_permanent->permanent; - - if (length > 0x7FFFF) { - //randomly trash data within buf - rnd = random() * (length - 0x7FFFF); - value = 31; - for (i = 0; i < 0x7FFFF; i++) { - value *= 109; - buf[rnd+i] ^= value; - } - } -} - -/* -=================================================================== - -EVENTS AND JOURNALING - -In addition to these events, .cfg files are also copied to the -journaled file -=================================================================== -*/ - -// bk001129 - here we go again: upped from 64 -// FIXME TTimo blunt upping from 256 to 1024 -// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=5 -#define MAX_PUSHED_EVENTS 1024 -// bk001129 - init, also static -static int com_pushedEventsHead = 0; -static int com_pushedEventsTail = 0; -// bk001129 - static -static sysEvent_t com_pushedEvents[MAX_PUSHED_EVENTS]; - -/* -================= -Com_InitJournaling -================= -*/ -void Com_InitJournaling( void ) { - Com_StartupVariable( "journal" ); - com_journal = Cvar_Get ("journal", "0", CVAR_INIT); - if ( !com_journal->integer ) { - return; - } - - if ( com_journal->integer == 1 ) { - Com_Printf( "Journaling events\n"); - com_journalFile = FS_FOpenFileWrite( "journal.dat" ); - com_journalDataFile = FS_FOpenFileWrite( "journaldata.dat" ); - } else if ( com_journal->integer == 2 ) { - Com_Printf( "Replaying journaled events\n"); - FS_FOpenFileRead( "journal.dat", &com_journalFile, qtrue ); - FS_FOpenFileRead( "journaldata.dat", &com_journalDataFile, qtrue ); - } - - if ( !com_journalFile || !com_journalDataFile ) { - Cvar_Set( "com_journal", "0" ); - com_journalFile = 0; - com_journalDataFile = 0; - Com_Printf( "Couldn't open journal files\n" ); - } -} - -/* -================= -Com_GetRealEvent -================= -*/ -sysEvent_t Com_GetRealEvent( void ) { - int r; - sysEvent_t ev; - - // either get an event from the system or the journal file - if ( com_journal->integer == 2 ) { - r = FS_Read( &ev, sizeof(ev), com_journalFile ); - if ( r != sizeof(ev) ) { - Com_Error( ERR_FATAL, "Error reading from journal file" ); - } - if ( ev.evPtrLength ) { - ev.evPtr = Z_Malloc( ev.evPtrLength ); - r = FS_Read( ev.evPtr, ev.evPtrLength, com_journalFile ); - if ( r != ev.evPtrLength ) { - Com_Error( ERR_FATAL, "Error reading from journal file" ); - } - } - } else { - ev = Sys_GetEvent(); - - // write the journal value out if needed - if ( com_journal->integer == 1 ) { - r = FS_Write( &ev, sizeof(ev), com_journalFile ); - if ( r != sizeof(ev) ) { - Com_Error( ERR_FATAL, "Error writing to journal file" ); - } - if ( ev.evPtrLength ) { - r = FS_Write( ev.evPtr, ev.evPtrLength, com_journalFile ); - if ( r != ev.evPtrLength ) { - Com_Error( ERR_FATAL, "Error writing to journal file" ); - } - } - } - } - - return ev; -} - - -/* -================= -Com_InitPushEvent -================= -*/ -// bk001129 - added -void Com_InitPushEvent( void ) { - // clear the static buffer array - // this requires SE_NONE to be accepted as a valid but NOP event - memset( com_pushedEvents, 0, sizeof(com_pushedEvents) ); - // reset counters while we are at it - // beware: GetEvent might still return an SE_NONE from the buffer - com_pushedEventsHead = 0; - com_pushedEventsTail = 0; -} - - -/* -================= -Com_PushEvent -================= -*/ -void Com_PushEvent( sysEvent_t *event ) { - sysEvent_t *ev; - static int printedWarning = 0; // bk001129 - init, bk001204 - explicit int - - ev = &com_pushedEvents[ com_pushedEventsHead & (MAX_PUSHED_EVENTS-1) ]; - - if ( com_pushedEventsHead - com_pushedEventsTail >= MAX_PUSHED_EVENTS ) { - - // don't print the warning constantly, or it can give time for more... - if ( !printedWarning ) { - printedWarning = qtrue; - Com_Printf( "WARNING: Com_PushEvent overflow\n" ); - } - - if ( ev->evPtr ) { - Z_Free( ev->evPtr ); - } - com_pushedEventsTail++; - } else { - printedWarning = qfalse; - } - - *ev = *event; - com_pushedEventsHead++; -} - -/* -================= -Com_GetEvent -================= -*/ -sysEvent_t Com_GetEvent( void ) { - if ( com_pushedEventsHead > com_pushedEventsTail ) { - com_pushedEventsTail++; - return com_pushedEvents[ (com_pushedEventsTail-1) & (MAX_PUSHED_EVENTS-1) ]; - } - return Com_GetRealEvent(); -} - -/* -================= -Com_RunAndTimeServerPacket -================= -*/ -void Com_RunAndTimeServerPacket( netadr_t *evFrom, msg_t *buf ) { - int t1, t2, msec; - - t1 = 0; - - if ( com_speeds->integer ) { - t1 = Sys_Milliseconds (); - } - - SV_PacketEvent( *evFrom, buf ); - - if ( com_speeds->integer ) { - t2 = Sys_Milliseconds (); - msec = t2 - t1; - if ( com_speeds->integer == 3 ) { - Com_Printf( "SV_PacketEvent time: %i\n", msec ); - } - } -} - -/* -================= -Com_EventLoop - -Returns last event time -================= -*/ -int Com_EventLoop( void ) { - sysEvent_t ev; - netadr_t evFrom; - byte bufData[MAX_MSGLEN]; - msg_t buf; - - MSG_Init( &buf, bufData, sizeof( bufData ) ); - - while ( 1 ) { - ev = Com_GetEvent(); - - // if no more events are available - if ( ev.evType == SE_NONE ) { - // manually send packet events for the loopback channel - while ( NET_GetLoopPacket( NS_CLIENT, &evFrom, &buf ) ) { - CL_PacketEvent( evFrom, &buf ); - } - - while ( NET_GetLoopPacket( NS_SERVER, &evFrom, &buf ) ) { - // if the server just shut down, flush the events - if ( com_sv_running->integer ) { - Com_RunAndTimeServerPacket( &evFrom, &buf ); - } - } - - return ev.evTime; - } - - - switch ( ev.evType ) { - default: - // bk001129 - was ev.evTime - Com_Error( ERR_FATAL, "Com_EventLoop: bad event type %i", ev.evType ); - break; - case SE_NONE: - break; - case SE_KEY: - CL_KeyEvent( ev.evValue, ev.evValue2, ev.evTime ); - break; - case SE_CHAR: - CL_CharEvent( ev.evValue ); - break; - case SE_MOUSE: - CL_MouseEvent( ev.evValue, ev.evValue2, ev.evTime ); - break; - case SE_JOYSTICK_AXIS: - CL_JoystickEvent( ev.evValue, ev.evValue2, ev.evTime ); - break; - case SE_CONSOLE: - Cbuf_AddText( (char *)ev.evPtr ); - Cbuf_AddText( "\n" ); - break; - case SE_PACKET: - // this cvar allows simulation of connections that - // drop a lot of packets. Note that loopback connections - // don't go through here at all. - if ( com_dropsim->value > 0 ) { - static int seed; - - if ( Q_random( &seed ) < com_dropsim->value ) { - break; // drop this packet - } - } - - evFrom = *(netadr_t *)ev.evPtr; - buf.cursize = ev.evPtrLength - sizeof( evFrom ); - - // we must copy the contents of the message out, because - // the event buffers are only large enough to hold the - // exact payload, but channel messages need to be large - // enough to hold fragment reassembly - if ( (unsigned)buf.cursize > buf.maxsize ) { - Com_Printf("Com_EventLoop: oversize packet\n"); - continue; - } - Com_Memcpy( buf.data, (byte *)((netadr_t *)ev.evPtr + 1), buf.cursize ); - if ( com_sv_running->integer ) { - Com_RunAndTimeServerPacket( &evFrom, &buf ); - } else { - CL_PacketEvent( evFrom, &buf ); - } - break; - } - - // free any block data - if ( ev.evPtr ) { - Z_Free( ev.evPtr ); - } - } - - return 0; // never reached -} - -/* -================ -Com_Milliseconds - -Can be used for profiling, but will be journaled accurately -================ -*/ -int Com_Milliseconds (void) { - sysEvent_t ev; - - // get events and push them until we get a null event with the current time - do { - - ev = Com_GetRealEvent(); - if ( ev.evType != SE_NONE ) { - Com_PushEvent( &ev ); - } - } while ( ev.evType != SE_NONE ); - - return ev.evTime; -} - -//============================================================================ - -/* -============= -Com_Error_f - -Just throw a fatal error to -test error shutdown procedures -============= -*/ -static void Com_Error_f (void) { - if ( Cmd_Argc() > 1 ) { - Com_Error( ERR_DROP, "Testing drop error" ); - } else { - Com_Error( ERR_FATAL, "Testing fatal error" ); - } -} - - -/* -============= -Com_Freeze_f - -Just freeze in place for a given number of seconds to test -error recovery -============= -*/ -static void Com_Freeze_f (void) { - float s; - int start, now; - - if ( Cmd_Argc() != 2 ) { - Com_Printf( "freeze \n" ); - return; - } - s = atof( Cmd_Argv(1) ); - - start = Com_Milliseconds(); - - while ( 1 ) { - now = Com_Milliseconds(); - if ( ( now - start ) * 0.001 > s ) { - break; - } - } -} - -/* -================= -Com_Crash_f - -A way to force a bus error for development reasons -================= -*/ -static void Com_Crash_f( void ) { - * ( int * ) 0 = 0x12345678; -} - -// TTimo: centralizing the cl_cdkey stuff after I discovered a buffer overflow problem with the dedicated server version -// not sure it's necessary to have different defaults for regular and dedicated, but I don't want to risk it -// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470 -#ifndef DEDICATED -char cl_cdkey[34] = " "; -#else -char cl_cdkey[34] = "123456789"; -#endif - -/* -================= -Com_ReadCDKey -================= -*/ -qboolean CL_CDKeyValidate( const char *key, const char *checksum ); -void Com_ReadCDKey( const char *filename ) { - fileHandle_t f; - char buffer[33]; - char fbuffer[MAX_OSPATH]; - - sprintf(fbuffer, "%s/q3key", filename); - - FS_SV_FOpenFileRead( fbuffer, &f ); - if ( !f ) { - Q_strncpyz( cl_cdkey, " ", 17 ); - return; - } - - Com_Memset( buffer, 0, sizeof(buffer) ); - - FS_Read( buffer, 16, f ); - FS_FCloseFile( f ); - - if (CL_CDKeyValidate(buffer, NULL)) { - Q_strncpyz( cl_cdkey, buffer, 17 ); - } else { - Q_strncpyz( cl_cdkey, " ", 17 ); - } -} - -/* -================= -Com_AppendCDKey -================= -*/ -void Com_AppendCDKey( const char *filename ) { - fileHandle_t f; - char buffer[33]; - char fbuffer[MAX_OSPATH]; - - sprintf(fbuffer, "%s/q3key", filename); - - FS_SV_FOpenFileRead( fbuffer, &f ); - if (!f) { - Q_strncpyz( &cl_cdkey[16], " ", 17 ); - return; - } - - Com_Memset( buffer, 0, sizeof(buffer) ); - - FS_Read( buffer, 16, f ); - FS_FCloseFile( f ); - - if (CL_CDKeyValidate(buffer, NULL)) { - strcat( &cl_cdkey[16], buffer ); - } else { - Q_strncpyz( &cl_cdkey[16], " ", 17 ); - } -} - -#ifndef DEDICATED // bk001204 -/* -================= -Com_WriteCDKey -================= -*/ -static void Com_WriteCDKey( const char *filename, const char *ikey ) { - fileHandle_t f; - char fbuffer[MAX_OSPATH]; - char key[17]; - - - sprintf(fbuffer, "%s/q3key", filename); - - - Q_strncpyz( key, ikey, 17 ); - - if(!CL_CDKeyValidate(key, NULL) ) { - return; - } - - f = FS_SV_FOpenFileWrite( fbuffer ); - if ( !f ) { - Com_Printf ("Couldn't write %s.\n", filename ); - return; - } - - FS_Write( key, 16, f ); - - FS_Printf( f, "\n// generated by quake, do not modify\r\n" ); - FS_Printf( f, "// Do not give this file to ANYONE.\r\n" ); - FS_Printf( f, "// id Software and Activision will NOT ask you to send this file to them.\r\n"); - - FS_FCloseFile( f ); -} -#endif - - -/* -================= -Com_Init -================= -*/ -void Com_Init( char *commandLine ) { - char *s; - - Com_Printf( "%s %s %s\n", Q3_VERSION, CPUSTRING, __DATE__ ); - - if ( setjmp (abortframe) ) { - Sys_Error ("Error during initialization"); - } - - // bk001129 - do this before anything else decides to push events - Com_InitPushEvent(); - - Com_InitSmallZoneMemory(); - Cvar_Init (); - - // prepare enough of the subsystems to handle - // cvar and command buffer management - Com_ParseCommandLine( commandLine ); - -// Swap_Init (); - Cbuf_Init (); - - Com_InitZoneMemory(); - Cmd_Init (); - - // override anything from the config files with command line args - Com_StartupVariable( NULL ); - - // get the developer cvar set as early as possible - Com_StartupVariable( "developer" ); - - // done early so bind command exists - CL_InitKeyCommands(); - - FS_InitFilesystem (); - - Com_InitJournaling(); - - Cbuf_AddText ("exec default.cfg\n"); - - // skip the q3config.cfg if "safe" is on the command line - if ( !Com_SafeMode() ) { - Cbuf_AddText ("exec q3config.cfg\n"); - } - - Cbuf_AddText ("exec autoexec.cfg\n"); - - Cbuf_Execute (); - - // override anything from the config files with command line args - Com_StartupVariable( NULL ); - - // get dedicated here for proper hunk megs initialization -#ifdef DEDICATED - com_dedicated = Cvar_Get ("dedicated", "1", CVAR_ROM); -#else - com_dedicated = Cvar_Get ("dedicated", "0", CVAR_LATCH); -#endif - // allocate the stack based hunk allocator - Com_InitHunkMemory(); - - // if any archived cvars are modified after this, we will trigger a writing - // of the config file - cvar_modifiedFlags &= ~CVAR_ARCHIVE; - - // - // init commands and vars - // - com_maxfps = Cvar_Get ("com_maxfps", "85", CVAR_ARCHIVE); - com_blood = Cvar_Get ("com_blood", "1", CVAR_ARCHIVE); - - com_developer = Cvar_Get ("developer", "0", CVAR_TEMP ); - com_logfile = Cvar_Get ("logfile", "0", CVAR_TEMP ); - - com_timescale = Cvar_Get ("timescale", "1", CVAR_CHEAT | CVAR_SYSTEMINFO ); - com_fixedtime = Cvar_Get ("fixedtime", "0", CVAR_CHEAT); - com_showtrace = Cvar_Get ("com_showtrace", "0", CVAR_CHEAT); - com_dropsim = Cvar_Get ("com_dropsim", "0", CVAR_CHEAT); - com_viewlog = Cvar_Get( "viewlog", "0", CVAR_CHEAT ); - com_speeds = Cvar_Get ("com_speeds", "0", 0); - com_timedemo = Cvar_Get ("timedemo", "0", CVAR_CHEAT); - com_cameraMode = Cvar_Get ("com_cameraMode", "0", CVAR_CHEAT); - - cl_paused = Cvar_Get ("cl_paused", "0", CVAR_ROM); - sv_paused = Cvar_Get ("sv_paused", "0", CVAR_ROM); - com_sv_running = Cvar_Get ("sv_running", "0", CVAR_ROM); - com_cl_running = Cvar_Get ("cl_running", "0", CVAR_ROM); - com_buildScript = Cvar_Get( "com_buildScript", "0", 0 ); - - com_introPlayed = Cvar_Get( "com_introplayed", "0", CVAR_ARCHIVE); - -#if defined(_WIN32) && defined(_DEBUG) - com_noErrorInterrupt = Cvar_Get( "com_noErrorInterrupt", "0", 0 ); -#endif - - if ( com_dedicated->integer ) { - if ( !com_viewlog->integer ) { - Cvar_Set( "viewlog", "1" ); - } - } - - if ( com_developer && com_developer->integer ) { - Cmd_AddCommand ("error", Com_Error_f); - Cmd_AddCommand ("crash", Com_Crash_f ); - Cmd_AddCommand ("freeze", Com_Freeze_f); - } - Cmd_AddCommand ("quit", Com_Quit_f); - Cmd_AddCommand ("changeVectors", MSG_ReportChangeVectors_f ); - Cmd_AddCommand ("writeconfig", Com_WriteConfig_f ); - - s = va("%s %s %s", Q3_VERSION, CPUSTRING, __DATE__ ); - com_version = Cvar_Get ("version", s, CVAR_ROM | CVAR_SERVERINFO ); - - Sys_Init(); - Netchan_Init( Com_Milliseconds() & 0xffff ); // pick a port value that should be nice and random - VM_Init(); - SV_Init(); - - com_dedicated->modified = qfalse; - if ( !com_dedicated->integer ) { - CL_Init(); - Sys_ShowConsole( com_viewlog->integer, qfalse ); - } - - // set com_frameTime so that if a map is started on the - // command line it will still be able to count on com_frameTime - // being random enough for a serverid - com_frameTime = Com_Milliseconds(); - - // add + commands from command line - if ( !Com_AddStartupCommands() ) { - // if the user didn't give any commands, run default action - if ( !com_dedicated->integer ) { - Cbuf_AddText ("cinematic idlogo.RoQ\n"); - if( !com_introPlayed->integer ) { - Cvar_Set( com_introPlayed->name, "1" ); - Cvar_Set( "nextmap", "cinematic intro.RoQ" ); - } - } - } - - // start in full screen ui mode - Cvar_Set("r_uiFullScreen", "1"); - - CL_StartHunkUsers(); - - // make sure single player is off by default - Cvar_Set("ui_singlePlayerActive", "0"); - - com_fullyInitialized = qtrue; - Com_Printf ("--- Common Initialization Complete ---\n"); -} - -//================================================================== - -void Com_WriteConfigToFile( const char *filename ) { - fileHandle_t f; - - f = FS_FOpenFileWrite( filename ); - if ( !f ) { - Com_Printf ("Couldn't write %s.\n", filename ); - return; - } - - FS_Printf (f, "// generated by quake, do not modify\n"); - Key_WriteBindings (f); - Cvar_WriteVariables (f); - FS_FCloseFile( f ); -} - - -/* -=============== -Com_WriteConfiguration - -Writes key bindings and archived cvars to config file if modified -=============== -*/ -void Com_WriteConfiguration( void ) { -#ifndef DEDICATED // bk001204 - cvar_t *fs; -#endif - // if we are quiting without fully initializing, make sure - // we don't write out anything - if ( !com_fullyInitialized ) { - return; - } - - if ( !(cvar_modifiedFlags & CVAR_ARCHIVE ) ) { - return; - } - cvar_modifiedFlags &= ~CVAR_ARCHIVE; - - Com_WriteConfigToFile( "q3config.cfg" ); - - // bk001119 - tentative "not needed for dedicated" -#ifndef DEDICATED - fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) { - Com_WriteCDKey( fs->string, &cl_cdkey[16] ); - } else { - Com_WriteCDKey( "baseq3", cl_cdkey ); - } -#endif -} - - -/* -=============== -Com_WriteConfig_f - -Write the config file to a specific name -=============== -*/ -void Com_WriteConfig_f( void ) { - char filename[MAX_QPATH]; - - if ( Cmd_Argc() != 2 ) { - Com_Printf( "Usage: writeconfig \n" ); - return; - } - - Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) ); - COM_DefaultExtension( filename, sizeof( filename ), ".cfg" ); - Com_Printf( "Writing %s.\n", filename ); - Com_WriteConfigToFile( filename ); -} - -/* -================ -Com_ModifyMsec -================ -*/ -int Com_ModifyMsec( int msec ) { - int clampTime; - - // - // modify time for debugging values - // - if ( com_fixedtime->integer ) { - msec = com_fixedtime->integer; - } else if ( com_timescale->value ) { - msec *= com_timescale->value; - } else if (com_cameraMode->integer) { - msec *= com_timescale->value; - } - - // don't let it scale below 1 msec - if ( msec < 1 && com_timescale->value) { - msec = 1; - } - - if ( com_dedicated->integer ) { - // dedicated servers don't want to clamp for a much longer - // period, because it would mess up all the client's views - // of time. - if ( msec > 500 ) { - Com_Printf( "Hitch warning: %i msec frame time\n", msec ); - } - clampTime = 5000; - } else - if ( !com_sv_running->integer ) { - // clients of remote servers do not want to clamp time, because - // it would skew their view of the server's time temporarily - clampTime = 5000; - } else { - // for local single player gaming - // we may want to clamp the time to prevent players from - // flying off edges when something hitches. - clampTime = 200; - } - - if ( msec > clampTime ) { - msec = clampTime; - } - - return msec; -} - -/* -================= -Com_Frame -================= -*/ -void Com_Frame( void ) { - - int msec, minMsec; - static int lastTime; - int key; - - int timeBeforeFirstEvents; - int timeBeforeServer; - int timeBeforeEvents; - int timeBeforeClient; - int timeAfter; - - - - - - if ( setjmp (abortframe) ) { - return; // an ERR_DROP was thrown - } - - // bk001204 - init to zero. - // also: might be clobbered by `longjmp' or `vfork' - timeBeforeFirstEvents =0; - timeBeforeServer =0; - timeBeforeEvents =0; - timeBeforeClient = 0; - timeAfter = 0; - - - // old net chan encryption key - key = 0x87243987; - - // write config file if anything changed - Com_WriteConfiguration(); - - // if "viewlog" has been modified, show or hide the log console - if ( com_viewlog->modified ) { - if ( !com_dedicated->value ) { - Sys_ShowConsole( com_viewlog->integer, qfalse ); - } - com_viewlog->modified = qfalse; - } - - // - // main event loop - // - if ( com_speeds->integer ) { - timeBeforeFirstEvents = Sys_Milliseconds (); - } - - // we may want to spin here if things are going too fast - if ( !com_dedicated->integer && com_maxfps->integer > 0 && !com_timedemo->integer ) { - minMsec = 1000 / com_maxfps->integer; - } else { - minMsec = 1; - } - do { - com_frameTime = Com_EventLoop(); - if ( lastTime > com_frameTime ) { - lastTime = com_frameTime; // possible on first frame - } - msec = com_frameTime - lastTime; - } while ( msec < minMsec ); - Cbuf_Execute (); - - lastTime = com_frameTime; - - // mess with msec if needed - com_frameMsec = msec; - msec = Com_ModifyMsec( msec ); - - // - // server side - // - if ( com_speeds->integer ) { - timeBeforeServer = Sys_Milliseconds (); - } - - SV_Frame( msec ); - - // if "dedicated" has been modified, start up - // or shut down the client system. - // Do this after the server may have started, - // but before the client tries to auto-connect - if ( com_dedicated->modified ) { - // get the latched value - Cvar_Get( "dedicated", "0", 0 ); - com_dedicated->modified = qfalse; - if ( !com_dedicated->integer ) { - CL_Init(); - Sys_ShowConsole( com_viewlog->integer, qfalse ); - } else { - CL_Shutdown(); - Sys_ShowConsole( 1, qtrue ); - } - } - - // - // client system - // - if ( !com_dedicated->integer ) { - // - // run event loop a second time to get server to client packets - // without a frame of latency - // - if ( com_speeds->integer ) { - timeBeforeEvents = Sys_Milliseconds (); - } - Com_EventLoop(); - Cbuf_Execute (); - - - // - // client side - // - if ( com_speeds->integer ) { - timeBeforeClient = Sys_Milliseconds (); - } - - CL_Frame( msec ); - - if ( com_speeds->integer ) { - timeAfter = Sys_Milliseconds (); - } - } - - // - // report timing information - // - if ( com_speeds->integer ) { - int all, sv, ev, cl; - - all = timeAfter - timeBeforeServer; - sv = timeBeforeEvents - timeBeforeServer; - ev = timeBeforeServer - timeBeforeFirstEvents + timeBeforeClient - timeBeforeEvents; - cl = timeAfter - timeBeforeClient; - sv -= time_game; - cl -= time_frontend + time_backend; - - Com_Printf ("frame:%i all:%3i sv:%3i ev:%3i cl:%3i gm:%3i rf:%3i bk:%3i\n", - com_frameNumber, all, sv, ev, cl, time_game, time_frontend, time_backend ); - } - - // - // trace optimization tracking - // - if ( com_showtrace->integer ) { - - extern int c_traces, c_brush_traces, c_patch_traces; - extern int c_pointcontents; - - Com_Printf ("%4i traces (%ib %ip) %4i points\n", c_traces, - c_brush_traces, c_patch_traces, c_pointcontents); - c_traces = 0; - c_brush_traces = 0; - c_patch_traces = 0; - c_pointcontents = 0; - } - - // old net chan encryption key - key = lastTime * 0x87243987; - - com_frameNumber++; -} - -/* -================= -Com_Shutdown -================= -*/ -void Com_Shutdown (void) { - if (logfile) { - FS_FCloseFile (logfile); - logfile = 0; - } - - if ( com_journalFile ) { - FS_FCloseFile( com_journalFile ); - com_journalFile = 0; - } - -} - -#if !( defined __VECTORC ) -#if !( defined __linux__ || defined __FreeBSD__ ) // r010123 - include FreeBSD -#if ((!id386) && (!defined __i386__)) // rcg010212 - for PPC - -void Com_Memcpy (void* dest, const void* src, const size_t count) -{ - memcpy(dest, src, count); -} - -void Com_Memset (void* dest, const int val, const size_t count) -{ - memset(dest, val, count); -} - -#else - -typedef enum -{ - PRE_READ, // prefetch assuming that buffer is used for reading only - PRE_WRITE, // prefetch assuming that buffer is used for writing only - PRE_READ_WRITE // prefetch assuming that buffer is used for both reading and writing -} e_prefetch; - -void Com_Prefetch (const void *s, const unsigned int bytes, e_prefetch type); - -#define EMMS_INSTRUCTION __asm emms - -void _copyDWord (unsigned int* dest, const unsigned int constant, const unsigned int count) { - __asm - { - mov edx,dest - mov eax,constant - mov ecx,count - and ecx,~7 - jz padding - sub ecx,8 - jmp loopu - align 16 -loopu: - test [edx+ecx*4 + 28],ebx // fetch next block destination to L1 cache - mov [edx+ecx*4 + 0],eax - mov [edx+ecx*4 + 4],eax - mov [edx+ecx*4 + 8],eax - mov [edx+ecx*4 + 12],eax - mov [edx+ecx*4 + 16],eax - mov [edx+ecx*4 + 20],eax - mov [edx+ecx*4 + 24],eax - mov [edx+ecx*4 + 28],eax - sub ecx,8 - jge loopu -padding: mov ecx,count - mov ebx,ecx - and ecx,7 - jz outta - and ebx,~7 - lea edx,[edx+ebx*4] // advance dest pointer - test [edx+0],eax // fetch destination to L1 cache - cmp ecx,4 - jl skip4 - mov [edx+0],eax - mov [edx+4],eax - mov [edx+8],eax - mov [edx+12],eax - add edx,16 - sub ecx,4 -skip4: cmp ecx,2 - jl skip2 - mov [edx+0],eax - mov [edx+4],eax - add edx,8 - sub ecx,2 -skip2: cmp ecx,1 - jl outta - mov [edx+0],eax -outta: - } -} - -// optimized memory copy routine that handles all alignment -// cases and block sizes efficiently -void Com_Memcpy (void* dest, const void* src, const size_t count) { - Com_Prefetch (src, count, PRE_READ); - __asm - { - push edi - push esi - mov ecx,count - cmp ecx,0 // count = 0 check (just to be on the safe side) - je outta - mov edx,dest - mov ebx,src - cmp ecx,32 // padding only? - jl padding - - mov edi,ecx - and edi,~31 // edi = count&~31 - sub edi,32 - - align 16 -loopMisAligned: - mov eax,[ebx + edi + 0 + 0*8] - mov esi,[ebx + edi + 4 + 0*8] - mov [edx+edi+0 + 0*8],eax - mov [edx+edi+4 + 0*8],esi - mov eax,[ebx + edi + 0 + 1*8] - mov esi,[ebx + edi + 4 + 1*8] - mov [edx+edi+0 + 1*8],eax - mov [edx+edi+4 + 1*8],esi - mov eax,[ebx + edi + 0 + 2*8] - mov esi,[ebx + edi + 4 + 2*8] - mov [edx+edi+0 + 2*8],eax - mov [edx+edi+4 + 2*8],esi - mov eax,[ebx + edi + 0 + 3*8] - mov esi,[ebx + edi + 4 + 3*8] - mov [edx+edi+0 + 3*8],eax - mov [edx+edi+4 + 3*8],esi - sub edi,32 - jge loopMisAligned - - mov edi,ecx - and edi,~31 - add ebx,edi // increase src pointer - add edx,edi // increase dst pointer - and ecx,31 // new count - jz outta // if count = 0, get outta here - -padding: - cmp ecx,16 - jl skip16 - mov eax,dword ptr [ebx] - mov dword ptr [edx],eax - mov eax,dword ptr [ebx+4] - mov dword ptr [edx+4],eax - mov eax,dword ptr [ebx+8] - mov dword ptr [edx+8],eax - mov eax,dword ptr [ebx+12] - mov dword ptr [edx+12],eax - sub ecx,16 - add ebx,16 - add edx,16 -skip16: - cmp ecx,8 - jl skip8 - mov eax,dword ptr [ebx] - mov dword ptr [edx],eax - mov eax,dword ptr [ebx+4] - sub ecx,8 - mov dword ptr [edx+4],eax - add ebx,8 - add edx,8 -skip8: - cmp ecx,4 - jl skip4 - mov eax,dword ptr [ebx] // here 4-7 bytes - add ebx,4 - sub ecx,4 - mov dword ptr [edx],eax - add edx,4 -skip4: // 0-3 remaining bytes - cmp ecx,2 - jl skip2 - mov ax,word ptr [ebx] // two bytes - cmp ecx,3 // less than 3? - mov word ptr [edx],ax - jl outta - mov al,byte ptr [ebx+2] // last byte - mov byte ptr [edx+2],al - jmp outta -skip2: - cmp ecx,1 - jl outta - mov al,byte ptr [ebx] - mov byte ptr [edx],al -outta: - pop esi - pop edi - } -} - -void Com_Memset (void* dest, const int val, const size_t count) -{ - unsigned int fillval; - - if (count < 8) - { - __asm - { - mov edx,dest - mov eax, val - mov ah,al - mov ebx,eax - and ebx, 0xffff - shl eax,16 - add eax,ebx // eax now contains pattern - mov ecx,count - cmp ecx,4 - jl skip4 - mov [edx],eax // copy first dword - add edx,4 - sub ecx,4 - skip4: cmp ecx,2 - jl skip2 - mov word ptr [edx],ax // copy 2 bytes - add edx,2 - sub ecx,2 - skip2: cmp ecx,0 - je skip1 - mov byte ptr [edx],al // copy single byte - skip1: - } - return; - } - - fillval = val; - - fillval = fillval|(fillval<<8); - fillval = fillval|(fillval<<16); // fill dword with 8-bit pattern - - _copyDWord ((unsigned int*)(dest),fillval, count/4); - - __asm // padding of 0-3 bytes - { - mov ecx,count - mov eax,ecx - and ecx,3 - jz skipA - and eax,~3 - mov ebx,dest - add ebx,eax - mov eax,fillval - cmp ecx,2 - jl skipB - mov word ptr [ebx],ax - cmp ecx,2 - je skipA - mov byte ptr [ebx+2],al - jmp skipA -skipB: - cmp ecx,0 - je skipA - mov byte ptr [ebx],al -skipA: - } -} - -qboolean Com_Memcmp (const void *src0, const void *src1, const unsigned int count) -{ - unsigned int i; - // MMX version anyone? - - if (count >= 16) - { - unsigned int *dw = (unsigned int*)(src0); - unsigned int *sw = (unsigned int*)(src1); - - unsigned int nm2 = count/16; - for (i = 0; i < nm2; i+=4) - { - unsigned int tmp = (dw[i+0]-sw[i+0])|(dw[i+1]-sw[i+1])| - (dw[i+2]-sw[i+2])|(dw[i+3]-sw[i+3]); - if (tmp) - return qfalse; - } - } - if (count & 15) - { - byte *d = (byte*)src0; - byte *s = (byte*)src1; - for (i = count & 0xfffffff0; i < count; i++) - if (d[i]!=s[i]) - return qfalse; - } - - return qtrue; -} - -void Com_Prefetch (const void *s, const unsigned int bytes, e_prefetch type) -{ - // write buffer prefetching is performed only if - // the processor benefits from it. Read and read/write - // prefetching is always performed. - - switch (type) - { - case PRE_WRITE : break; - case PRE_READ: - case PRE_READ_WRITE: - - __asm - { - mov ebx,s - mov ecx,bytes - cmp ecx,4096 // clamp to 4kB - jle skipClamp - mov ecx,4096 -skipClamp: - add ecx,0x1f - shr ecx,5 // number of cache lines - jz skip - jmp loopie - - align 16 - loopie: test byte ptr [ebx],al - add ebx,32 - dec ecx - jnz loopie - skip: - } - - break; - } -} -#endif -#endif -#endif // bk001208 - memset/memcpy assembly, Q_acos needed (RC4) -//------------------------------------------------------------------------ - - -/* -===================== -Q_acos - -the msvc acos doesn't always return a value between -PI and PI: - -int i; -i = 1065353246; -acos(*(float*) &i) == -1.#IND0 - - This should go in q_math but it is too late to add new traps - to game and ui -===================== -*/ -float Q_acos(float c) { - float angle; - - angle = acos(c); - - if (angle > M_PI) { - return (float)M_PI; - } - if (angle < -M_PI) { - return (float)M_PI; - } - return angle; -} - -/* -=========================================== -command line completion -=========================================== -*/ - -/* -================== -Field_Clear -================== -*/ -void Field_Clear( field_t *edit ) { - memset(edit->buffer, 0, MAX_EDIT_LINE); - edit->cursor = 0; - edit->scroll = 0; -} - -static const char *completionString; -static char shortestMatch[MAX_TOKEN_CHARS]; -static int matchCount; -// field we are working on, passed to Field_CompleteCommand (&g_consoleCommand for instance) -static field_t *completionField; - -/* -=============== -FindMatches - -=============== -*/ -static void FindMatches( const char *s ) { - int i; - - if ( Q_stricmpn( s, completionString, strlen( completionString ) ) ) { - return; - } - matchCount++; - if ( matchCount == 1 ) { - Q_strncpyz( shortestMatch, s, sizeof( shortestMatch ) ); - return; - } - - // cut shortestMatch to the amount common with s - for ( i = 0 ; s[i] ; i++ ) { - if ( tolower(shortestMatch[i]) != tolower(s[i]) ) { - shortestMatch[i] = 0; - } - } -} - -/* -=============== -PrintMatches - -=============== -*/ -static void PrintMatches( const char *s ) { - if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) { - Com_Printf( " %s\n", s ); - } -} - -static void keyConcatArgs( void ) { - int i; - char *arg; - - for ( i = 1 ; i < Cmd_Argc() ; i++ ) { - Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " ); - arg = Cmd_Argv( i ); - while (*arg) { - if (*arg == ' ') { - Q_strcat( completionField->buffer, sizeof( completionField->buffer ), "\""); - break; - } - arg++; - } - Q_strcat( completionField->buffer, sizeof( completionField->buffer ), Cmd_Argv( i ) ); - if (*arg == ' ') { - Q_strcat( completionField->buffer, sizeof( completionField->buffer ), "\""); - } - } -} - -static void ConcatRemaining( const char *src, const char *start ) { - char *str; - - str = strstr(src, start); - if (!str) { - keyConcatArgs(); - return; - } - - str += strlen(start); - Q_strcat( completionField->buffer, sizeof( completionField->buffer ), str); -} - -/* -=============== -Field_CompleteCommand - -perform Tab expansion -NOTE TTimo this was originally client code only - moved to common code when writing tty console for *nix dedicated server -=============== -*/ -void Field_CompleteCommand( field_t *field ) { - field_t temp; - - completionField = field; - - // only look at the first token for completion purposes - Cmd_TokenizeString( completionField->buffer ); - - completionString = Cmd_Argv(0); - if ( completionString[0] == '\\' || completionString[0] == '/' ) { - completionString++; - } - matchCount = 0; - shortestMatch[0] = 0; - - if ( strlen( completionString ) == 0 ) { - return; - } - - Cmd_CommandCompletion( FindMatches ); - Cvar_CommandCompletion( FindMatches ); - - if ( matchCount == 0 ) { - return; // no matches - } - - Com_Memcpy(&temp, completionField, sizeof(field_t)); - - if ( matchCount == 1 ) { - Com_sprintf( completionField->buffer, sizeof( completionField->buffer ), "\\%s", shortestMatch ); - if ( Cmd_Argc() == 1 ) { - Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " ); - } else { - ConcatRemaining( temp.buffer, completionString ); - } - completionField->cursor = strlen( completionField->buffer ); - return; - } - - // multiple matches, complete to shortest - Com_sprintf( completionField->buffer, sizeof( completionField->buffer ), "\\%s", shortestMatch ); - completionField->cursor = strlen( completionField->buffer ); - ConcatRemaining( temp.buffer, completionString ); - - Com_Printf( "]%s\n", completionField->buffer ); - - // run through again, printing matches - Cmd_CommandCompletion( PrintMatches ); - Cvar_CommandCompletion( PrintMatches ); -} diff --git a/tools/quake3/bspc/deps/qcommon/cvar.c b/tools/quake3/bspc/deps/qcommon/cvar.c deleted file mode 100644 index f954f0d4..00000000 --- a/tools/quake3/bspc/deps/qcommon/cvar.c +++ /dev/null @@ -1,906 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// cvar.c -- dynamic variable tracking - -#include "q_shared.h" -#include "qcommon.h" - -cvar_t *cvar_vars; -cvar_t *cvar_cheats; -int cvar_modifiedFlags; - -#define MAX_CVARS 1024 -cvar_t cvar_indexes[MAX_CVARS]; -int cvar_numIndexes; - -#define FILE_HASH_SIZE 256 -static cvar_t* hashTable[FILE_HASH_SIZE]; - -cvar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force); - -/* -================ -return a hash value for the filename -================ -*/ -static long generateHashValue( const char *fname ) { - int i; - long hash; - char letter; - - hash = 0; - i = 0; - while (fname[i] != '\0') { - letter = tolower(fname[i]); - hash+=(long)(letter)*(i+119); - i++; - } - hash &= (FILE_HASH_SIZE-1); - return hash; -} - -/* -============ -Cvar_ValidateString -============ -*/ -static qboolean Cvar_ValidateString( const char *s ) { - if ( !s ) { - return qfalse; - } - if ( strchr( s, '\\' ) ) { - return qfalse; - } - if ( strchr( s, '\"' ) ) { - return qfalse; - } - if ( strchr( s, ';' ) ) { - return qfalse; - } - return qtrue; -} - -/* -============ -Cvar_FindVar -============ -*/ -static cvar_t *Cvar_FindVar( const char *var_name ) { - cvar_t *var; - long hash; - - hash = generateHashValue(var_name); - - for (var=hashTable[hash] ; var ; var=var->hashNext) { - if (!Q_stricmp(var_name, var->name)) { - return var; - } - } - - return NULL; -} - -/* -============ -Cvar_VariableValue -============ -*/ -float Cvar_VariableValue( const char *var_name ) { - cvar_t *var; - - var = Cvar_FindVar (var_name); - if (!var) - return 0; - return var->value; -} - - -/* -============ -Cvar_VariableIntegerValue -============ -*/ -int Cvar_VariableIntegerValue( const char *var_name ) { - cvar_t *var; - - var = Cvar_FindVar (var_name); - if (!var) - return 0; - return var->integer; -} - - -/* -============ -Cvar_VariableString -============ -*/ -char *Cvar_VariableString( const char *var_name ) { - cvar_t *var; - - var = Cvar_FindVar (var_name); - if (!var) - return ""; - return var->string; -} - - -/* -============ -Cvar_VariableStringBuffer -============ -*/ -void Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) { - cvar_t *var; - - var = Cvar_FindVar (var_name); - if (!var) { - *buffer = 0; - } - else { - Q_strncpyz( buffer, var->string, bufsize ); - } -} - - -/* -============ -Cvar_CommandCompletion -============ -*/ -void Cvar_CommandCompletion( void(*callback)(const char *s) ) { - cvar_t *cvar; - - for ( cvar = cvar_vars ; cvar ; cvar = cvar->next ) { - callback( cvar->name ); - } -} - - -/* -============ -Cvar_Get - -If the variable already exists, the value will not be set unless CVAR_ROM -The flags will be or'ed in if the variable exists. -============ -*/ -cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { - cvar_t *var; - long hash; - - if ( !var_name || ! var_value ) { - Com_Error( ERR_FATAL, "Cvar_Get: NULL parameter" ); - } - - if ( !Cvar_ValidateString( var_name ) ) { - Com_Printf("invalid cvar name string: %s\n", var_name ); - var_name = "BADNAME"; - } - -#if 0 // FIXME: values with backslash happen - if ( !Cvar_ValidateString( var_value ) ) { - Com_Printf("invalid cvar value string: %s\n", var_value ); - var_value = "BADVALUE"; - } -#endif - - var = Cvar_FindVar (var_name); - if ( var ) { - // if the C code is now specifying a variable that the user already - // set a value for, take the new value as the reset value - if ( ( var->flags & CVAR_USER_CREATED ) && !( flags & CVAR_USER_CREATED ) - && var_value[0] ) { - var->flags &= ~CVAR_USER_CREATED; - Z_Free( var->resetString ); - var->resetString = CopyString( var_value ); - - // ZOID--needs to be set so that cvars the game sets as - // SERVERINFO get sent to clients - cvar_modifiedFlags |= flags; - } - - var->flags |= flags; - // only allow one non-empty reset string without a warning - if ( !var->resetString[0] ) { - // we don't have a reset string yet - Z_Free( var->resetString ); - var->resetString = CopyString( var_value ); - } else if ( var_value[0] && strcmp( var->resetString, var_value ) ) { - Com_DPrintf( "Warning: cvar \"%s\" given initial values: \"%s\" and \"%s\"\n", - var_name, var->resetString, var_value ); - } - // if we have a latched string, take that value now - if ( var->latchedString ) { - char *s; - - s = var->latchedString; - var->latchedString = NULL; // otherwise cvar_set2 would free it - Cvar_Set2( var_name, s, qtrue ); - Z_Free( s ); - } - -// use a CVAR_SET for rom sets, get won't override -#if 0 - // CVAR_ROM always overrides - if ( flags & CVAR_ROM ) { - Cvar_Set2( var_name, var_value, qtrue ); - } -#endif - return var; - } - - // - // allocate a new cvar - // - if ( cvar_numIndexes >= MAX_CVARS ) { - Com_Error( ERR_FATAL, "MAX_CVARS" ); - } - var = &cvar_indexes[cvar_numIndexes]; - cvar_numIndexes++; - var->name = CopyString (var_name); - var->string = CopyString (var_value); - var->modified = qtrue; - var->modificationCount = 1; - var->value = atof (var->string); - var->integer = atoi(var->string); - var->resetString = CopyString( var_value ); - - // link the variable in - var->next = cvar_vars; - cvar_vars = var; - - var->flags = flags; - - hash = generateHashValue(var_name); - var->hashNext = hashTable[hash]; - hashTable[hash] = var; - - return var; -} - -/* -============ -Cvar_Set2 -============ -*/ -cvar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force ) { - cvar_t *var; - - Com_DPrintf( "Cvar_Set2: %s %s\n", var_name, value ); - - if ( !Cvar_ValidateString( var_name ) ) { - Com_Printf("invalid cvar name string: %s\n", var_name ); - var_name = "BADNAME"; - } - -#if 0 // FIXME - if ( value && !Cvar_ValidateString( value ) ) { - Com_Printf("invalid cvar value string: %s\n", value ); - var_value = "BADVALUE"; - } -#endif - - var = Cvar_FindVar (var_name); - if (!var) { - if ( !value ) { - return NULL; - } - // create it - if ( !force ) { - return Cvar_Get( var_name, value, CVAR_USER_CREATED ); - } else { - return Cvar_Get (var_name, value, 0); - } - } - - if (!value ) { - value = var->resetString; - } - - if (!strcmp(value,var->string)) { - return var; - } - // note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo) - cvar_modifiedFlags |= var->flags; - - if (!force) - { - if (var->flags & CVAR_ROM) - { - Com_Printf ("%s is read only.\n", var_name); - return var; - } - - if (var->flags & CVAR_INIT) - { - Com_Printf ("%s is write protected.\n", var_name); - return var; - } - - if (var->flags & CVAR_LATCH) - { - if (var->latchedString) - { - if (strcmp(value, var->latchedString) == 0) - return var; - Z_Free (var->latchedString); - } - else - { - if (strcmp(value, var->string) == 0) - return var; - } - - Com_Printf ("%s will be changed upon restarting.\n", var_name); - var->latchedString = CopyString(value); - var->modified = qtrue; - var->modificationCount++; - return var; - } - - if ( (var->flags & CVAR_CHEAT) && !cvar_cheats->integer ) - { - Com_Printf ("%s is cheat protected.\n", var_name); - return var; - } - - } - else - { - if (var->latchedString) - { - Z_Free (var->latchedString); - var->latchedString = NULL; - } - } - - if (!strcmp(value, var->string)) - return var; // not changed - - var->modified = qtrue; - var->modificationCount++; - - Z_Free (var->string); // free the old value string - - var->string = CopyString(value); - var->value = atof (var->string); - var->integer = atoi (var->string); - - return var; -} - -/* -============ -Cvar_Set -============ -*/ -void Cvar_Set( const char *var_name, const char *value) { - Cvar_Set2 (var_name, value, qtrue); -} - -/* -============ -Cvar_SetLatched -============ -*/ -void Cvar_SetLatched( const char *var_name, const char *value) { - Cvar_Set2 (var_name, value, qfalse); -} - -/* -============ -Cvar_SetValue -============ -*/ -void Cvar_SetValue( const char *var_name, float value) { - char val[32]; - - if ( value == (int)value ) { - Com_sprintf (val, sizeof(val), "%i",(int)value); - } else { - Com_sprintf (val, sizeof(val), "%f",value); - } - Cvar_Set (var_name, val); -} - - -/* -============ -Cvar_Reset -============ -*/ -void Cvar_Reset( const char *var_name ) { - Cvar_Set2( var_name, NULL, qfalse ); -} - - -/* -============ -Cvar_SetCheatState - -Any testing variables will be reset to the safe values -============ -*/ -void Cvar_SetCheatState( void ) { - cvar_t *var; - - // set all default vars to the safe value - for ( var = cvar_vars ; var ; var = var->next ) { - if ( var->flags & CVAR_CHEAT ) { - // the CVAR_LATCHED|CVAR_CHEAT vars might escape the reset here - // because of a different var->latchedString - if (var->latchedString) - { - Z_Free(var->latchedString); - var->latchedString = NULL; - } - if (strcmp(var->resetString,var->string)) { - Cvar_Set( var->name, var->resetString ); - } - } - } -} - -/* -============ -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 qfalse; - } - - // perform a variable print or set - if ( Cmd_Argc() == 1 ) { - Com_Printf ("\"%s\" is:\"%s" S_COLOR_WHITE "\" default:\"%s" S_COLOR_WHITE "\"\n", v->name, v->string, v->resetString ); - if ( v->latchedString ) { - Com_Printf( "latched: \"%s\"\n", v->latchedString ); - } - return qtrue; - } - - // set the value if forcing isn't required - Cvar_Set2 (v->name, Cmd_Argv(1), qfalse); - return qtrue; -} - - -/* -============ -Cvar_Toggle_f - -Toggles a cvar for easy single key binding -============ -*/ -void Cvar_Toggle_f( void ) { - int v; - - if ( Cmd_Argc() != 2 ) { - Com_Printf ("usage: toggle \n"); - return; - } - - v = Cvar_VariableValue( Cmd_Argv( 1 ) ); - v = !v; - - Cvar_Set2 (Cmd_Argv(1), va("%i", v), qfalse); -} - -/* -============ -Cvar_Set_f - -Allows setting and defining of arbitrary cvars from console, even if they -weren't declared in C code. -============ -*/ -void Cvar_Set_f( void ) { - int i, c, l, len; - char combined[MAX_STRING_TOKENS]; - - c = Cmd_Argc(); - if ( c < 3 ) { - Com_Printf ("usage: set \n"); - return; - } - - combined[0] = 0; - l = 0; - for ( i = 2 ; i < c ; i++ ) { - len = strlen ( Cmd_Argv( i ) + 1 ); - if ( l + len >= MAX_STRING_TOKENS - 2 ) { - break; - } - strcat( combined, Cmd_Argv( i ) ); - if ( i != c-1 ) { - strcat( combined, " " ); - } - l += len; - } - Cvar_Set2 (Cmd_Argv(1), combined, qfalse); -} - -/* -============ -Cvar_SetU_f - -As Cvar_Set, but also flags it as userinfo -============ -*/ -void Cvar_SetU_f( void ) { - cvar_t *v; - - if ( Cmd_Argc() != 3 ) { - Com_Printf ("usage: setu \n"); - return; - } - Cvar_Set_f(); - v = Cvar_FindVar( Cmd_Argv( 1 ) ); - if ( !v ) { - return; - } - v->flags |= CVAR_USERINFO; -} - -/* -============ -Cvar_SetS_f - -As Cvar_Set, but also flags it as userinfo -============ -*/ -void Cvar_SetS_f( void ) { - cvar_t *v; - - if ( Cmd_Argc() != 3 ) { - Com_Printf ("usage: sets \n"); - return; - } - Cvar_Set_f(); - v = Cvar_FindVar( Cmd_Argv( 1 ) ); - if ( !v ) { - return; - } - v->flags |= CVAR_SERVERINFO; -} - -/* -============ -Cvar_SetA_f - -As Cvar_Set, but also flags it as archived -============ -*/ -void Cvar_SetA_f( void ) { - cvar_t *v; - - if ( Cmd_Argc() != 3 ) { - Com_Printf ("usage: seta \n"); - return; - } - Cvar_Set_f(); - v = Cvar_FindVar( Cmd_Argv( 1 ) ); - if ( !v ) { - return; - } - v->flags |= CVAR_ARCHIVE; -} - -/* -============ -Cvar_Reset_f -============ -*/ -void Cvar_Reset_f( void ) { - if ( Cmd_Argc() != 2 ) { - Com_Printf ("usage: reset \n"); - return; - } - Cvar_Reset( Cmd_Argv( 1 ) ); -} - -/* -============ -Cvar_WriteVariables - -Appends lines containing "set variable value" for all variables -with the archive flag set to qtrue. -============ -*/ -void Cvar_WriteVariables( fileHandle_t f ) { - cvar_t *var; - char buffer[1024]; - - for (var = cvar_vars ; var ; var = var->next) { - if( Q_stricmp( var->name, "cl_cdkey" ) == 0 ) { - continue; - } - if( var->flags & CVAR_ARCHIVE ) { - // write the latched value, even if it hasn't taken effect yet - if ( var->latchedString ) { - Com_sprintf (buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->latchedString); - } else { - Com_sprintf (buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->string); - } - FS_Printf (f, "%s", buffer); - } - } -} - -/* -============ -Cvar_List_f -============ -*/ -void Cvar_List_f( void ) { - cvar_t *var; - int i; - char *match; - - if ( Cmd_Argc() > 1 ) { - match = Cmd_Argv( 1 ); - } else { - match = NULL; - } - - i = 0; - for (var = cvar_vars ; var ; var = var->next, i++) - { - if (match && !Com_Filter(match, var->name, qfalse)) continue; - - if (var->flags & CVAR_SERVERINFO) { - Com_Printf("S"); - } else { - Com_Printf(" "); - } - if (var->flags & CVAR_USERINFO) { - Com_Printf("U"); - } else { - Com_Printf(" "); - } - if (var->flags & CVAR_ROM) { - Com_Printf("R"); - } else { - Com_Printf(" "); - } - if (var->flags & CVAR_INIT) { - Com_Printf("I"); - } else { - Com_Printf(" "); - } - if (var->flags & CVAR_ARCHIVE) { - Com_Printf("A"); - } else { - Com_Printf(" "); - } - if (var->flags & CVAR_LATCH) { - Com_Printf("L"); - } else { - Com_Printf(" "); - } - if (var->flags & CVAR_CHEAT) { - Com_Printf("C"); - } else { - Com_Printf(" "); - } - - Com_Printf (" %s \"%s\"\n", var->name, var->string); - } - - Com_Printf ("\n%i total cvars\n", i); - Com_Printf ("%i cvar indexes\n", cvar_numIndexes); -} - -/* -============ -Cvar_Restart_f - -Resets all cvars to their hardcoded values -============ -*/ -void Cvar_Restart_f( void ) { - cvar_t *var; - cvar_t **prev; - - prev = &cvar_vars; - while ( 1 ) { - var = *prev; - if ( !var ) { - break; - } - - // don't mess with rom values, or some inter-module - // communication will get broken (com_cl_running, etc) - if ( var->flags & ( CVAR_ROM | CVAR_INIT | CVAR_NORESTART ) ) { - prev = &var->next; - continue; - } - - // throw out any variables the user created - if ( var->flags & CVAR_USER_CREATED ) { - *prev = var->next; - if ( var->name ) { - Z_Free( var->name ); - } - if ( var->string ) { - Z_Free( var->string ); - } - if ( var->latchedString ) { - Z_Free( var->latchedString ); - } - if ( var->resetString ) { - Z_Free( var->resetString ); - } - // clear the var completely, since we - // can't remove the index from the list - Com_Memset( var, 0, sizeof( var ) ); - continue; - } - - Cvar_Set( var->name, var->resetString ); - - prev = &var->next; - } -} - - - -/* -===================== -Cvar_InfoString -===================== -*/ -char *Cvar_InfoString( int bit ) { - static char info[MAX_INFO_STRING]; - cvar_t *var; - - info[0] = 0; - - for (var = cvar_vars ; var ; var = var->next) { - if (var->flags & bit) { - Info_SetValueForKey (info, var->name, var->string); - } - } - return info; -} - -/* -===================== -Cvar_InfoString_Big - - handles large info strings ( CS_SYSTEMINFO ) -===================== -*/ -char *Cvar_InfoString_Big( int bit ) { - static char info[BIG_INFO_STRING]; - cvar_t *var; - - info[0] = 0; - - for (var = cvar_vars ; var ; var = var->next) { - if (var->flags & bit) { - Info_SetValueForKey_Big (info, var->name, var->string); - } - } - return info; -} - - - -/* -===================== -Cvar_InfoStringBuffer -===================== -*/ -void Cvar_InfoStringBuffer( int bit, char* buff, int buffsize ) { - Q_strncpyz(buff,Cvar_InfoString(bit),buffsize); -} - -/* -===================== -Cvar_Register - -basically a slightly modified Cvar_Get for the interpreted modules -===================== -*/ -void Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) { - cvar_t *cv; - - cv = Cvar_Get( varName, defaultValue, flags ); - if ( !vmCvar ) { - return; - } - vmCvar->handle = cv - cvar_indexes; - vmCvar->modificationCount = -1; - Cvar_Update( vmCvar ); -} - - -/* -===================== -Cvar_Register - -updates an interpreted modules' version of a cvar -===================== -*/ -void Cvar_Update( vmCvar_t *vmCvar ) { - cvar_t *cv = NULL; // bk001129 - assert(vmCvar); // bk - - if ( (unsigned)vmCvar->handle >= cvar_numIndexes ) { - Com_Error( ERR_DROP, "Cvar_Update: handle out of range" ); - } - - cv = cvar_indexes + vmCvar->handle; - - if ( cv->modificationCount == vmCvar->modificationCount ) { - return; - } - if ( !cv->string ) { - return; // variable might have been cleared by a cvar_restart - } - vmCvar->modificationCount = cv->modificationCount; - // bk001129 - mismatches. - if ( strlen(cv->string)+1 > MAX_CVAR_VALUE_STRING ) - Com_Error( ERR_DROP, "Cvar_Update: src %s length %d exceeds MAX_CVAR_VALUE_STRING", - cv->string, - strlen(cv->string), - sizeof(vmCvar->string) ); - // bk001212 - Q_strncpyz guarantees zero padding and dest[MAX_CVAR_VALUE_STRING-1]==0 - // bk001129 - paranoia. Never trust the destination string. - // bk001129 - beware, sizeof(char*) is always 4 (for cv->string). - // sizeof(vmCvar->string) always MAX_CVAR_VALUE_STRING - //Q_strncpyz( vmCvar->string, cv->string, sizeof( vmCvar->string ) ); // id - Q_strncpyz( vmCvar->string, cv->string, MAX_CVAR_VALUE_STRING ); - - vmCvar->value = cv->value; - vmCvar->integer = cv->integer; -} - - -/* -============ -Cvar_Init - -Reads in all archived cvars -============ -*/ -void Cvar_Init (void) { - cvar_cheats = Cvar_Get("sv_cheats", "1", CVAR_ROM | CVAR_SYSTEMINFO ); - - Cmd_AddCommand ("toggle", Cvar_Toggle_f); - Cmd_AddCommand ("set", Cvar_Set_f); - Cmd_AddCommand ("sets", Cvar_SetS_f); - Cmd_AddCommand ("setu", Cvar_SetU_f); - Cmd_AddCommand ("seta", Cvar_SetA_f); - Cmd_AddCommand ("reset", Cvar_Reset_f); - Cmd_AddCommand ("cvarlist", Cvar_List_f); - Cmd_AddCommand ("cvar_restart", Cvar_Restart_f); -} diff --git a/tools/quake3/bspc/deps/qcommon/files.c b/tools/quake3/bspc/deps/qcommon/files.c deleted file mode 100644 index ba4080ad..00000000 --- a/tools/quake3/bspc/deps/qcommon/files.c +++ /dev/null @@ -1,3419 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -/***************************************************************************** - * name: files.c - * - * desc: handle based filesystem for Quake III Arena - * - * $Archive: /MissionPack/code/qcommon/files.c $ - * - *****************************************************************************/ - - -#include "q_shared.h" -#include "qcommon.h" -#include "unzip.h" - -/* -============================================================================= - -QUAKE3 FILESYSTEM - -All of Quake's data access is through a hierarchical file system, but the contents of -the file system can be transparently merged from several sources. - -A "qpath" is a reference to game file data. MAX_ZPATH is 256 characters, which must include -a terminating zero. "..", "\\", and ":" are explicitly illegal in qpaths to prevent any -references outside the quake directory system. - -The "base path" is the path to the directory holding all the game directories and usually -the executable. It defaults to ".", but can be overridden with a "+set fs_basepath c:\quake3" -command line to allow code debugging in a different directory. Basepath cannot -be modified at all after startup. Any files that are created (demos, screenshots, -etc) will be created reletive to the base path, so base path should usually be writable. - -The "cd path" is the path to an alternate hierarchy that will be searched if a file -is not located in the base path. A user can do a partial install that copies some -data to a base path created on their hard drive and leave the rest on the cd. Files -are never writen to the cd path. It defaults to a value set by the installer, like -"e:\quake3", but it can be overridden with "+set ds_cdpath g:\quake3". - -If a user runs the game directly from a CD, the base path would be on the CD. This -should still function correctly, but all file writes will fail (harmlessly). - -The "home path" is the path used for all write access. On win32 systems we have "base path" -== "home path", but on *nix systems the base installation is usually readonly, and -"home path" points to ~/.q3a or similar - -The user can also install custom mods and content in "home path", so it should be searched -along with "home path" and "cd path" for game content. - - -The "base game" is the directory under the paths where data comes from by default, and -can be either "baseq3" or "demoq3". - -The "current game" may be the same as the base game, or it may be the name of another -directory under the paths that should be searched for files before looking in the base game. -This is the basis for addons. - -Clients automatically set the game directory after receiving a gamestate from a server, -so only servers need to worry about +set fs_game. - -No other directories outside of the base game and current game will ever be referenced by -filesystem functions. - -To save disk space and speed loading, directory trees can be collapsed into zip files. -The files use a ".pk3" extension to prevent users from unzipping them accidentally, but -otherwise the are simply normal uncompressed zip files. A game directory can have multiple -zip files of the form "pak0.pk3", "pak1.pk3", etc. Zip files are searched in decending order -from the highest number to the lowest, and will always take precedence over the filesystem. -This allows a pk3 distributed as a patch to override all existing data. - -Because we will have updated executables freely available online, there is no point to -trying to restrict demo / oem versions of the game with code changes. Demo / oem versions -should be exactly the same executables as release versions, but with different data that -automatically restricts where game media can come from to prevent add-ons from working. - -After the paths are initialized, quake will look for the product.txt file. If not -found and verified, the game will run in restricted mode. In restricted mode, only -files contained in demoq3/pak0.pk3 will be available for loading, and only if the zip header is -verified to not have been modified. A single exception is made for q3config.cfg. Files -can still be written out in restricted mode, so screenshots and demos are allowed. -Restricted mode can be tested by setting "+set fs_restrict 1" on the command line, even -if there is a valid product.txt under the basepath or cdpath. - -If not running in restricted mode, and a file is not found in any local filesystem, -an attempt will be made to download it and save it under the base path. - -If the "fs_copyfiles" cvar is set to 1, then every time a file is sourced from the cd -path, it will be copied over to the base path. This is a development aid to help build -test releases and to copy working sets over slow network links. - -File search order: when FS_FOpenFileRead gets called it will go through the fs_searchpaths -structure and stop on the first successful hit. fs_searchpaths is built with successive -calls to FS_AddGameDirectory - -Additionaly, we search in several subdirectories: -current game is the current mode -base game is a variable to allow mods based on other mods -(such as baseq3 + missionpack content combination in a mod for instance) -BASEGAME is the hardcoded base game ("baseq3") - -e.g. the qpath "sound/newstuff/test.wav" would be searched for in the following places: - -home path + current game's zip files -home path + current game's directory -base path + current game's zip files -base path + current game's directory -cd path + current game's zip files -cd path + current game's directory - -home path + base game's zip file -home path + base game's directory -base path + base game's zip file -base path + base game's directory -cd path + base game's zip file -cd path + base game's directory - -home path + BASEGAME's zip file -home path + BASEGAME's directory -base path + BASEGAME's zip file -base path + BASEGAME's directory -cd path + BASEGAME's zip file -cd path + BASEGAME's directory - -server download, to be written to home path + current game's directory - - -The filesystem can be safely shutdown and reinitialized with different -basedir / cddir / game combinations, but all other subsystems that rely on it -(sound, video) must also be forced to restart. - -Because the same files are loaded by both the clip model (CM_) and renderer (TR_) -subsystems, a simple single-file caching scheme is used. The CM_ subsystems will -load the file with a request to cache. Only one file will be kept cached at a time, -so any models that are going to be referenced by both subsystems should alternate -between the CM_ load function and the ref load function. - -TODO: A qpath that starts with a leading slash will always refer to the base game, even if another -game is currently active. This allows character models, skins, and sounds to be downloaded -to a common directory no matter which game is active. - -How to prevent downloading zip files? -Pass pk3 file names in systeminfo, and download before FS_Restart()? - -Aborting a download disconnects the client from the server. - -How to mark files as downloadable? Commercial add-ons won't be downloadable. - -Non-commercial downloads will want to download the entire zip file. -the game would have to be reset to actually read the zip in - -Auto-update information - -Path separators - -Casing - - separate server gamedir and client gamedir, so if the user starts - a local game after having connected to a network game, it won't stick - with the network game. - - allow menu options for game selection? - -Read / write config to floppy option. - -Different version coexistance? - -When building a pak file, make sure a q3config.cfg isn't present in it, -or configs will never get loaded from disk! - - todo: - - downloading (outside fs?) - game directory passing and restarting - -============================================================================= - -*/ - -#define DEMOGAME "demota" - -// every time a new demo pk3 file is built, this checksum must be updated. -// the easiest way to get it is to just run the game and see what it spits out -#define DEMO_PAK_CHECKSUM 437558517u - -// if this is defined, the executable positively won't work with any paks other -// than the demo pak, even if productid is present. This is only used for our -// last demo release to prevent the mac and linux users from using the demo -// executable with the production windows pak before the mac/linux products -// hit the shelves a little later -// NOW defined in build files -//#define PRE_RELEASE_TADEMO - -#define MAX_ZPATH 256 -#define MAX_SEARCH_PATHS 4096 -#define MAX_FILEHASH_SIZE 1024 - -typedef struct fileInPack_s { - char *name; // name of the file - unsigned long pos; // file info position in zip - struct fileInPack_s* next; // next file in the hash -} fileInPack_t; - -typedef struct { - char pakFilename[MAX_OSPATH]; // c:\quake3\baseq3\pak0.pk3 - char pakBasename[MAX_OSPATH]; // pak0 - char pakGamename[MAX_OSPATH]; // baseq3 - unzFile handle; // handle to zip file - int checksum; // regular checksum - int pure_checksum; // checksum for pure - int numfiles; // number of files in pk3 - int referenced; // referenced file flags - int hashSize; // hash table size (power of 2) - fileInPack_t* *hashTable; // hash table - fileInPack_t* buildBuffer; // buffer with the filenames etc. -} pack_t; - -typedef struct { - char path[MAX_OSPATH]; // c:\quake3 - char gamedir[MAX_OSPATH]; // baseq3 -} directory_t; - -typedef struct searchpath_s { - struct searchpath_s *next; - - pack_t *pack; // only one of pack / dir will be non NULL - directory_t *dir; -} searchpath_t; - -static char fs_gamedir[MAX_OSPATH]; // this will be a single file name with no separators -static cvar_t *fs_debug; -static cvar_t *fs_homepath; -static cvar_t *fs_basepath; -static cvar_t *fs_basegame; -static cvar_t *fs_cdpath; -static cvar_t *fs_copyfiles; -static cvar_t *fs_gamedirvar; -static cvar_t *fs_restrict; -static searchpath_t *fs_searchpaths; -static int fs_readCount; // total bytes read -static int fs_loadCount; // total files read -static int fs_loadStack; // total files in memory -static int fs_packFiles; // total number of files in packs - -static int fs_fakeChkSum; -static int fs_checksumFeed; - -typedef union qfile_gus { - FILE* o; - unzFile z; -} qfile_gut; - -typedef struct qfile_us { - qfile_gut file; - qboolean unique; -} qfile_ut; - -typedef struct { - qfile_ut handleFiles; - qboolean handleSync; - int baseOffset; - int fileSize; - int zipFilePos; - qboolean zipFile; - qboolean streamed; - char name[MAX_ZPATH]; -} fileHandleData_t; - -static fileHandleData_t fsh[MAX_FILE_HANDLES]; - -// TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=540 -// wether we did a reorder on the current search path when joining the server -static qboolean fs_reordered; - -// never load anything from pk3 files that are not present at the server when pure -static int fs_numServerPaks; -static int fs_serverPaks[MAX_SEARCH_PATHS]; // checksums -static char *fs_serverPakNames[MAX_SEARCH_PATHS]; // pk3 names - -// only used for autodownload, to make sure the client has at least -// all the pk3 files that are referenced at the server side -static int fs_numServerReferencedPaks; -static int fs_serverReferencedPaks[MAX_SEARCH_PATHS]; // checksums -static char *fs_serverReferencedPakNames[MAX_SEARCH_PATHS]; // pk3 names - -// last valid game folder used -char lastValidBase[MAX_OSPATH]; -char lastValidGame[MAX_OSPATH]; - -// productId: This file is copyright 1999 Id Software, and may not be duplicated except during a licensed installation of the full commercial version of Quake 3:Arena -static byte fs_scrambledProductId[152] = { -220, 129, 255, 108, 244, 163, 171, 55, 133, 65, 199, 36, 140, 222, 53, 99, 65, 171, 175, 232, 236, 193, 210, 250, 169, 104, 231, 231, 21, 201, 170, 208, 135, 175, 130, 136, 85, 215, 71, 23, 96, 32, 96, 83, 44, 240, 219, 138, 184, 215, 73, 27, 196, 247, 55, 139, 148, 68, 78, 203, 213, 238, 139, 23, 45, 205, 118, 186, 236, 230, 231, 107, 212, 1, 10, 98, 30, 20, 116, 180, 216, 248, 166, 35, 45, 22, 215, 229, 35, 116, 250, 167, 117, 3, 57, 55, 201, 229, 218, 222, 128, 12, 141, 149, 32, 110, 168, 215, 184, 53, 31, 147, 62, 12, 138, 67, 132, 54, 125, 6, 221, 148, 140, 4, 21, 44, 198, 3, 126, 12, 100, 236, 61, 42, 44, 251, 15, 135, 14, 134, 89, 92, 177, 246, 152, 106, 124, 78, 118, 80, 28, 42 -}; - -#ifdef FS_MISSING -FILE* missingFiles = NULL; -#endif - -/* -============== -FS_Initialized -============== -*/ - -qboolean FS_Initialized() { - return (fs_searchpaths != NULL); -} - -/* -================= -FS_PakIsPure -================= -*/ -qboolean FS_PakIsPure( pack_t *pack ) { - int i; - - if ( fs_numServerPaks ) { - for ( i = 0 ; i < fs_numServerPaks ; i++ ) { - // FIXME: also use hashed file names - // NOTE TTimo: a pk3 with same checksum but different name would be validated too - // I don't see this as allowing for any exploit, it would only happen if the client does manips of it's file names 'not a bug' - if ( pack->checksum == fs_serverPaks[i] ) { - return qtrue; // on the aproved list - } - } - return qfalse; // not on the pure server pak list - } - return qtrue; -} - - -/* -================= -FS_LoadStack -return load stack -================= -*/ -int FS_LoadStack() -{ - return fs_loadStack; -} - -/* -================ -return a hash value for the filename -================ -*/ -static long FS_HashFileName( const char *fname, int hashSize ) { - int i; - long hash; - char letter; - - hash = 0; - i = 0; - while (fname[i] != '\0') { - letter = tolower(fname[i]); - if (letter =='.') break; // don't include extension - if (letter =='\\') letter = '/'; // damn path names - if (letter == PATH_SEP) letter = '/'; // damn path names - hash+=(long)(letter)*(i+119); - i++; - } - hash = (hash ^ (hash >> 10) ^ (hash >> 20)); - hash &= (hashSize-1); - return hash; -} - -static fileHandle_t FS_HandleForFile(void) { - int i; - - for ( i = 1 ; i < MAX_FILE_HANDLES ; i++ ) { - if ( fsh[i].handleFiles.file.o == NULL ) { - return i; - } - } - Com_Error( ERR_DROP, "FS_HandleForFile: none free" ); - return 0; -} - -static FILE *FS_FileForHandle( fileHandle_t f ) { - if ( f < 0 || f > MAX_FILE_HANDLES ) { - Com_Error( ERR_DROP, "FS_FileForHandle: out of reange" ); - } - if (fsh[f].zipFile == qtrue) { - Com_Error( ERR_DROP, "FS_FileForHandle: can't get FILE on zip file" ); - } - if ( ! fsh[f].handleFiles.file.o ) { - Com_Error( ERR_DROP, "FS_FileForHandle: NULL" ); - } - - return fsh[f].handleFiles.file.o; -} - -void FS_ForceFlush( fileHandle_t f ) { - FILE *file; - - file = FS_FileForHandle(f); - setvbuf( file, NULL, _IONBF, 0 ); -} - -/* -================ -FS_filelength - -If this is called on a non-unique FILE (from a pak file), -it will return the size of the pak file, not the expected -size of the file. -================ -*/ -int FS_filelength( fileHandle_t f ) { - int pos; - int end; - FILE* h; - - h = FS_FileForHandle(f); - pos = ftell (h); - fseek (h, 0, SEEK_END); - end = ftell (h); - fseek (h, pos, SEEK_SET); - - return end; -} - -/* -==================== -FS_ReplaceSeparators - -Fix things up differently for win/unix/mac -==================== -*/ -static void FS_ReplaceSeparators( char *path ) { - char *s; - - for ( s = path ; *s ; s++ ) { - if ( *s == '/' || *s == '\\' ) { - *s = PATH_SEP; - } - } -} - -/* -=================== -FS_BuildOSPath - -Qpath may have either forward or backwards slashes -=================== -*/ -char *FS_BuildOSPath( const char *base, const char *game, const char *qpath ) { - char temp[MAX_OSPATH]; - static char ospath[2][MAX_OSPATH]; - static int toggle; - - toggle ^= 1; // flip-flop to allow two returns without clash - - if( !game || !game[0] ) { - game = fs_gamedir; - } - - Com_sprintf( temp, sizeof(temp), "/%s/%s", game, qpath ); - FS_ReplaceSeparators( temp ); - Com_sprintf( ospath[toggle], sizeof( ospath[0] ), "%s%s", base, temp ); - - return ospath[toggle]; -} - - -/* -============ -FS_CreatePath - -Creates any directories needed to store the given filename -============ -*/ -static qboolean FS_CreatePath (char *OSPath) { - char *ofs; - - // make absolutely sure that it can't back up the path - // FIXME: is c: allowed??? - if ( strstr( OSPath, ".." ) || strstr( OSPath, "::" ) ) { - Com_Printf( "WARNING: refusing to create relative path \"%s\"\n", OSPath ); - return qtrue; - } - - for (ofs = OSPath+1 ; *ofs ; ofs++) { - if (*ofs == PATH_SEP) { - // create the directory - *ofs = 0; - Sys_Mkdir (OSPath); - *ofs = PATH_SEP; - } - } - return qfalse; -} - -/* -================= -FS_CopyFile - -Copy a fully specified file from one place to another -================= -*/ -static void FS_CopyFile( char *fromOSPath, char *toOSPath ) { - FILE *f; - int len; - byte *buf; - - Com_Printf( "copy %s to %s\n", fromOSPath, toOSPath ); - - if (strstr(fromOSPath, "journal.dat") || strstr(fromOSPath, "journaldata.dat")) { - Com_Printf( "Ignoring journal files\n"); - return; - } - - f = fopen( fromOSPath, "rb" ); - if ( !f ) { - return; - } - fseek (f, 0, SEEK_END); - len = ftell (f); - fseek (f, 0, SEEK_SET); - - // we are using direct malloc instead of Z_Malloc here, so it - // probably won't work on a mac... Its only for developers anyway... - buf = malloc( len ); - if (fread( buf, 1, len, f ) != len) - Com_Error( ERR_FATAL, "Short read in FS_Copyfiles()\n" ); - fclose( f ); - - if( FS_CreatePath( toOSPath ) ) { - return; - } - - f = fopen( toOSPath, "wb" ); - if ( !f ) { - return; - } - if (fwrite( buf, 1, len, f ) != len) - Com_Error( ERR_FATAL, "Short write in FS_Copyfiles()\n" ); - fclose( f ); - free( buf ); -} - -/* -=========== -FS_Remove - -=========== -*/ -static void FS_Remove( const char *osPath ) { - remove( osPath ); -} - -/* -================ -FS_FileExists - -Tests if the file exists in the current gamedir, this DOES NOT -search the paths. This is to determine if opening a file to write -(which always goes into the current gamedir) will cause any overwrites. -NOTE TTimo: this goes with FS_FOpenFileWrite for opening the file afterwards -================ -*/ -qboolean FS_FileExists( const char *file ) -{ - FILE *f; - char *testpath; - - testpath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, file ); - - f = fopen( testpath, "rb" ); - if (f) { - fclose( f ); - return qtrue; - } - return qfalse; -} - -/* -================ -FS_SV_FileExists - -Tests if the file exists -================ -*/ -qboolean FS_SV_FileExists( const char *file ) -{ - FILE *f; - char *testpath; - - testpath = FS_BuildOSPath( fs_homepath->string, file, ""); - testpath[strlen(testpath)-1] = '\0'; - - f = fopen( testpath, "rb" ); - if (f) { - fclose( f ); - return qtrue; - } - return qfalse; -} - - -/* -=========== -FS_SV_FOpenFileWrite - -=========== -*/ -fileHandle_t FS_SV_FOpenFileWrite( const char *filename ) { - char *ospath; - fileHandle_t f; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - ospath = FS_BuildOSPath( fs_homepath->string, filename, "" ); - ospath[strlen(ospath)-1] = '\0'; - - f = FS_HandleForFile(); - fsh[f].zipFile = qfalse; - - if ( fs_debug->integer ) { - Com_Printf( "FS_SV_FOpenFileWrite: %s\n", ospath ); - } - - if( FS_CreatePath( ospath ) ) { - return 0; - } - - Com_DPrintf( "writing to: %s\n", ospath ); - fsh[f].handleFiles.file.o = fopen( ospath, "wb" ); - - Q_strncpyz( fsh[f].name, filename, sizeof( fsh[f].name ) ); - - fsh[f].handleSync = qfalse; - if (!fsh[f].handleFiles.file.o) { - f = 0; - } - return f; -} - -/* -=========== -FS_SV_FOpenFileRead -search for a file somewhere below the home path, base path or cd path -we search in that order, matching FS_SV_FOpenFileRead order -=========== -*/ -int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp ) { - char *ospath; - fileHandle_t f = 0; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - f = FS_HandleForFile(); - fsh[f].zipFile = qfalse; - - Q_strncpyz( fsh[f].name, filename, sizeof( fsh[f].name ) ); - - // don't let sound stutter - S_ClearSoundBuffer(); - - // search homepath - ospath = FS_BuildOSPath( fs_homepath->string, filename, "" ); - // remove trailing slash - ospath[strlen(ospath)-1] = '\0'; - - if ( fs_debug->integer ) { - Com_Printf( "FS_SV_FOpenFileRead (fs_homepath): %s\n", ospath ); - } - - fsh[f].handleFiles.file.o = fopen( ospath, "rb" ); - fsh[f].handleSync = qfalse; - if (!fsh[f].handleFiles.file.o) - { - // NOTE TTimo on non *nix systems, fs_homepath == fs_basepath, might want to avoid - if (Q_stricmp(fs_homepath->string,fs_basepath->string)) - { - // search basepath - ospath = FS_BuildOSPath( fs_basepath->string, filename, "" ); - ospath[strlen(ospath)-1] = '\0'; - - if ( fs_debug->integer ) - { - Com_Printf( "FS_SV_FOpenFileRead (fs_basepath): %s\n", ospath ); - } - - fsh[f].handleFiles.file.o = fopen( ospath, "rb" ); - fsh[f].handleSync = qfalse; - - if ( !fsh[f].handleFiles.file.o ) - { - f = 0; - } - } - } - - if (!fsh[f].handleFiles.file.o) { - // search cd path - ospath = FS_BuildOSPath( fs_cdpath->string, filename, "" ); - ospath[strlen(ospath)-1] = '\0'; - - if (fs_debug->integer) - { - Com_Printf( "FS_SV_FOpenFileRead (fs_cdpath) : %s\n", ospath ); - } - - fsh[f].handleFiles.file.o = fopen( ospath, "rb" ); - fsh[f].handleSync = qfalse; - - if( !fsh[f].handleFiles.file.o ) { - f = 0; - } - } - - *fp = f; - if (f) { - return FS_filelength(f); - } - return 0; -} - - -/* -=========== -FS_SV_Rename - -=========== -*/ -void FS_SV_Rename( const char *from, const char *to ) { - char *from_ospath, *to_ospath; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - // don't let sound stutter - S_ClearSoundBuffer(); - - from_ospath = FS_BuildOSPath( fs_homepath->string, from, "" ); - to_ospath = FS_BuildOSPath( fs_homepath->string, to, "" ); - from_ospath[strlen(from_ospath)-1] = '\0'; - to_ospath[strlen(to_ospath)-1] = '\0'; - - if ( fs_debug->integer ) { - Com_Printf( "FS_SV_Rename: %s --> %s\n", from_ospath, to_ospath ); - } - - if (rename( from_ospath, to_ospath )) { - // Failed, try copying it and deleting the original - FS_CopyFile ( from_ospath, to_ospath ); - FS_Remove ( from_ospath ); - } -} - - - -/* -=========== -FS_Rename - -=========== -*/ -void FS_Rename( const char *from, const char *to ) { - char *from_ospath, *to_ospath; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - // don't let sound stutter - S_ClearSoundBuffer(); - - from_ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, from ); - to_ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, to ); - - if ( fs_debug->integer ) { - Com_Printf( "FS_Rename: %s --> %s\n", from_ospath, to_ospath ); - } - - if (rename( from_ospath, to_ospath )) { - // Failed, try copying it and deleting the original - FS_CopyFile ( from_ospath, to_ospath ); - FS_Remove ( from_ospath ); - } -} - -/* -============== -FS_FCloseFile - -If the FILE pointer is an open pak file, leave it open. - -For some reason, other dll's can't just cal fclose() -on files returned by FS_FOpenFile... -============== -*/ -void FS_FCloseFile( fileHandle_t f ) { - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - if (fsh[f].streamed) { - Sys_EndStreamedFile(f); - } - if (fsh[f].zipFile == qtrue) { - unzCloseCurrentFile( fsh[f].handleFiles.file.z ); - if ( fsh[f].handleFiles.unique ) { - unzClose( fsh[f].handleFiles.file.z ); - } - Com_Memset( &fsh[f], 0, sizeof( fsh[f] ) ); - return; - } - - // we didn't find it as a pak, so close it as a unique file - if (fsh[f].handleFiles.file.o) { - fclose (fsh[f].handleFiles.file.o); - } - Com_Memset( &fsh[f], 0, sizeof( fsh[f] ) ); -} - -/* -=========== -FS_FOpenFileWrite - -=========== -*/ -fileHandle_t FS_FOpenFileWrite( const char *filename ) { - char *ospath; - fileHandle_t f; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - f = FS_HandleForFile(); - fsh[f].zipFile = qfalse; - - ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, filename ); - - if ( fs_debug->integer ) { - Com_Printf( "FS_FOpenFileWrite: %s\n", ospath ); - } - - if( FS_CreatePath( ospath ) ) { - return 0; - } - - // enabling the following line causes a recursive function call loop - // when running with +set logfile 1 +set developer 1 - //Com_DPrintf( "writing to: %s\n", ospath ); - fsh[f].handleFiles.file.o = fopen( ospath, "wb" ); - - Q_strncpyz( fsh[f].name, filename, sizeof( fsh[f].name ) ); - - fsh[f].handleSync = qfalse; - if (!fsh[f].handleFiles.file.o) { - f = 0; - } - return f; -} - -/* -=========== -FS_FOpenFileAppend - -=========== -*/ -fileHandle_t FS_FOpenFileAppend( const char *filename ) { - char *ospath; - fileHandle_t f; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - f = FS_HandleForFile(); - fsh[f].zipFile = qfalse; - - Q_strncpyz( fsh[f].name, filename, sizeof( fsh[f].name ) ); - - // don't let sound stutter - S_ClearSoundBuffer(); - - ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, filename ); - - if ( fs_debug->integer ) { - Com_Printf( "FS_FOpenFileAppend: %s\n", ospath ); - } - - if( FS_CreatePath( ospath ) ) { - return 0; - } - - fsh[f].handleFiles.file.o = fopen( ospath, "ab" ); - fsh[f].handleSync = qfalse; - if (!fsh[f].handleFiles.file.o) { - f = 0; - } - return f; -} - -/* -=========== -FS_FilenameCompare - -Ignore case and seprator char distinctions -=========== -*/ -qboolean FS_FilenameCompare( const char *s1, const char *s2 ) { - int c1, c2; - - do { - c1 = *s1++; - c2 = *s2++; - - if (c1 >= 'a' && c1 <= 'z') { - c1 -= ('a' - 'A'); - } - if (c2 >= 'a' && c2 <= 'z') { - c2 -= ('a' - 'A'); - } - - if ( c1 == '\\' || c1 == ':' ) { - c1 = '/'; - } - if ( c2 == '\\' || c2 == ':' ) { - c2 = '/'; - } - - if (c1 != c2) { - return -1; // strings not equal - } - } while (c1); - - return 0; // strings are equal -} - -/* -=========== -FS_ShiftedStrStr -=========== -*/ -char *FS_ShiftedStrStr(const char *string, const char *substring, int shift) { - char buf[MAX_STRING_TOKENS]; - int i; - - for (i = 0; substring[i]; i++) { - buf[i] = substring[i] + shift; - } - buf[i] = '\0'; - return strstr(string, buf); -} - -/* -=========== -FS_FOpenFileRead - -Finds the file in the search path. -Returns filesize and an open FILE pointer. -Used for streaming data out of either a -separate file or a ZIP file. -=========== -*/ -extern qboolean com_fullyInitialized; - -int FS_FOpenFileRead( const char *filename, fileHandle_t *file, qboolean uniqueFILE ) { - searchpath_t *search; - char *netpath; - pack_t *pak; - fileInPack_t *pakFile; - directory_t *dir; - long hash; - unz_s *zfi; - FILE *temp; - int l; - char demoExt[16]; - - hash = 0; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - if ( file == NULL ) { - // just wants to see if file is there - for ( search = fs_searchpaths ; search ; search = search->next ) { - // - if ( search->pack ) { - hash = FS_HashFileName(filename, search->pack->hashSize); - } - // is the element a pak file? - if ( search->pack && search->pack->hashTable[hash] ) { - // look through all the pak file elements - pak = search->pack; - pakFile = pak->hashTable[hash]; - do { - // case and separator insensitive comparisons - if ( !FS_FilenameCompare( pakFile->name, filename ) ) { - // found it! - return qtrue; - } - pakFile = pakFile->next; - } while(pakFile != NULL); - } else if ( search->dir ) { - dir = search->dir; - - netpath = FS_BuildOSPath( dir->path, dir->gamedir, filename ); - temp = fopen (netpath, "rb"); - if ( !temp ) { - continue; - } - fclose(temp); - return qtrue; - } - } - return qfalse; - } - - if ( !filename ) { - Com_Error( ERR_FATAL, "FS_FOpenFileRead: NULL 'filename' parameter passed\n" ); - } - - Com_sprintf (demoExt, sizeof(demoExt), ".dm_%d",PROTOCOL_VERSION ); - // qpaths are not supposed to have a leading slash - if ( filename[0] == '/' || filename[0] == '\\' ) { - filename++; - } - - // make absolutely sure that it can't back up the path. - // The searchpaths do guarantee that something will always - // be prepended, so we don't need to worry about "c:" or "//limbo" - if ( strstr( filename, ".." ) || strstr( filename, "::" ) ) { - *file = 0; - return -1; - } - - // make sure the q3key file is only readable by the quake3.exe at initialization - // any other time the key should only be accessed in memory using the provided functions - if( com_fullyInitialized && strstr( filename, "q3key" ) ) { - *file = 0; - return -1; - } - - // - // search through the path, one element at a time - // - - *file = FS_HandleForFile(); - fsh[*file].handleFiles.unique = uniqueFILE; - - for ( search = fs_searchpaths ; search ; search = search->next ) { - // - if ( search->pack ) { - hash = FS_HashFileName(filename, search->pack->hashSize); - } - // is the element a pak file? - if ( search->pack && search->pack->hashTable[hash] ) { - // disregard if it doesn't match one of the allowed pure pak files - if ( !FS_PakIsPure(search->pack) ) { - continue; - } - - // look through all the pak file elements - pak = search->pack; - pakFile = pak->hashTable[hash]; - do { - // case and separator insensitive comparisons - if ( !FS_FilenameCompare( pakFile->name, filename ) ) { - // found it! - - // mark the pak as having been referenced and mark specifics on cgame and ui - // shaders, txt, arena files by themselves do not count as a reference as - // these are loaded from all pk3s - // from every pk3 file.. - l = strlen( filename ); - if ( !(pak->referenced & FS_GENERAL_REF)) { - if ( Q_stricmp(filename + l - 7, ".shader") != 0 && - Q_stricmp(filename + l - 4, ".txt") != 0 && - Q_stricmp(filename + l - 4, ".cfg") != 0 && - Q_stricmp(filename + l - 7, ".config") != 0 && - strstr(filename, "levelshots") == NULL && - Q_stricmp(filename + l - 4, ".bot") != 0 && - Q_stricmp(filename + l - 6, ".arena") != 0 && - Q_stricmp(filename + l - 5, ".menu") != 0) { - pak->referenced |= FS_GENERAL_REF; - } - } - - // qagame.qvm - 13 - // dTZT`X!di` - if (!(pak->referenced & FS_QAGAME_REF) && FS_ShiftedStrStr(filename, "dTZT`X!di`", 13)) { - pak->referenced |= FS_QAGAME_REF; - } - // cgame.qvm - 7 - // \`Zf^'jof - if (!(pak->referenced & FS_CGAME_REF) && FS_ShiftedStrStr(filename , "\\`Zf^'jof", 7)) { - pak->referenced |= FS_CGAME_REF; - } - // ui.qvm - 5 - // pd)lqh - if (!(pak->referenced & FS_UI_REF) && FS_ShiftedStrStr(filename , "pd)lqh", 5)) { - pak->referenced |= FS_UI_REF; - } - - if ( uniqueFILE ) { - // open a new file on the pakfile - fsh[*file].handleFiles.file.z = unzReOpen (pak->pakFilename, pak->handle); - if (fsh[*file].handleFiles.file.z == NULL) { - Com_Error (ERR_FATAL, "Couldn't reopen %s", pak->pakFilename); - } - } else { - fsh[*file].handleFiles.file.z = pak->handle; - } - Q_strncpyz( fsh[*file].name, filename, sizeof( fsh[*file].name ) ); - fsh[*file].zipFile = qtrue; - zfi = (unz_s *)fsh[*file].handleFiles.file.z; - // in case the file was new - temp = zfi->file; - // set the file position in the zip file (also sets the current file info) - unzSetCurrentFileInfoPosition(pak->handle, pakFile->pos); - // copy the file info into the unzip structure - Com_Memcpy( zfi, pak->handle, sizeof(unz_s) ); - // we copy this back into the structure - zfi->file = temp; - // open the file in the zip - unzOpenCurrentFile( fsh[*file].handleFiles.file.z ); - fsh[*file].zipFilePos = pakFile->pos; - - if ( fs_debug->integer ) { - Com_Printf( "FS_FOpenFileRead: %s (found in '%s')\n", - filename, pak->pakFilename ); - } - return zfi->cur_file_info.uncompressed_size; - } - pakFile = pakFile->next; - } while(pakFile != NULL); - } else if ( search->dir ) { - // check a file in the directory tree - - // if we are running restricted, the only files we - // will allow to come from the directory are .cfg files - l = strlen( filename ); - // FIXME TTimo I'm not sure about the fs_numServerPaks test - // if you are using FS_ReadFile to find out if a file exists, - // this test can make the search fail although the file is in the directory - // I had the problem on https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=8 - // turned out I used FS_FileExists instead - if ( fs_restrict->integer || fs_numServerPaks ) { - - if ( Q_stricmp( filename + l - 4, ".cfg" ) // for config files - && Q_stricmp( filename + l - 5, ".menu" ) // menu files - && Q_stricmp( filename + l - 5, ".game" ) // menu files - && Q_stricmp( filename + l - strlen(demoExt), demoExt ) // menu files - && Q_stricmp( filename + l - 4, ".dat" ) ) { // for journal files - continue; - } - } - - dir = search->dir; - - netpath = FS_BuildOSPath( dir->path, dir->gamedir, filename ); - fsh[*file].handleFiles.file.o = fopen (netpath, "rb"); - if ( !fsh[*file].handleFiles.file.o ) { - continue; - } - - if ( Q_stricmp( filename + l - 4, ".cfg" ) // for config files - && Q_stricmp( filename + l - 5, ".menu" ) // menu files - && Q_stricmp( filename + l - 5, ".game" ) // menu files - && Q_stricmp( filename + l - strlen(demoExt), demoExt ) // menu files - && Q_stricmp( filename + l - 4, ".dat" ) ) { // for journal files - fs_fakeChkSum = random(); - } - - Q_strncpyz( fsh[*file].name, filename, sizeof( fsh[*file].name ) ); - fsh[*file].zipFile = qfalse; - if ( fs_debug->integer ) { - Com_Printf( "FS_FOpenFileRead: %s (found in '%s/%s')\n", filename, - dir->path, dir->gamedir ); - } - - // if we are getting it from the cdpath, optionally copy it - // to the basepath - if ( fs_copyfiles->integer && !Q_stricmp( dir->path, fs_cdpath->string ) ) { - char *copypath; - - copypath = FS_BuildOSPath( fs_basepath->string, dir->gamedir, filename ); - FS_CopyFile( netpath, copypath ); - } - - return FS_filelength (*file); - } - } - - Com_DPrintf ("Can't find %s\n", filename); -#ifdef FS_MISSING - if (missingFiles) { - fprintf(missingFiles, "%s\n", filename); - } -#endif - *file = 0; - return -1; -} - - -/* -================= -FS_Read - -Properly handles partial reads -================= -*/ -int FS_Read2( void *buffer, int len, fileHandle_t f ) { - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - if ( !f ) { - return 0; - } - if (fsh[f].streamed) { - int r; - fsh[f].streamed = qfalse; - r = Sys_StreamedRead( buffer, len, 1, f); - fsh[f].streamed = qtrue; - return r; - } else { - return FS_Read( buffer, len, f); - } -} - -int FS_Read( void *buffer, int len, fileHandle_t f ) { - int block, remaining; - int read; - byte *buf; - int tries; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - if ( !f ) { - return 0; - } - - buf = (byte *)buffer; - fs_readCount += len; - - if (fsh[f].zipFile == qfalse) { - remaining = len; - tries = 0; - while (remaining) { - block = remaining; - read = fread (buf, 1, block, fsh[f].handleFiles.file.o); - if (read == 0) { - // we might have been trying to read from a CD, which - // sometimes returns a 0 read on windows - if (!tries) { - tries = 1; - } else { - return len-remaining; //Com_Error (ERR_FATAL, "FS_Read: 0 bytes read"); - } - } - - if (read == -1) { - Com_Error (ERR_FATAL, "FS_Read: -1 bytes read"); - } - - remaining -= read; - buf += read; - } - return len; - } else { - return unzReadCurrentFile(fsh[f].handleFiles.file.z, buffer, len); - } -} - -/* -================= -FS_Write - -Properly handles partial writes -================= -*/ -int FS_Write( const void *buffer, int len, fileHandle_t h ) { - int block, remaining; - int written; - byte *buf; - int tries; - FILE *f; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - if ( !h ) { - return 0; - } - - f = FS_FileForHandle(h); - buf = (byte *)buffer; - - remaining = len; - tries = 0; - while (remaining) { - block = remaining; - written = fwrite (buf, 1, block, f); - if (written == 0) { - if (!tries) { - tries = 1; - } else { - Com_Printf( "FS_Write: 0 bytes written\n" ); - return 0; - } - } - - if (written == -1) { - Com_Printf( "FS_Write: -1 bytes written\n" ); - return 0; - } - - remaining -= written; - buf += written; - } - if ( fsh[h].handleSync ) { - fflush( f ); - } - return len; -} - -void QDECL FS_Printf( fileHandle_t h, const char *fmt, ... ) { - va_list argptr; - char msg[MAXPRINTMSG]; - - va_start (argptr,fmt); - Q_vsnprintf (msg, sizeof(msg), fmt, argptr); - va_end (argptr); - - FS_Write(msg, strlen(msg), h); -} - -/* -================= -FS_Seek - -================= -*/ -int FS_Seek( fileHandle_t f, long offset, int origin ) { - int _origin; - char foo[65536]; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - return -1; - } - - if (fsh[f].streamed) { - fsh[f].streamed = qfalse; - Sys_StreamSeek( f, offset, origin ); - fsh[f].streamed = qtrue; - } - - if (fsh[f].zipFile == qtrue) { - if (offset == 0 && origin == FS_SEEK_SET) { - // set the file position in the zip file (also sets the current file info) - unzSetCurrentFileInfoPosition(fsh[f].handleFiles.file.z, fsh[f].zipFilePos); - return unzOpenCurrentFile(fsh[f].handleFiles.file.z); - } else if (offset<65536) { - // set the file position in the zip file (also sets the current file info) - unzSetCurrentFileInfoPosition(fsh[f].handleFiles.file.z, fsh[f].zipFilePos); - unzOpenCurrentFile(fsh[f].handleFiles.file.z); - return FS_Read(foo, offset, f); - } else { - Com_Error( ERR_FATAL, "ZIP FILE FSEEK NOT YET IMPLEMENTED\n" ); - return -1; - } - } else { - FILE *file; - file = FS_FileForHandle(f); - switch( origin ) { - case FS_SEEK_CUR: - _origin = SEEK_CUR; - break; - case FS_SEEK_END: - _origin = SEEK_END; - break; - case FS_SEEK_SET: - _origin = SEEK_SET; - break; - default: - _origin = SEEK_CUR; - Com_Error( ERR_FATAL, "Bad origin in FS_Seek\n" ); - break; - } - - return fseek( file, offset, _origin ); - } -} - - -/* -====================================================================================== - -CONVENIENCE FUNCTIONS FOR ENTIRE FILES - -====================================================================================== -*/ - -int FS_FileIsInPAK(const char *filename, int *pChecksum ) { - searchpath_t *search; - pack_t *pak; - fileInPack_t *pakFile; - long hash = 0; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - if ( !filename ) { - Com_Error( ERR_FATAL, "FS_FOpenFileRead: NULL 'filename' parameter passed\n" ); - } - - // qpaths are not supposed to have a leading slash - if ( filename[0] == '/' || filename[0] == '\\' ) { - filename++; - } - - // make absolutely sure that it can't back up the path. - // The searchpaths do guarantee that something will always - // be prepended, so we don't need to worry about "c:" or "//limbo" - if ( strstr( filename, ".." ) || strstr( filename, "::" ) ) { - return -1; - } - - // - // search through the path, one element at a time - // - - for ( search = fs_searchpaths ; search ; search = search->next ) { - // - if (search->pack) { - hash = FS_HashFileName(filename, search->pack->hashSize); - } - // is the element a pak file? - if ( search->pack && search->pack->hashTable[hash] ) { - // disregard if it doesn't match one of the allowed pure pak files - if ( !FS_PakIsPure(search->pack) ) { - continue; - } - - // look through all the pak file elements - pak = search->pack; - pakFile = pak->hashTable[hash]; - do { - // case and separator insensitive comparisons - if ( !FS_FilenameCompare( pakFile->name, filename ) ) { - if (pChecksum) { - *pChecksum = pak->pure_checksum; - } - return 1; - } - pakFile = pakFile->next; - } while(pakFile != NULL); - } - } - return -1; -} - -/* -============ -FS_ReadFile - -Filename are relative to the quake search path -a null buffer will just return the file length without loading -============ -*/ -int FS_ReadFile( const char *qpath, void **buffer ) { - fileHandle_t h; - byte* buf; - qboolean isConfig; - int len; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - if ( !qpath || !qpath[0] ) { - Com_Error( ERR_FATAL, "FS_ReadFile with empty name\n" ); - } - - buf = NULL; // quiet compiler warning - - // if this is a .cfg file and we are playing back a journal, read - // it from the journal file - if ( strstr( qpath, ".cfg" ) ) { - isConfig = qtrue; - if ( com_journal && com_journal->integer == 2 ) { - int r; - - Com_DPrintf( "Loading %s from journal file.\n", qpath ); - r = FS_Read( &len, sizeof( len ), com_journalDataFile ); - if ( r != sizeof( len ) ) { - if (buffer != NULL) *buffer = NULL; - return -1; - } - // if the file didn't exist when the journal was created - if (!len) { - if (buffer == NULL) { - return 1; // hack for old journal files - } - *buffer = NULL; - return -1; - } - if (buffer == NULL) { - return len; - } - - buf = Hunk_AllocateTempMemory(len+1); - *buffer = buf; - - r = FS_Read( buf, len, com_journalDataFile ); - if ( r != len ) { - Com_Error( ERR_FATAL, "Read from journalDataFile failed" ); - } - - fs_loadCount++; - fs_loadStack++; - - // guarantee that it will have a trailing 0 for string operations - buf[len] = 0; - - return len; - } - } else { - isConfig = qfalse; - } - - // look for it in the filesystem or pack files - len = FS_FOpenFileRead( qpath, &h, qfalse ); - if ( h == 0 ) { - if ( buffer ) { - *buffer = NULL; - } - // if we are journalling and it is a config file, write a zero to the journal file - if ( isConfig && com_journal && com_journal->integer == 1 ) { - Com_DPrintf( "Writing zero for %s to journal file.\n", qpath ); - len = 0; - FS_Write( &len, sizeof( len ), com_journalDataFile ); - FS_Flush( com_journalDataFile ); - } - return -1; - } - - if ( !buffer ) { - if ( isConfig && com_journal && com_journal->integer == 1 ) { - Com_DPrintf( "Writing len for %s to journal file.\n", qpath ); - FS_Write( &len, sizeof( len ), com_journalDataFile ); - FS_Flush( com_journalDataFile ); - } - FS_FCloseFile( h); - return len; - } - - fs_loadCount++; - fs_loadStack++; - - buf = Hunk_AllocateTempMemory(len+1); - *buffer = buf; - - FS_Read (buf, len, h); - - // guarantee that it will have a trailing 0 for string operations - buf[len] = 0; - FS_FCloseFile( h ); - - // if we are journalling and it is a config file, write it to the journal file - if ( isConfig && com_journal && com_journal->integer == 1 ) { - Com_DPrintf( "Writing %s to journal file.\n", qpath ); - FS_Write( &len, sizeof( len ), com_journalDataFile ); - FS_Write( buf, len, com_journalDataFile ); - FS_Flush( com_journalDataFile ); - } - return len; -} - -/* -============= -FS_FreeFile -============= -*/ -void FS_FreeFile( void *buffer ) { - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - if ( !buffer ) { - Com_Error( ERR_FATAL, "FS_FreeFile( NULL )" ); - } - fs_loadStack--; - - Hunk_FreeTempMemory( buffer ); - - // if all of our temp files are free, clear all of our space - if ( fs_loadStack == 0 ) { - Hunk_ClearTempMemory(); - } -} - -/* -============ -FS_WriteFile - -Filename are reletive to the quake search path -============ -*/ -void FS_WriteFile( const char *qpath, const void *buffer, int size ) { - fileHandle_t f; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - if ( !qpath || !buffer ) { - Com_Error( ERR_FATAL, "FS_WriteFile: NULL parameter" ); - } - - f = FS_FOpenFileWrite( qpath ); - if ( !f ) { - Com_Printf( "Failed to open %s\n", qpath ); - return; - } - - FS_Write( buffer, size, f ); - - FS_FCloseFile( f ); -} - - - -/* -========================================================================== - -ZIP FILE LOADING - -========================================================================== -*/ - -/* -================= -FS_LoadZipFile - -Creates a new pak_t in the search chain for the contents -of a zip file. -================= -*/ -static pack_t *FS_LoadZipFile( char *zipfile, const char *basename ) -{ - fileInPack_t *buildBuffer; - pack_t *pack; - unzFile uf; - int err; - unz_global_info gi; - char filename_inzip[MAX_ZPATH]; - unz_file_info file_info; - int i, len; - long hash; - int fs_numHeaderLongs; - int *fs_headerLongs; - char *namePtr; - - fs_numHeaderLongs = 0; - - uf = unzOpen(zipfile); - err = unzGetGlobalInfo (uf,&gi); - - if (err != UNZ_OK) - return NULL; - - fs_packFiles += gi.number_entry; - - len = 0; - unzGoToFirstFile(uf); - for (i = 0; i < gi.number_entry; i++) - { - err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0); - if (err != UNZ_OK) { - break; - } - len += strlen(filename_inzip) + 1; - unzGoToNextFile(uf); - } - - buildBuffer = Z_Malloc( (gi.number_entry * sizeof( fileInPack_t )) + len ); - namePtr = ((char *) buildBuffer) + gi.number_entry * sizeof( fileInPack_t ); - fs_headerLongs = Z_Malloc( gi.number_entry * sizeof(int) ); - - // get the hash table size from the number of files in the zip - // because lots of custom pk3 files have less than 32 or 64 files - for (i = 1; i <= MAX_FILEHASH_SIZE; i <<= 1) { - if (i > gi.number_entry) { - break; - } - } - - pack = Z_Malloc( sizeof( pack_t ) + i * sizeof(fileInPack_t *) ); - pack->hashSize = i; - pack->hashTable = (fileInPack_t **) (((char *) pack) + sizeof( pack_t )); - for(i = 0; i < pack->hashSize; i++) { - pack->hashTable[i] = NULL; - } - - Q_strncpyz( pack->pakFilename, zipfile, sizeof( pack->pakFilename ) ); - Q_strncpyz( pack->pakBasename, basename, sizeof( pack->pakBasename ) ); - - // strip .pk3 if needed - if ( strlen( pack->pakBasename ) > 4 && !Q_stricmp( pack->pakBasename + strlen( pack->pakBasename ) - 4, ".pk3" ) ) { - pack->pakBasename[strlen( pack->pakBasename ) - 4] = 0; - } - - pack->handle = uf; - pack->numfiles = gi.number_entry; - unzGoToFirstFile(uf); - - for (i = 0; i < gi.number_entry; i++) - { - err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0); - if (err != UNZ_OK) { - break; - } - if (file_info.uncompressed_size > 0) { - fs_headerLongs[fs_numHeaderLongs++] = LittleLong(file_info.crc); - } - Q_strlwr( filename_inzip ); - hash = FS_HashFileName(filename_inzip, pack->hashSize); - buildBuffer[i].name = namePtr; - strcpy( buildBuffer[i].name, filename_inzip ); - namePtr += strlen(filename_inzip) + 1; - // store the file position in the zip - unzGetCurrentFileInfoPosition(uf, &buildBuffer[i].pos); - // - buildBuffer[i].next = pack->hashTable[hash]; - pack->hashTable[hash] = &buildBuffer[i]; - unzGoToNextFile(uf); - } - - pack->checksum = Com_BlockChecksum( fs_headerLongs, 4 * fs_numHeaderLongs ); - pack->pure_checksum = Com_BlockChecksumKey( fs_headerLongs, 4 * fs_numHeaderLongs, LittleLong(fs_checksumFeed) ); - pack->checksum = LittleLong( pack->checksum ); - pack->pure_checksum = LittleLong( pack->pure_checksum ); - - Z_Free(fs_headerLongs); - - pack->buildBuffer = buildBuffer; - return pack; -} - -/* -================================================================================= - -DIRECTORY SCANNING FUNCTIONS - -================================================================================= -*/ - -#define MAX_FOUND_FILES 0x1000 - -static int FS_ReturnPath( const char *zname, char *zpath, int *depth ) { - int len, at, newdep; - - newdep = 0; - zpath[0] = 0; - len = 0; - at = 0; - - while(zname[at] != 0) - { - if (zname[at]=='/' || zname[at]=='\\') { - len = at; - newdep++; - } - at++; - } - strcpy(zpath, zname); - zpath[len] = 0; - *depth = newdep; - - return len; -} - -/* -================== -FS_AddFileToList -================== -*/ -static int FS_AddFileToList( char *name, char *list[MAX_FOUND_FILES], int nfiles ) { - int i; - - if ( nfiles == MAX_FOUND_FILES - 1 ) { - return nfiles; - } - for ( i = 0 ; i < nfiles ; i++ ) { - if ( !Q_stricmp( name, list[i] ) ) { - return nfiles; // allready in list - } - } - list[nfiles] = CopyString( name ); - nfiles++; - - return nfiles; -} - -/* -=============== -FS_ListFilteredFiles - -Returns a uniqued list of files that match the given criteria -from all search paths -=============== -*/ -char **FS_ListFilteredFiles( const char *path, const char *extension, char *filter, int *numfiles ) { - int nfiles; - char **listCopy; - char *list[MAX_FOUND_FILES]; - searchpath_t *search; - int i; - int pathLength; - int extensionLength; - int length, pathDepth, temp; - pack_t *pak; - fileInPack_t *buildBuffer; - char zpath[MAX_ZPATH]; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - if ( !path ) { - *numfiles = 0; - return NULL; - } - if ( !extension ) { - extension = ""; - } - - pathLength = strlen( path ); - if ( path[pathLength-1] == '\\' || path[pathLength-1] == '/' ) { - pathLength--; - } - extensionLength = strlen( extension ); - nfiles = 0; - FS_ReturnPath(path, zpath, &pathDepth); - - // - // search through the path, one element at a time, adding to list - // - for (search = fs_searchpaths ; search ; search = search->next) { - // is the element a pak file? - if (search->pack) { - - //ZOID: If we are pure, don't search for files on paks that - // aren't on the pure list - if ( !FS_PakIsPure(search->pack) ) { - continue; - } - - // look through all the pak file elements - pak = search->pack; - buildBuffer = pak->buildBuffer; - for (i = 0; i < pak->numfiles; i++) { - char *name; - int zpathLen, depth; - - // check for directory match - name = buildBuffer[i].name; - // - if (filter) { - // case insensitive - if (!Com_FilterPath( filter, name, qfalse )) - continue; - // unique the match - nfiles = FS_AddFileToList( name, list, nfiles ); - } - else { - - zpathLen = FS_ReturnPath(name, zpath, &depth); - - if ( (depth-pathDepth)>2 || pathLength > zpathLen || Q_stricmpn( name, path, pathLength ) ) { - continue; - } - - // check for extension match - length = strlen( name ); - if ( length < extensionLength ) { - continue; - } - - if ( Q_stricmp( name + length - extensionLength, extension ) ) { - continue; - } - // unique the match - - temp = pathLength; - if (pathLength) { - temp++; // include the '/' - } - nfiles = FS_AddFileToList( name + temp, list, nfiles ); - } - } - } else if (search->dir) { // scan for files in the filesystem - char *netpath; - int numSysFiles; - char **sysFiles; - char *name; - - // don't scan directories for files if we are pure or restricted - if ( fs_restrict->integer || fs_numServerPaks ) { - continue; - } else { - netpath = FS_BuildOSPath( search->dir->path, search->dir->gamedir, path ); - sysFiles = Sys_ListFiles( netpath, extension, filter, &numSysFiles, qfalse ); - for ( i = 0 ; i < numSysFiles ; i++ ) { - // unique the match - name = sysFiles[i]; - nfiles = FS_AddFileToList( name, list, nfiles ); - } - Sys_FreeFileList( sysFiles ); - } - } - } - - // return a copy of the list - *numfiles = nfiles; - - if ( !nfiles ) { - return NULL; - } - - listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); - for ( i = 0 ; i < nfiles ; i++ ) { - listCopy[i] = list[i]; - } - listCopy[i] = NULL; - - return listCopy; -} - -/* -================= -FS_ListFiles -================= -*/ -char **FS_ListFiles( const char *path, const char *extension, int *numfiles ) { - return FS_ListFilteredFiles( path, extension, NULL, numfiles ); -} - -/* -================= -FS_FreeFileList -================= -*/ -void FS_FreeFileList( char **list ) { - int i; - - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); - } - - if ( !list ) { - return; - } - - for ( i = 0 ; list[i] ; i++ ) { - Z_Free( list[i] ); - } - - Z_Free( list ); -} - - -/* -================ -FS_GetFileList -================ -*/ -int FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ) { - int nFiles, i, nTotal, nLen; - char **pFiles = NULL; - - *listbuf = 0; - nFiles = 0; - nTotal = 0; - - if (Q_stricmp(path, "$modlist") == 0) { - return FS_GetModList(listbuf, bufsize); - } - - pFiles = FS_ListFiles(path, extension, &nFiles); - - for (i =0; i < nFiles; i++) { - nLen = strlen(pFiles[i]) + 1; - if (nTotal + nLen + 1 < bufsize) { - strcpy(listbuf, pFiles[i]); - listbuf += nLen; - nTotal += nLen; - } - else { - nFiles = i; - break; - } - } - - FS_FreeFileList(pFiles); - - return nFiles; -} - -/* -======================= -Sys_ConcatenateFileLists - -mkv: Naive implementation. Concatenates three lists into a - new list, and frees the old lists from the heap. -bk001129 - from cvs1.17 (mkv) - -FIXME TTimo those two should move to common.c next to Sys_ListFiles -======================= - */ -static unsigned int Sys_CountFileList(char **list) -{ - int i = 0; - - if (list) - { - while (*list) - { - list++; - i++; - } - } - return i; -} - -static char** Sys_ConcatenateFileLists( char **list0, char **list1, char **list2 ) -{ - int totalLength = 0; - char** cat = NULL, **dst, **src; - - totalLength += Sys_CountFileList(list0); - totalLength += Sys_CountFileList(list1); - totalLength += Sys_CountFileList(list2); - - /* Create new list. */ - dst = cat = Z_Malloc( ( totalLength + 1 ) * sizeof( char* ) ); - - /* Copy over lists. */ - if (list0) - { - for (src = list0; *src; src++, dst++) - *dst = *src; - } - if (list1) - { - for (src = list1; *src; src++, dst++) - *dst = *src; - } - if (list2) - { - for (src = list2; *src; src++, dst++) - *dst = *src; - } - - // Terminate the list - *dst = NULL; - - // Free our old lists. - // NOTE: not freeing their content, it's been merged in dst and still being used - if (list0) Z_Free( list0 ); - if (list1) Z_Free( list1 ); - if (list2) Z_Free( list2 ); - - return cat; -} - -/* -================ -FS_GetModList - -Returns a list of mod directory names -A mod directory is a peer to baseq3 with a pk3 in it -The directories are searched in base path, cd path and home path -================ -*/ -int FS_GetModList( char *listbuf, int bufsize ) { - int nMods, i, j, nTotal, nLen, nPaks, nPotential, nDescLen; - char **pFiles = NULL; - char **pPaks = NULL; - char *name, *path; - char descPath[MAX_OSPATH]; - fileHandle_t descHandle; - - int dummy; - char **pFiles0 = NULL; - char **pFiles1 = NULL; - char **pFiles2 = NULL; - qboolean bDrop = qfalse; - - *listbuf = 0; - nMods = nPotential = nTotal = 0; - - pFiles0 = Sys_ListFiles( fs_homepath->string, NULL, NULL, &dummy, qtrue ); - pFiles1 = Sys_ListFiles( fs_basepath->string, NULL, NULL, &dummy, qtrue ); - pFiles2 = Sys_ListFiles( fs_cdpath->string, NULL, NULL, &dummy, qtrue ); - // we searched for mods in the three paths - // it is likely that we have duplicate names now, which we will cleanup below - pFiles = Sys_ConcatenateFileLists( pFiles0, pFiles1, pFiles2 ); - nPotential = Sys_CountFileList(pFiles); - - for ( i = 0 ; i < nPotential ; i++ ) { - name = pFiles[i]; - // NOTE: cleaner would involve more changes - // ignore duplicate mod directories - if (i!=0) { - bDrop = qfalse; - for(j=0; jstring, name, "" ); - nPaks = 0; - pPaks = Sys_ListFiles(path, ".pk3", NULL, &nPaks, qfalse); - Sys_FreeFileList( pPaks ); // we only use Sys_ListFiles to check wether .pk3 files are present - - /* Try on cd path */ - if( nPaks <= 0 ) { - path = FS_BuildOSPath( fs_cdpath->string, name, "" ); - nPaks = 0; - pPaks = Sys_ListFiles( path, ".pk3", NULL, &nPaks, qfalse ); - Sys_FreeFileList( pPaks ); - } - - /* try on home path */ - if ( nPaks <= 0 ) - { - path = FS_BuildOSPath( fs_homepath->string, name, "" ); - nPaks = 0; - pPaks = Sys_ListFiles( path, ".pk3", NULL, &nPaks, qfalse ); - Sys_FreeFileList( pPaks ); - } - - if (nPaks > 0) { - nLen = strlen(name) + 1; - // nLen is the length of the mod path - // we need to see if there is a description available - descPath[0] = '\0'; - strcpy(descPath, name); - strcat(descPath, "/description.txt"); - nDescLen = FS_SV_FOpenFileRead( descPath, &descHandle ); - if ( nDescLen > 0 && descHandle) { - FILE *file; - file = FS_FileForHandle(descHandle); - Com_Memset( descPath, 0, sizeof( descPath ) ); - nDescLen = fread(descPath, 1, 48, file); - if (nDescLen >= 0) { - descPath[nDescLen] = '\0'; - } - FS_FCloseFile(descHandle); - } else { - strcpy(descPath, name); - } - nDescLen = strlen(descPath) + 1; - - if (nTotal + nLen + 1 + nDescLen + 1 < bufsize) { - strcpy(listbuf, name); - listbuf += nLen; - strcpy(listbuf, descPath); - listbuf += nDescLen; - nTotal += nLen + nDescLen; - nMods++; - } - else { - break; - } - } - } - } - Sys_FreeFileList( pFiles ); - - return nMods; -} - - - - -//============================================================================ - -/* -================ -FS_Dir_f -================ -*/ -void FS_Dir_f( void ) { - char *path; - char *extension; - char **dirnames; - int ndirs; - int i; - - if ( Cmd_Argc() < 2 || Cmd_Argc() > 3 ) { - Com_Printf( "usage: dir [extension]\n" ); - return; - } - - if ( Cmd_Argc() == 2 ) { - path = Cmd_Argv( 1 ); - extension = ""; - } else { - path = Cmd_Argv( 1 ); - extension = Cmd_Argv( 2 ); - } - - Com_Printf( "Directory of %s %s\n", path, extension ); - Com_Printf( "---------------\n" ); - - dirnames = FS_ListFiles( path, extension, &ndirs ); - - for ( i = 0; i < ndirs; i++ ) { - Com_Printf( "%s\n", dirnames[i] ); - } - FS_FreeFileList( dirnames ); -} - -/* -=========== -FS_ConvertPath -=========== -*/ -void FS_ConvertPath( char *s ) { - while (*s) { - if ( *s == '\\' || *s == ':' ) { - *s = '/'; - } - s++; - } -} - -/* -=========== -FS_PathCmp - -Ignore case and seprator char distinctions -=========== -*/ -int FS_PathCmp( const char *s1, const char *s2 ) { - int c1, c2; - - do { - c1 = *s1++; - c2 = *s2++; - - if (c1 >= 'a' && c1 <= 'z') { - c1 -= ('a' - 'A'); - } - if (c2 >= 'a' && c2 <= 'z') { - c2 -= ('a' - 'A'); - } - - if ( c1 == '\\' || c1 == ':' ) { - c1 = '/'; - } - if ( c2 == '\\' || c2 == ':' ) { - c2 = '/'; - } - - if (c1 < c2) { - return -1; // strings not equal - } - if (c1 > c2) { - return 1; - } - } while (c1); - - return 0; // strings are equal -} - -/* -================ -FS_SortFileList -================ -*/ -void FS_SortFileList(char **filelist, int numfiles) { - int i, j, k, numsortedfiles; - char **sortedlist; - - sortedlist = Z_Malloc( ( numfiles + 1 ) * sizeof( *sortedlist ) ); - sortedlist[0] = NULL; - numsortedfiles = 0; - for (i = 0; i < numfiles; i++) { - for (j = 0; j < numsortedfiles; j++) { - if (FS_PathCmp(filelist[i], sortedlist[j]) < 0) { - break; - } - } - for (k = numsortedfiles; k > j; k--) { - sortedlist[k] = sortedlist[k-1]; - } - sortedlist[j] = filelist[i]; - numsortedfiles++; - } - Com_Memcpy(filelist, sortedlist, numfiles * sizeof( *filelist ) ); - Z_Free(sortedlist); -} - -/* -================ -FS_NewDir_f -================ -*/ -void FS_NewDir_f( void ) { - char *filter; - char **dirnames; - int ndirs; - int i; - - if ( Cmd_Argc() < 2 ) { - Com_Printf( "usage: fdir \n" ); - Com_Printf( "example: fdir *q3dm*.bsp\n"); - return; - } - - filter = Cmd_Argv( 1 ); - - Com_Printf( "---------------\n" ); - - dirnames = FS_ListFilteredFiles( "", "", filter, &ndirs ); - - FS_SortFileList(dirnames, ndirs); - - for ( i = 0; i < ndirs; i++ ) { - FS_ConvertPath(dirnames[i]); - Com_Printf( "%s\n", dirnames[i] ); - } - Com_Printf( "%d files listed\n", ndirs ); - FS_FreeFileList( dirnames ); -} - -/* -============ -FS_Path_f - -============ -*/ -void FS_Path_f( void ) { - searchpath_t *s; - int i; - - Com_Printf ("Current search path:\n"); - for (s = fs_searchpaths; s; s = s->next) { - if (s->pack) { - Com_Printf ("%s (%i files)\n", s->pack->pakFilename, s->pack->numfiles); - if ( fs_numServerPaks ) { - if ( !FS_PakIsPure(s->pack) ) { - Com_Printf( " not on the pure list\n" ); - } else { - Com_Printf( " on the pure list\n" ); - } - } - } else { - Com_Printf ("%s/%s\n", s->dir->path, s->dir->gamedir ); - } - } - - - Com_Printf( "\n" ); - for ( i = 1 ; i < MAX_FILE_HANDLES ; i++ ) { - if ( fsh[i].handleFiles.file.o ) { - Com_Printf( "handle %i: %s\n", i, fsh[i].name ); - } - } -} - -/* -============ -FS_TouchFile_f - -The only purpose of this function is to allow game script files to copy -arbitrary files furing an "fs_copyfiles 1" run. -============ -*/ -void FS_TouchFile_f( void ) { - fileHandle_t f; - - if ( Cmd_Argc() != 2 ) { - Com_Printf( "Usage: touchFile \n" ); - return; - } - - FS_FOpenFileRead( Cmd_Argv( 1 ), &f, qfalse ); - if ( f ) { - FS_FCloseFile( f ); - } -} - -//=========================================================================== - - -static int QDECL paksort( const void *a, const void *b ) { - char *aa, *bb; - - aa = *(char **)a; - bb = *(char **)b; - - return FS_PathCmp( aa, bb ); -} - -/* -================ -FS_AddGameDirectory - -Sets fs_gamedir, adds the directory to the head of the path, -then loads the zip headers -================ -*/ -#define MAX_PAKFILES 1024 -static void FS_AddGameDirectory( const char *path, const char *dir ) { - searchpath_t *sp; - int i; - searchpath_t *search; - pack_t *pak; - char *pakfile; - int numfiles; - char **pakfiles; - char *sorted[MAX_PAKFILES]; - - // this fixes the case where fs_basepath is the same as fs_cdpath - // which happens on full installs - for ( sp = fs_searchpaths ; sp ; sp = sp->next ) { - if ( sp->dir && !Q_stricmp(sp->dir->path, path) && !Q_stricmp(sp->dir->gamedir, dir)) { - return; // we've already got this one - } - } - - Q_strncpyz( fs_gamedir, dir, sizeof( fs_gamedir ) ); - - // - // add the directory to the search path - // - search = Z_Malloc (sizeof(searchpath_t)); - search->dir = Z_Malloc( sizeof( *search->dir ) ); - - Q_strncpyz( search->dir->path, path, sizeof( search->dir->path ) ); - Q_strncpyz( search->dir->gamedir, dir, sizeof( search->dir->gamedir ) ); - search->next = fs_searchpaths; - fs_searchpaths = search; - - // find all pak files in this directory - pakfile = FS_BuildOSPath( path, dir, "" ); - pakfile[ strlen(pakfile) - 1 ] = 0; // strip the trailing slash - - pakfiles = Sys_ListFiles( pakfile, ".pk3", NULL, &numfiles, qfalse ); - - // sort them so that later alphabetic matches override - // earlier ones. This makes pak1.pk3 override pak0.pk3 - if ( numfiles > MAX_PAKFILES ) { - numfiles = MAX_PAKFILES; - } - for ( i = 0 ; i < numfiles ; i++ ) { - sorted[i] = pakfiles[i]; - } - - qsort( sorted, numfiles, 4, paksort ); - - for ( i = 0 ; i < numfiles ; i++ ) { - pakfile = FS_BuildOSPath( path, dir, sorted[i] ); - if ( ( pak = FS_LoadZipFile( pakfile, sorted[i] ) ) == 0 ) - continue; - // store the game name for downloading - strcpy(pak->pakGamename, dir); - - search = Z_Malloc (sizeof(searchpath_t)); - search->pack = pak; - search->next = fs_searchpaths; - fs_searchpaths = search; - } - - // done - Sys_FreeFileList( pakfiles ); -} - -/* -================ -FS_idPak -================ -*/ -qboolean FS_idPak( char *pak, char *base ) { - int i; - - for (i = 0; i < NUM_ID_PAKS; i++) { - if ( !FS_FilenameCompare(pak, va("%s/pak%d", base, i)) ) { - break; - } - } - if (i < NUM_ID_PAKS) { - return qtrue; - } - return qfalse; -} - -/* -================ -FS_ComparePaks - ----------------- -dlstring == qtrue - -Returns a list of pak files that we should download from the server. They all get stored -in the current gamedir and an FS_Restart will be fired up after we download them all. - -The string is the format: - -@remotename@localname [repeat] - -static int fs_numServerReferencedPaks; -static int fs_serverReferencedPaks[MAX_SEARCH_PATHS]; -static char *fs_serverReferencedPakNames[MAX_SEARCH_PATHS]; - ----------------- -dlstring == qfalse - -we are not interested in a download string format, we want something human-readable -(this is used for diagnostics while connecting to a pure server) - -================ -*/ -qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring ) { - searchpath_t *sp; - qboolean havepak, badchecksum; - int i; - - if ( !fs_numServerReferencedPaks ) { - return qfalse; // Server didn't send any pack information along - } - - *neededpaks = 0; - - for ( i = 0 ; i < fs_numServerReferencedPaks ; i++ ) { - // Ok, see if we have this pak file - badchecksum = qfalse; - havepak = qfalse; - - // never autodownload any of the id paks - if ( FS_idPak(fs_serverReferencedPakNames[i], "baseq3") || FS_idPak(fs_serverReferencedPakNames[i], "missionpack") ) { - continue; - } - - for ( sp = fs_searchpaths ; sp ; sp = sp->next ) { - if ( sp->pack && sp->pack->checksum == fs_serverReferencedPaks[i] ) { - havepak = qtrue; // This is it! - break; - } - } - - if ( !havepak && fs_serverReferencedPakNames[i] && *fs_serverReferencedPakNames[i] ) { - // Don't got it - - if (dlstring) - { - // Remote name - Q_strcat( neededpaks, len, "@"); - Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] ); - Q_strcat( neededpaks, len, ".pk3" ); - - // Local name - Q_strcat( neededpaks, len, "@"); - // Do we have one with the same name? - if ( FS_SV_FileExists( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) ) - { - char st[MAX_ZPATH]; - // We already have one called this, we need to download it to another name - // Make something up with the checksum in it - Com_sprintf( st, sizeof( st ), "%s.%08x.pk3", fs_serverReferencedPakNames[i], fs_serverReferencedPaks[i] ); - Q_strcat( neededpaks, len, st ); - } else - { - Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] ); - Q_strcat( neededpaks, len, ".pk3" ); - } - } - else - { - Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] ); - Q_strcat( neededpaks, len, ".pk3" ); - // Do we have one with the same name? - if ( FS_SV_FileExists( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) ) - { - Q_strcat( neededpaks, len, " (local file exists with wrong checksum)"); - } - Q_strcat( neededpaks, len, "\n"); - } - } - } - - if ( *neededpaks ) { - return qtrue; - } - - return qfalse; // We have them all -} - -/* -================ -FS_Shutdown - -Frees all resources and closes all files -================ -*/ -void FS_Shutdown( qboolean closemfp ) { - searchpath_t *p, *next; - int i; - - for(i = 0; i < MAX_FILE_HANDLES; i++) { - if (fsh[i].fileSize) { - FS_FCloseFile(i); - } - } - - // free everything - for ( p = fs_searchpaths ; p ; p = next ) { - next = p->next; - - if ( p->pack ) { - unzClose(p->pack->handle); - Z_Free( p->pack->buildBuffer ); - Z_Free( p->pack ); - } - if ( p->dir ) { - Z_Free( p->dir ); - } - Z_Free( p ); - } - - // any FS_ calls will now be an error until reinitialized - fs_searchpaths = NULL; - - Cmd_RemoveCommand( "path" ); - Cmd_RemoveCommand( "dir" ); - Cmd_RemoveCommand( "fdir" ); - Cmd_RemoveCommand( "touchFile" ); - -#ifdef FS_MISSING - if (closemfp) { - fclose(missingFiles); - } -#endif -} - -void Com_AppendCDKey( const char *filename ); -void Com_ReadCDKey( const char *filename ); - -/* -================ -FS_ReorderPurePaks -NOTE TTimo: the reordering that happens here is not reflected in the cvars (\cvarlist *pak*) - this can lead to misleading situations, see https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=540 -================ -*/ -static void FS_ReorderPurePaks() -{ - searchpath_t *s; - int i; - searchpath_t **p_insert_index, // for linked list reordering - **p_previous; // when doing the scan - - // only relevant when connected to pure server - if ( !fs_numServerPaks ) - return; - - fs_reordered = qfalse; - - p_insert_index = &fs_searchpaths; // we insert in order at the beginning of the list - for ( i = 0 ; i < fs_numServerPaks ; i++ ) { - p_previous = p_insert_index; // track the pointer-to-current-item - for (s = *p_insert_index; s; s = s->next) { - // the part of the list before p_insert_index has been sorted already - if (s->pack && fs_serverPaks[i] == s->pack->checksum) { - fs_reordered = qtrue; - // move this element to the insert list - *p_previous = s->next; - s->next = *p_insert_index; - *p_insert_index = s; - // increment insert list - p_insert_index = &s->next; - break; // iterate to next server pack - } - p_previous = &s->next; - } - } -} - -/* -================ -FS_Startup -================ -*/ -static void FS_Startup( const char *gameName ) { - const char *homePath; - cvar_t *fs; - - Com_Printf( "----- FS_Startup -----\n" ); - - fs_debug = Cvar_Get( "fs_debug", "0", 0 ); - fs_copyfiles = Cvar_Get( "fs_copyfiles", "0", CVAR_INIT ); - fs_cdpath = Cvar_Get ("fs_cdpath", Sys_DefaultCDPath(), CVAR_INIT ); - fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultInstallPath(), CVAR_INIT ); - fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_INIT ); - homePath = Sys_DefaultHomePath(); - if (!homePath || !homePath[0]) { - homePath = fs_basepath->string; - } - fs_homepath = Cvar_Get ("fs_homepath", homePath, CVAR_INIT ); - fs_gamedirvar = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - fs_restrict = Cvar_Get ("fs_restrict", "", CVAR_INIT ); - - // add search path elements in reverse priority order - if (fs_cdpath->string[0]) { - FS_AddGameDirectory( fs_cdpath->string, gameName ); - } - if (fs_basepath->string[0]) { - FS_AddGameDirectory( fs_basepath->string, gameName ); - } - // fs_homepath is somewhat particular to *nix systems, only add if relevant - // NOTE: same filtering below for mods and basegame - if (fs_basepath->string[0] && Q_stricmp(fs_homepath->string,fs_basepath->string)) { - FS_AddGameDirectory ( fs_homepath->string, gameName ); - } - - // check for additional base game so mods can be based upon other mods - if ( fs_basegame->string[0] && !Q_stricmp( gameName, BASEGAME ) && Q_stricmp( fs_basegame->string, gameName ) ) { - if (fs_cdpath->string[0]) { - FS_AddGameDirectory(fs_cdpath->string, fs_basegame->string); - } - if (fs_basepath->string[0]) { - FS_AddGameDirectory(fs_basepath->string, fs_basegame->string); - } - if (fs_homepath->string[0] && Q_stricmp(fs_homepath->string,fs_basepath->string)) { - FS_AddGameDirectory(fs_homepath->string, fs_basegame->string); - } - } - - // check for additional game folder for mods - if ( fs_gamedirvar->string[0] && !Q_stricmp( gameName, BASEGAME ) && Q_stricmp( fs_gamedirvar->string, gameName ) ) { - if (fs_cdpath->string[0]) { - FS_AddGameDirectory(fs_cdpath->string, fs_gamedirvar->string); - } - if (fs_basepath->string[0]) { - FS_AddGameDirectory(fs_basepath->string, fs_gamedirvar->string); - } - if (fs_homepath->string[0] && Q_stricmp(fs_homepath->string,fs_basepath->string)) { - FS_AddGameDirectory(fs_homepath->string, fs_gamedirvar->string); - } - } - - Com_ReadCDKey( "baseq3" ); - fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - if (fs && fs->string[0] != 0) { - Com_AppendCDKey( fs->string ); - } - - // add our commands - Cmd_AddCommand ("path", FS_Path_f); - Cmd_AddCommand ("dir", FS_Dir_f ); - Cmd_AddCommand ("fdir", FS_NewDir_f ); - Cmd_AddCommand ("touchFile", FS_TouchFile_f ); - - // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=506 - // reorder the pure pk3 files according to server order - FS_ReorderPurePaks(); - - // print the current search paths - FS_Path_f(); - - fs_gamedirvar->modified = qfalse; // We just loaded, it's not modified - - Com_Printf( "----------------------\n" ); - -#ifdef FS_MISSING - if (missingFiles == NULL) { - missingFiles = fopen( "\\missing.txt", "ab" ); - } -#endif - Com_Printf( "%d files in pk3 files\n", fs_packFiles ); -} - - -/* -=================== -FS_SetRestrictions - -Looks for product keys and restricts media add on ability -if the full version is not found -=================== -*/ -static void FS_SetRestrictions( void ) { - searchpath_t *path; - -#ifndef PRE_RELEASE_DEMO - char *productId; - - // if fs_restrict is set, don't even look for the id file, - // which allows the demo release to be tested even if - // the full game is present - if ( !fs_restrict->integer ) { - // look for the full game id - FS_ReadFile( "productid.txt", (void **)&productId ); - if ( productId ) { - // check against the hardcoded string - int seed, i; - - seed = 5000; - for ( i = 0 ; i < sizeof( fs_scrambledProductId ) ; i++ ) { - if ( ( fs_scrambledProductId[i] ^ (seed&255) ) != productId[i] ) { - break; - } - seed = (69069 * seed + 1); - } - - FS_FreeFile( productId ); - - if ( i == sizeof( fs_scrambledProductId ) ) { - return; // no restrictions - } - Com_Error( ERR_FATAL, "Invalid product identification" ); - } - } -#endif - Cvar_Set( "fs_restrict", "1" ); - - Com_Printf( "\nRunning in restricted demo mode.\n\n" ); - - // restart the filesystem with just the demo directory - FS_Shutdown(qfalse); - FS_Startup( DEMOGAME ); - - // make sure that the pak file has the header checksum we expect - for ( path = fs_searchpaths ; path ; path = path->next ) { - if ( path->pack ) { - // a tiny attempt to keep the checksum from being scannable from the exe - if ( (path->pack->checksum ^ 0x02261994u) != (DEMO_PAK_CHECKSUM ^ 0x02261994u) ) { - Com_Error( ERR_FATAL, "Corrupted pak0.pk3: %u", path->pack->checksum ); - } - } - } -} - -/* -===================== -FS_GamePureChecksum - -Returns the checksum of the pk3 from which the server loaded the qagame.qvm -===================== -*/ -const char *FS_GamePureChecksum( void ) { - static char info[MAX_STRING_TOKENS]; - searchpath_t *search; - - info[0] = 0; - - for ( search = fs_searchpaths ; search ; search = search->next ) { - // is the element a pak file? - if ( search->pack ) { - if (search->pack->referenced & FS_QAGAME_REF) { - Com_sprintf(info, sizeof(info), "%d", search->pack->checksum); - } - } - } - - return info; -} - -/* -===================== -FS_LoadedPakChecksums - -Returns a space separated string containing the checksums of all loaded pk3 files. -Servers with sv_pure set will get this string and pass it to clients. -===================== -*/ -const char *FS_LoadedPakChecksums( void ) { - static char info[BIG_INFO_STRING]; - searchpath_t *search; - - info[0] = 0; - - for ( search = fs_searchpaths ; search ; search = search->next ) { - // is the element a pak file? - if ( !search->pack ) { - continue; - } - - Q_strcat( info, sizeof( info ), va("%i ", search->pack->checksum ) ); - } - - return info; -} - -/* -===================== -FS_LoadedPakNames - -Returns a space separated string containing the names of all loaded pk3 files. -Servers with sv_pure set will get this string and pass it to clients. -===================== -*/ -const char *FS_LoadedPakNames( void ) { - static char info[BIG_INFO_STRING]; - searchpath_t *search; - - info[0] = 0; - - for ( search = fs_searchpaths ; search ; search = search->next ) { - // is the element a pak file? - if ( !search->pack ) { - continue; - } - - if (*info) { - Q_strcat(info, sizeof( info ), " " ); - } - Q_strcat( info, sizeof( info ), search->pack->pakBasename ); - } - - return info; -} - -/* -===================== -FS_LoadedPakPureChecksums - -Returns a space separated string containing the pure checksums of all loaded pk3 files. -Servers with sv_pure use these checksums to compare with the checksums the clients send -back to the server. -===================== -*/ -const char *FS_LoadedPakPureChecksums( void ) { - static char info[BIG_INFO_STRING]; - searchpath_t *search; - - info[0] = 0; - - for ( search = fs_searchpaths ; search ; search = search->next ) { - // is the element a pak file? - if ( !search->pack ) { - continue; - } - - Q_strcat( info, sizeof( info ), va("%i ", search->pack->pure_checksum ) ); - } - - return info; -} - -/* -===================== -FS_ReferencedPakChecksums - -Returns a space separated string containing the checksums of all referenced pk3 files. -The server will send this to the clients so they can check which files should be auto-downloaded. -===================== -*/ -const char *FS_ReferencedPakChecksums( void ) { - static char info[BIG_INFO_STRING]; - searchpath_t *search; - - info[0] = 0; - - - for ( search = fs_searchpaths ; search ; search = search->next ) { - // is the element a pak file? - if ( search->pack ) { - if (search->pack->referenced || Q_stricmpn(search->pack->pakGamename, BASEGAME, strlen(BASEGAME))) { - Q_strcat( info, sizeof( info ), va("%i ", search->pack->checksum ) ); - } - } - } - - return info; -} - -/* -===================== -FS_ReferencedPakPureChecksums - -Returns a space separated string containing the pure checksums of all referenced pk3 files. -Servers with sv_pure set will get this string back from clients for pure validation - -The string has a specific order, "cgame ui @ ref1 ref2 ref3 ..." -===================== -*/ -const char *FS_ReferencedPakPureChecksums( void ) { - static char info[BIG_INFO_STRING]; - searchpath_t *search; - int nFlags, numPaks, checksum; - - info[0] = 0; - - checksum = fs_checksumFeed; - numPaks = 0; - for (nFlags = FS_CGAME_REF; nFlags; nFlags = nFlags >> 1) { - if (nFlags & FS_GENERAL_REF) { - // add a delimter between must haves and general refs - //Q_strcat(info, sizeof(info), "@ "); - info[strlen(info)+1] = '\0'; - info[strlen(info)+2] = '\0'; - info[strlen(info)] = '@'; - info[strlen(info)] = ' '; - } - for ( search = fs_searchpaths ; search ; search = search->next ) { - // is the element a pak file and has it been referenced based on flag? - if ( search->pack && (search->pack->referenced & nFlags)) { - Q_strcat( info, sizeof( info ), va("%i ", search->pack->pure_checksum ) ); - if (nFlags & (FS_CGAME_REF | FS_UI_REF)) { - break; - } - checksum ^= search->pack->pure_checksum; - numPaks++; - } - } - if (fs_fakeChkSum != 0) { - // only added if a non-pure file is referenced - Q_strcat( info, sizeof( info ), va("%i ", fs_fakeChkSum ) ); - } - } - // last checksum is the encoded number of referenced pk3s - checksum ^= numPaks; - Q_strcat( info, sizeof( info ), va("%i ", checksum ) ); - - return info; -} - -/* -===================== -FS_ReferencedPakNames - -Returns a space separated string containing the names of all referenced pk3 files. -The server will send this to the clients so they can check which files should be auto-downloaded. -===================== -*/ -const char *FS_ReferencedPakNames( void ) { - static char info[BIG_INFO_STRING]; - searchpath_t *search; - - info[0] = 0; - - // we want to return ALL pk3's from the fs_game path - // and referenced one's from baseq3 - for ( search = fs_searchpaths ; search ; search = search->next ) { - // is the element a pak file? - if ( search->pack ) { - if (*info) { - Q_strcat(info, sizeof( info ), " " ); - } - if (search->pack->referenced || Q_stricmpn(search->pack->pakGamename, BASEGAME, strlen(BASEGAME))) { - Q_strcat( info, sizeof( info ), search->pack->pakGamename ); - Q_strcat( info, sizeof( info ), "/" ); - Q_strcat( info, sizeof( info ), search->pack->pakBasename ); - } - } - } - - return info; -} - -/* -===================== -FS_ClearPakReferences -===================== -*/ -void FS_ClearPakReferences( int flags ) { - searchpath_t *search; - - if ( !flags ) { - flags = -1; - } - for ( search = fs_searchpaths; search; search = search->next ) { - // is the element a pak file and has it been referenced? - if ( search->pack ) { - search->pack->referenced &= ~flags; - } - } -} - - -/* -===================== -FS_PureServerSetLoadedPaks - -If the string is empty, all data sources will be allowed. -If not empty, only pk3 files that match one of the space -separated checksums will be checked for files, with the -exception of .cfg and .dat files. -===================== -*/ -void FS_PureServerSetLoadedPaks( const char *pakSums, const char *pakNames ) { - int i, c, d; - - Cmd_TokenizeString( pakSums ); - - c = Cmd_Argc(); - if ( c > MAX_SEARCH_PATHS ) { - c = MAX_SEARCH_PATHS; - } - - fs_numServerPaks = c; - - for ( i = 0 ; i < c ; i++ ) { - fs_serverPaks[i] = atoi( Cmd_Argv( i ) ); - } - - if (fs_numServerPaks) { - Com_DPrintf( "Connected to a pure server.\n" ); - } - else - { - if (fs_reordered) - { - // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=540 - // force a restart to make sure the search order will be correct - Com_DPrintf( "FS search reorder is required\n" ); - FS_Restart(fs_checksumFeed); - return; - } - } - - for ( i = 0 ; i < c ; i++ ) { - if (fs_serverPakNames[i]) { - Z_Free(fs_serverPakNames[i]); - } - fs_serverPakNames[i] = NULL; - } - if ( pakNames && *pakNames ) { - Cmd_TokenizeString( pakNames ); - - d = Cmd_Argc(); - if ( d > MAX_SEARCH_PATHS ) { - d = MAX_SEARCH_PATHS; - } - - for ( i = 0 ; i < d ; i++ ) { - fs_serverPakNames[i] = CopyString( Cmd_Argv( i ) ); - } - } -} - -/* -===================== -FS_PureServerSetReferencedPaks - -The checksums and names of the pk3 files referenced at the server -are sent to the client and stored here. The client will use these -checksums to see if any pk3 files need to be auto-downloaded. -===================== -*/ -void FS_PureServerSetReferencedPaks( const char *pakSums, const char *pakNames ) { - int i, c, d; - - Cmd_TokenizeString( pakSums ); - - c = Cmd_Argc(); - if ( c > MAX_SEARCH_PATHS ) { - c = MAX_SEARCH_PATHS; - } - - fs_numServerReferencedPaks = c; - - for ( i = 0 ; i < c ; i++ ) { - fs_serverReferencedPaks[i] = atoi( Cmd_Argv( i ) ); - } - - for ( i = 0 ; i < c ; i++ ) { - if (fs_serverReferencedPakNames[i]) { - Z_Free(fs_serverReferencedPakNames[i]); - } - fs_serverReferencedPakNames[i] = NULL; - } - if ( pakNames && *pakNames ) { - Cmd_TokenizeString( pakNames ); - - d = Cmd_Argc(); - if ( d > MAX_SEARCH_PATHS ) { - d = MAX_SEARCH_PATHS; - } - - for ( i = 0 ; i < d ; i++ ) { - fs_serverReferencedPakNames[i] = CopyString( Cmd_Argv( i ) ); - } - } -} - -/* -================ -FS_InitFilesystem - -Called only at inital startup, not when the filesystem -is resetting due to a game change -================ -*/ -void FS_InitFilesystem( void ) { - // allow command line parms to override our defaults - // we have to specially handle this, because normal command - // line variable sets don't happen until after the filesystem - // has already been initialized - Com_StartupVariable( "fs_cdpath" ); - Com_StartupVariable( "fs_basepath" ); - Com_StartupVariable( "fs_homepath" ); - Com_StartupVariable( "fs_game" ); - Com_StartupVariable( "fs_copyfiles" ); - Com_StartupVariable( "fs_restrict" ); - - // try to start up normally - FS_Startup( BASEGAME ); - - // see if we are going to allow add-ons - FS_SetRestrictions(); - - // if we can't find default.cfg, assume that the paths are - // busted and error out now, rather than getting an unreadable - // graphics screen when the font fails to load - if ( FS_ReadFile( "default.cfg", NULL ) <= 0 ) { - Com_Error( ERR_FATAL, "Couldn't load default.cfg" ); - // bk001208 - SafeMode see below, FIXME? - } - - Q_strncpyz(lastValidBase, fs_basepath->string, sizeof(lastValidBase)); - Q_strncpyz(lastValidGame, fs_gamedirvar->string, sizeof(lastValidGame)); - - // bk001208 - SafeMode see below, FIXME? -} - - -/* -================ -FS_Restart -================ -*/ -void FS_Restart( int checksumFeed ) { - - // free anything we currently have loaded - FS_Shutdown(qfalse); - - // set the checksum feed - fs_checksumFeed = checksumFeed; - - // clear pak references - FS_ClearPakReferences(0); - - // try to start up normally - FS_Startup( BASEGAME ); - - // see if we are going to allow add-ons - FS_SetRestrictions(); - - // if we can't find default.cfg, assume that the paths are - // busted and error out now, rather than getting an unreadable - // graphics screen when the font fails to load - if ( FS_ReadFile( "default.cfg", NULL ) <= 0 ) { - // this might happen when connecting to a pure server not using BASEGAME/pak0.pk3 - // (for instance a TA demo server) - if (lastValidBase[0]) { - FS_PureServerSetLoadedPaks("", ""); - Cvar_Set("fs_basepath", lastValidBase); - Cvar_Set("fs_gamedirvar", lastValidGame); - lastValidBase[0] = '\0'; - lastValidGame[0] = '\0'; - Cvar_Set( "fs_restrict", "0" ); - FS_Restart(checksumFeed); - Com_Error( ERR_DROP, "Invalid game folder\n" ); - return; - } - Com_Error( ERR_FATAL, "Couldn't load default.cfg" ); - } - - // bk010116 - new check before safeMode - if ( Q_stricmp(fs_gamedirvar->string, lastValidGame) ) { - // skip the q3config.cfg if "safe" is on the command line - if ( !Com_SafeMode() ) { - Cbuf_AddText ("exec q3config.cfg\n"); - } - } - - Q_strncpyz(lastValidBase, fs_basepath->string, sizeof(lastValidBase)); - Q_strncpyz(lastValidGame, fs_gamedirvar->string, sizeof(lastValidGame)); - -} - -/* -================= -FS_ConditionalRestart -restart if necessary -================= -*/ -qboolean FS_ConditionalRestart( int checksumFeed ) { - if( fs_gamedirvar->modified || checksumFeed != fs_checksumFeed ) { - FS_Restart( checksumFeed ); - return qtrue; - } - return qfalse; -} - -/* -======================================================================================== - -Handle based file calls for virtual machines - -======================================================================================== -*/ - -int FS_FOpenFileByMode( const char *qpath, fileHandle_t *f, fsMode_t mode ) { - int r; - qboolean sync; - - sync = qfalse; - - switch( mode ) { - case FS_READ: - r = FS_FOpenFileRead( qpath, f, qtrue ); - break; - case FS_WRITE: - *f = FS_FOpenFileWrite( qpath ); - r = 0; - if (*f == 0) { - r = -1; - } - break; - case FS_APPEND_SYNC: - sync = qtrue; - case FS_APPEND: - *f = FS_FOpenFileAppend( qpath ); - r = 0; - if (*f == 0) { - r = -1; - } - break; - default: - Com_Error( ERR_FATAL, "FSH_FOpenFile: bad mode" ); - return -1; - } - - if (!f) { - return r; - } - - if ( *f ) { - if (fsh[*f].zipFile == qtrue) { - fsh[*f].baseOffset = unztell(fsh[*f].handleFiles.file.z); - } else { - fsh[*f].baseOffset = ftell(fsh[*f].handleFiles.file.o); - } - fsh[*f].fileSize = r; - fsh[*f].streamed = qfalse; - - if (mode == FS_READ) { - Sys_BeginStreamedFile( *f, 0x4000 ); - fsh[*f].streamed = qtrue; - } - } - fsh[*f].handleSync = sync; - - return r; -} - -int FS_FTell( fileHandle_t f ) { - int pos; - if (fsh[f].zipFile == qtrue) { - pos = unztell(fsh[f].handleFiles.file.z); - } else { - pos = ftell(fsh[f].handleFiles.file.o); - } - return pos; -} - -void FS_Flush( fileHandle_t f ) { - fflush(fsh[f].handleFiles.file.o); -} - diff --git a/tools/quake3/bspc/deps/qcommon/huffman.c b/tools/quake3/bspc/deps/qcommon/huffman.c deleted file mode 100644 index dd8da981..00000000 --- a/tools/quake3/bspc/deps/qcommon/huffman.c +++ /dev/null @@ -1,437 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/* This is based on the Adaptive Huffman algorithm described in Sayood's Data - * Compression book. The ranks are not actually stored, but implicitly defined - * by the location of a node within a doubly-linked list */ - -#include "q_shared.h" -#include "qcommon.h" - -static int bloc = 0; - -void Huff_putBit( int bit, byte *fout, int *offset) { - bloc = *offset; - if ((bloc&7) == 0) { - fout[(bloc>>3)] = 0; - } - fout[(bloc>>3)] |= bit << (bloc&7); - bloc++; - *offset = bloc; -} - -int Huff_getBit( byte *fin, int *offset) { - int t; - bloc = *offset; - t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1; - bloc++; - *offset = bloc; - return t; -} - -/* Add a bit to the output file (buffered) */ -static void add_bit (char bit, byte *fout) { - if ((bloc&7) == 0) { - fout[(bloc>>3)] = 0; - } - fout[(bloc>>3)] |= bit << (bloc&7); - bloc++; -} - -/* Receive one bit from the input file (buffered) */ -static int get_bit (byte *fin) { - int t; - t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1; - bloc++; - return t; -} - -static node_t **get_ppnode(huff_t* huff) { - node_t **tppnode; - if (!huff->freelist) { - return &(huff->nodePtrs[huff->blocPtrs++]); - } else { - tppnode = huff->freelist; - huff->freelist = (node_t **)*tppnode; - return tppnode; - } -} - -static void free_ppnode(huff_t* huff, node_t **ppnode) { - *ppnode = (node_t *)huff->freelist; - huff->freelist = ppnode; -} - -/* Swap the location of these two nodes in the tree */ -static void swap (huff_t* huff, node_t *node1, node_t *node2) { - node_t *par1, *par2; - - par1 = node1->parent; - par2 = node2->parent; - - if (par1) { - if (par1->left == node1) { - par1->left = node2; - } else { - par1->right = node2; - } - } else { - huff->tree = node2; - } - - if (par2) { - if (par2->left == node2) { - par2->left = node1; - } else { - par2->right = node1; - } - } else { - huff->tree = node1; - } - - node1->parent = par2; - node2->parent = par1; -} - -/* Swap these two nodes in the linked list (update ranks) */ -static void swaplist(node_t *node1, node_t *node2) { - node_t *par1; - - par1 = node1->next; - node1->next = node2->next; - node2->next = par1; - - par1 = node1->prev; - node1->prev = node2->prev; - node2->prev = par1; - - if (node1->next == node1) { - node1->next = node2; - } - if (node2->next == node2) { - node2->next = node1; - } - if (node1->next) { - node1->next->prev = node1; - } - if (node2->next) { - node2->next->prev = node2; - } - if (node1->prev) { - node1->prev->next = node1; - } - if (node2->prev) { - node2->prev->next = node2; - } -} - -/* Do the increments */ -static void increment(huff_t* huff, node_t *node) { - node_t *lnode; - - if (!node) { - return; - } - - if (node->next != NULL && node->next->weight == node->weight) { - lnode = *node->head; - if (lnode != node->parent) { - swap(huff, lnode, node); - } - swaplist(lnode, node); - } - if (node->prev && node->prev->weight == node->weight) { - *node->head = node->prev; - } else { - *node->head = NULL; - free_ppnode(huff, node->head); - } - node->weight++; - if (node->next && node->next->weight == node->weight) { - node->head = node->next->head; - } else { - node->head = get_ppnode(huff); - *node->head = node; - } - if (node->parent) { - increment(huff, node->parent); - if (node->prev == node->parent) { - swaplist(node, node->parent); - if (*node->head == node) { - *node->head = node->parent; - } - } - } -} - -void Huff_addRef(huff_t* huff, byte ch) { - node_t *tnode, *tnode2; - if (huff->loc[ch] == NULL) { /* if this is the first transmission of this node */ - tnode = &(huff->nodeList[huff->blocNode++]); - tnode2 = &(huff->nodeList[huff->blocNode++]); - - tnode2->symbol = INTERNAL_NODE; - tnode2->weight = 1; - tnode2->next = huff->lhead->next; - if (huff->lhead->next) { - huff->lhead->next->prev = tnode2; - if (huff->lhead->next->weight == 1) { - tnode2->head = huff->lhead->next->head; - } else { - tnode2->head = get_ppnode(huff); - *tnode2->head = tnode2; - } - } else { - tnode2->head = get_ppnode(huff); - *tnode2->head = tnode2; - } - huff->lhead->next = tnode2; - tnode2->prev = huff->lhead; - - tnode->symbol = ch; - tnode->weight = 1; - tnode->next = huff->lhead->next; - if (huff->lhead->next) { - huff->lhead->next->prev = tnode; - if (huff->lhead->next->weight == 1) { - tnode->head = huff->lhead->next->head; - } else { - /* this should never happen */ - tnode->head = get_ppnode(huff); - *tnode->head = tnode2; - } - } else { - /* this should never happen */ - tnode->head = get_ppnode(huff); - *tnode->head = tnode; - } - huff->lhead->next = tnode; - tnode->prev = huff->lhead; - tnode->left = tnode->right = NULL; - - if (huff->lhead->parent) { - if (huff->lhead->parent->left == huff->lhead) { /* lhead is guaranteed to by the NYT */ - huff->lhead->parent->left = tnode2; - } else { - huff->lhead->parent->right = tnode2; - } - } else { - huff->tree = tnode2; - } - - tnode2->right = tnode; - tnode2->left = huff->lhead; - - tnode2->parent = huff->lhead->parent; - huff->lhead->parent = tnode->parent = tnode2; - - huff->loc[ch] = tnode; - - increment(huff, tnode2->parent); - } else { - increment(huff, huff->loc[ch]); - } -} - -/* Get a symbol */ -int Huff_Receive (node_t *node, int *ch, byte *fin) { - while (node && node->symbol == INTERNAL_NODE) { - if (get_bit(fin)) { - node = node->right; - } else { - node = node->left; - } - } - if (!node) { - return 0; -// Com_Error(ERR_DROP, "Illegal tree!\n"); - } - return (*ch = node->symbol); -} - -/* Get a symbol */ -void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset) { - bloc = *offset; - while (node && node->symbol == INTERNAL_NODE) { - if (get_bit(fin)) { - node = node->right; - } else { - node = node->left; - } - } - if (!node) { - *ch = 0; - return; -// Com_Error(ERR_DROP, "Illegal tree!\n"); - } - *ch = node->symbol; - *offset = bloc; -} - -/* Send the prefix code for this node */ -static void send(node_t *node, node_t *child, byte *fout) { - if (node->parent) { - send(node->parent, node, fout); - } - if (child) { - if (node->right == child) { - add_bit(1, fout); - } else { - add_bit(0, fout); - } - } -} - -/* Send a symbol */ -void Huff_transmit (huff_t *huff, int ch, byte *fout) { - int i; - if (huff->loc[ch] == NULL) { - /* node_t hasn't been transmitted, send a NYT, then the symbol */ - Huff_transmit(huff, NYT, fout); - for (i = 7; i >= 0; i--) { - add_bit((char)((ch >> i) & 0x1), fout); - } - } else { - send(huff->loc[ch], NULL, fout); - } -} - -void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset) { - bloc = *offset; - send(huff->loc[ch], NULL, fout); - *offset = bloc; -} - -void Huff_Decompress(msg_t *mbuf, int offset) { - int ch, cch, i, j, size; - byte seq[65536]; - byte* buffer; - huff_t huff; - - size = mbuf->cursize - offset; - buffer = mbuf->data + offset; - - if ( size <= 0 ) { - return; - } - - Com_Memset(&huff, 0, sizeof(huff_t)); - // Initialize the tree & list with the NYT node - huff.tree = huff.lhead = huff.ltail = huff.loc[NYT] = &(huff.nodeList[huff.blocNode++]); - huff.tree->symbol = NYT; - huff.tree->weight = 0; - huff.lhead->next = huff.lhead->prev = NULL; - huff.tree->parent = huff.tree->left = huff.tree->right = NULL; - - cch = buffer[0]*256 + buffer[1]; - // don't overflow with bad messages - if ( cch > mbuf->maxsize - offset ) { - cch = mbuf->maxsize - offset; - } - bloc = 16; - - for ( j = 0; j < cch; j++ ) { - ch = 0; - // don't overflow reading from the messages - // FIXME: would it be better to have a overflow check in get_bit ? - if ( (bloc >> 3) > size ) { - seq[j] = 0; - break; - } - Huff_Receive(huff.tree, &ch, buffer); /* Get a character */ - if ( ch == NYT ) { /* We got a NYT, get the symbol associated with it */ - ch = 0; - for ( i = 0; i < 8; i++ ) { - ch = (ch<<1) + get_bit(buffer); - } - } - - seq[j] = ch; /* Write symbol */ - - Huff_addRef(&huff, (byte)ch); /* Increment node */ - } - mbuf->cursize = cch + offset; - Com_Memcpy(mbuf->data + offset, seq, cch); -} - -extern int oldsize; - -void Huff_Compress(msg_t *mbuf, int offset) { - int i, ch, size; - byte seq[65536]; - byte* buffer; - huff_t huff; - - size = mbuf->cursize - offset; - buffer = mbuf->data+ + offset; - - if (size<=0) { - return; - } - - Com_Memset(&huff, 0, sizeof(huff_t)); - // Add the NYT (not yet transmitted) node into the tree/list */ - huff.tree = huff.lhead = huff.loc[NYT] = &(huff.nodeList[huff.blocNode++]); - huff.tree->symbol = NYT; - huff.tree->weight = 0; - huff.lhead->next = huff.lhead->prev = NULL; - huff.tree->parent = huff.tree->left = huff.tree->right = NULL; - huff.loc[NYT] = huff.tree; - - seq[0] = (size>>8); - seq[1] = size&0xff; - - bloc = 16; - - for (i=0; icursize = (bloc>>3) + offset; - Com_Memcpy(mbuf->data+offset, seq, (bloc>>3)); -} - -void Huff_Init(huffman_t *huff) { - - Com_Memset(&huff->compressor, 0, sizeof(huff_t)); - Com_Memset(&huff->decompressor, 0, sizeof(huff_t)); - - // Initialize the tree & list with the NYT node - huff->decompressor.tree = huff->decompressor.lhead = huff->decompressor.ltail = huff->decompressor.loc[NYT] = &(huff->decompressor.nodeList[huff->decompressor.blocNode++]); - huff->decompressor.tree->symbol = NYT; - huff->decompressor.tree->weight = 0; - huff->decompressor.lhead->next = huff->decompressor.lhead->prev = NULL; - huff->decompressor.tree->parent = huff->decompressor.tree->left = huff->decompressor.tree->right = NULL; - - // Add the NYT (not yet transmitted) node into the tree/list */ - huff->compressor.tree = huff->compressor.lhead = huff->compressor.loc[NYT] = &(huff->compressor.nodeList[huff->compressor.blocNode++]); - huff->compressor.tree->symbol = NYT; - huff->compressor.tree->weight = 0; - huff->compressor.lhead->next = huff->compressor.lhead->prev = NULL; - huff->compressor.tree->parent = huff->compressor.tree->left = huff->compressor.tree->right = NULL; - huff->compressor.loc[NYT] = huff->compressor.tree; -} - diff --git a/tools/quake3/bspc/deps/qcommon/ioapi.h b/tools/quake3/bspc/deps/qcommon/ioapi.h deleted file mode 100644 index 7d457baa..00000000 --- a/tools/quake3/bspc/deps/qcommon/ioapi.h +++ /dev/null @@ -1,75 +0,0 @@ -/* ioapi.h -- IO base function header for compress/uncompress .zip - files using zlib + zip or unzip API - - Version 1.01e, February 12th, 2005 - - Copyright (C) 1998-2005 Gilles Vollant -*/ - -#ifndef _ZLIBIOAPI_H -#define _ZLIBIOAPI_H - - -#define ZLIB_FILEFUNC_SEEK_CUR (1) -#define ZLIB_FILEFUNC_SEEK_END (2) -#define ZLIB_FILEFUNC_SEEK_SET (0) - -#define ZLIB_FILEFUNC_MODE_READ (1) -#define ZLIB_FILEFUNC_MODE_WRITE (2) -#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) - -#define ZLIB_FILEFUNC_MODE_EXISTING (4) -#define ZLIB_FILEFUNC_MODE_CREATE (8) - - -#ifndef ZCALLBACK - -#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) -#define ZCALLBACK CALLBACK -#else -#define ZCALLBACK -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); -typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); -typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); -typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); -typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); -typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); -typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); - -typedef struct zlib_filefunc_def_s -{ - open_file_func zopen_file; - read_file_func zread_file; - write_file_func zwrite_file; - tell_file_func ztell_file; - seek_file_func zseek_file; - close_file_func zclose_file; - testerror_file_func zerror_file; - voidpf opaque; -} zlib_filefunc_def; - - - -void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); - -#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) -#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) -#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) -#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) -#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) -#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) - - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/tools/quake3/bspc/deps/qcommon/md4.c b/tools/quake3/bspc/deps/qcommon/md4.c deleted file mode 100644 index 4592bd32..00000000 --- a/tools/quake3/bspc/deps/qcommon/md4.c +++ /dev/null @@ -1,303 +0,0 @@ -/* GLOBAL.H - RSAREF types and constants */ - -#include -#if defined(_WIN32) -#pragma warning(disable : 4711) // selected for automatic inline expansion -#endif - -/* POINTER defines a generic pointer type */ -typedef unsigned char *POINTER; - -/* UINT2 defines a two byte word */ -typedef unsigned short int UINT2; - -/* UINT4 defines a four byte word */ -typedef unsigned long int UINT4; - - -/* MD4.H - header file for MD4C.C */ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. - -All rights reserved. - -License to copy and use this software is granted provided that it is identified as the “RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing this software or this function. -License is also granted to make and use derivative works provided that such works are identified as “derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing the derived work. -RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided “as is” without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this documentation and/or software. */ - -/* MD4 context. */ -typedef struct { - UINT4 state[4]; /* state (ABCD) */ - UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ - unsigned char buffer[64]; /* input buffer */ -} MD4_CTX; - -void MD4Init (MD4_CTX *); -void MD4Update (MD4_CTX *, const unsigned char *, unsigned int); -void MD4Final (unsigned char [16], MD4_CTX *); - -#ifndef __VECTORC -//void Com_Memset (void* dest, const int val, const size_t count); -//void Com_Memcpy (void* dest, const void* src, const size_t count); -#else -#define Com_Memset memset -#define Com_Memcpy memcpy -#endif - -/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm */ -/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved. - -License to copy and use this software is granted provided that it is identified as the -RSA Data Security, Inc. MD4 Message-Digest Algorithm - in all material mentioning or referencing this software or this function. -License is also granted to make and use derivative works provided that such works are identified as -derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm -in all material mentioning or referencing the derived work. -RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided -as is without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this documentation and/or software. */ - -/* Constants for MD4Transform routine. */ -#define S11 3 -#define S12 7 -#define S13 11 -#define S14 19 -#define S21 3 -#define S22 5 -#define S23 9 -#define S24 13 -#define S31 3 -#define S32 9 -#define S33 11 -#define S34 15 - -static void MD4Transform (UINT4 [4], const unsigned char [64]); -static void Encode (unsigned char *, UINT4 *, unsigned int); -static void Decode (UINT4 *, const unsigned char *, unsigned int); - -static unsigned char PADDING[64] = { -0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* F, G and H are basic MD4 functions. */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) - -/* ROTATE_LEFT rotates x left n bits. */ -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - -/* FF, GG and HH are transformations for rounds 1, 2 and 3 */ -/* Rotation is separate from addition to prevent recomputation */ -#define FF(a, b, c, d, x, s) {(a) += F ((b), (c), (d)) + (x); (a) = ROTATE_LEFT ((a), (s));} - -#define GG(a, b, c, d, x, s) {(a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; (a) = ROTATE_LEFT ((a), (s));} - -#define HH(a, b, c, d, x, s) {(a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; (a) = ROTATE_LEFT ((a), (s));} - - -/* MD4 initialization. Begins an MD4 operation, writing a new context. */ -void MD4Init (MD4_CTX *context) -{ - context->count[0] = context->count[1] = 0; - -/* Load magic initialization constants.*/ -context->state[0] = 0x67452301; -context->state[1] = 0xefcdab89; -context->state[2] = 0x98badcfe; -context->state[3] = 0x10325476; -} - -/* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */ -void MD4Update (MD4_CTX *context, const unsigned char *input, unsigned int inputLen) -{ - unsigned int i, index, partLen; - - /* Compute number of bytes mod 64 */ - index = (unsigned int)((context->count[0] >> 3) & 0x3F); - - /* Update number of bits */ - if ((context->count[0] += ((UINT4)inputLen << 3))< ((UINT4)inputLen << 3)) - context->count[1]++; - - context->count[1] += ((UINT4)inputLen >> 29); - - partLen = 64 - index; - - /* Transform as many times as possible.*/ - if (inputLen >= partLen) - { - //Com_Memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); - memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); - MD4Transform (context->state, context->buffer); - - for (i = partLen; i + 63 < inputLen; i += 64) - MD4Transform (context->state, &input[i]); - - index = 0; - } - else - i = 0; - - /* Buffer remaining input */ - //Com_Memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); - memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); -} - - -/* MD4 finalization. Ends an MD4 message-digest operation, writing the the message digest and zeroizing the context. */ -void MD4Final (unsigned char digest[16], MD4_CTX *context) -{ - unsigned char bits[8]; - unsigned int index, padLen; - - /* Save number of bits */ - Encode (bits, context->count, 8); - - /* Pad out to 56 mod 64.*/ - index = (unsigned int)((context->count[0] >> 3) & 0x3f); - padLen = (index < 56) ? (56 - index) : (120 - index); - MD4Update (context, PADDING, padLen); - - /* Append length (before padding) */ - MD4Update (context, bits, 8); - - /* Store state in digest */ - Encode (digest, context->state, 16); - - /* Zeroize sensitive information.*/ - //Com_Memset ((POINTER)context, 0, sizeof (*context)); - memset ((POINTER)context, 0, sizeof (*context)); -} - - -/* MD4 basic transformation. Transforms state based on block. */ -static void MD4Transform (UINT4 state[4], const unsigned char block[64]) -{ - UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - - Decode (x, block, 64); - -/* Round 1 */ -FF (a, b, c, d, x[ 0], S11); /* 1 */ -FF (d, a, b, c, x[ 1], S12); /* 2 */ -FF (c, d, a, b, x[ 2], S13); /* 3 */ -FF (b, c, d, a, x[ 3], S14); /* 4 */ -FF (a, b, c, d, x[ 4], S11); /* 5 */ -FF (d, a, b, c, x[ 5], S12); /* 6 */ -FF (c, d, a, b, x[ 6], S13); /* 7 */ -FF (b, c, d, a, x[ 7], S14); /* 8 */ -FF (a, b, c, d, x[ 8], S11); /* 9 */ -FF (d, a, b, c, x[ 9], S12); /* 10 */ -FF (c, d, a, b, x[10], S13); /* 11 */ -FF (b, c, d, a, x[11], S14); /* 12 */ -FF (a, b, c, d, x[12], S11); /* 13 */ -FF (d, a, b, c, x[13], S12); /* 14 */ -FF (c, d, a, b, x[14], S13); /* 15 */ -FF (b, c, d, a, x[15], S14); /* 16 */ - -/* Round 2 */ -GG (a, b, c, d, x[ 0], S21); /* 17 */ -GG (d, a, b, c, x[ 4], S22); /* 18 */ -GG (c, d, a, b, x[ 8], S23); /* 19 */ -GG (b, c, d, a, x[12], S24); /* 20 */ -GG (a, b, c, d, x[ 1], S21); /* 21 */ -GG (d, a, b, c, x[ 5], S22); /* 22 */ -GG (c, d, a, b, x[ 9], S23); /* 23 */ -GG (b, c, d, a, x[13], S24); /* 24 */ -GG (a, b, c, d, x[ 2], S21); /* 25 */ -GG (d, a, b, c, x[ 6], S22); /* 26 */ -GG (c, d, a, b, x[10], S23); /* 27 */ -GG (b, c, d, a, x[14], S24); /* 28 */ -GG (a, b, c, d, x[ 3], S21); /* 29 */ -GG (d, a, b, c, x[ 7], S22); /* 30 */ -GG (c, d, a, b, x[11], S23); /* 31 */ -GG (b, c, d, a, x[15], S24); /* 32 */ - -/* Round 3 */ -HH (a, b, c, d, x[ 0], S31); /* 33 */ -HH (d, a, b, c, x[ 8], S32); /* 34 */ -HH (c, d, a, b, x[ 4], S33); /* 35 */ -HH (b, c, d, a, x[12], S34); /* 36 */ -HH (a, b, c, d, x[ 2], S31); /* 37 */ -HH (d, a, b, c, x[10], S32); /* 38 */ -HH (c, d, a, b, x[ 6], S33); /* 39 */ -HH (b, c, d, a, x[14], S34); /* 40 */ -HH (a, b, c, d, x[ 1], S31); /* 41 */ -HH (d, a, b, c, x[ 9], S32); /* 42 */ -HH (c, d, a, b, x[ 5], S33); /* 43 */ -HH (b, c, d, a, x[13], S34); /* 44 */ -HH (a, b, c, d, x[ 3], S31); /* 45 */ -HH (d, a, b, c, x[11], S32); /* 46 */ -HH (c, d, a, b, x[ 7], S33); /* 47 */ -HH (b, c, d, a, x[15], S34); /* 48 */ - -state[0] += a; -state[1] += b; -state[2] += c; -state[3] += d; - - /* Zeroize sensitive information.*/ - //Com_Memset ((POINTER)x, 0, sizeof (x)); - memset ((POINTER)x, 0, sizeof (x)); -} - - -/* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */ -static void Encode (unsigned char *output, UINT4 *input, unsigned int len) -{ - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) { - output[j] = (unsigned char)(input[i] & 0xff); - output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); - output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); - output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); - } -} - - -/* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */ -static void Decode (UINT4 *output, const unsigned char *input, unsigned int len) -{ -unsigned int i, j; - -for (i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); -} - -//=================================================================== - -unsigned Com_BlockChecksum (void *buffer, int length) -{ - int digest[4]; - unsigned val; - MD4_CTX ctx; - - MD4Init (&ctx); - MD4Update (&ctx, (unsigned char *)buffer, length); - MD4Final ( (unsigned char *)digest, &ctx); - - val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; - - return val; -} - -unsigned Com_BlockChecksumKey (void *buffer, int length, int key) -{ - int digest[4]; - unsigned val; - MD4_CTX ctx; - - MD4Init (&ctx); - MD4Update (&ctx, (unsigned char *)&key, 4); - MD4Update (&ctx, (unsigned char *)buffer, length); - MD4Final ( (unsigned char *)digest, &ctx); - - val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; - - return val; -} diff --git a/tools/quake3/bspc/deps/qcommon/msg.c b/tools/quake3/bspc/deps/qcommon/msg.c deleted file mode 100644 index b7e7a0a7..00000000 --- a/tools/quake3/bspc/deps/qcommon/msg.c +++ /dev/null @@ -1,1757 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "q_shared.h" -#include "qcommon.h" - -static huffman_t msgHuff; - -static qboolean msgInit = qfalse; - -int pcount[256]; - -/* -============================================================================== - - MESSAGE IO FUNCTIONS - -Handles byte ordering and avoids alignment errors -============================================================================== -*/ - -int oldsize = 0; - -void MSG_initHuffman(); - -void MSG_Init( msg_t *buf, byte *data, int length ) { - if (!msgInit) { - MSG_initHuffman(); - } - Com_Memset (buf, 0, sizeof(*buf)); - buf->data = data; - buf->maxsize = length; -} - -void MSG_InitOOB( msg_t *buf, byte *data, int length ) { - if (!msgInit) { - MSG_initHuffman(); - } - Com_Memset (buf, 0, sizeof(*buf)); - buf->data = data; - buf->maxsize = length; - buf->oob = qtrue; -} - -void MSG_Clear( msg_t *buf ) { - buf->cursize = 0; - buf->overflowed = qfalse; - buf->bit = 0; //<- in bits -} - - -void MSG_Bitstream( msg_t *buf ) { - buf->oob = qfalse; -} - -void MSG_BeginReading( msg_t *msg ) { - msg->readcount = 0; - msg->bit = 0; - msg->oob = qfalse; -} - -void MSG_BeginReadingOOB( msg_t *msg ) { - msg->readcount = 0; - msg->bit = 0; - msg->oob = qtrue; -} - -void MSG_Copy(msg_t *buf, byte *data, int length, msg_t *src) -{ - if (lengthcursize) { - Com_Error( ERR_DROP, "MSG_Copy: can't copy into a smaller msg_t buffer"); - } - Com_Memcpy(buf, src, sizeof(msg_t)); - buf->data = data; - Com_Memcpy(buf->data, src->data, src->cursize); -} - -/* -============================================================================= - -bit functions - -============================================================================= -*/ - -int overflows; - -// negative bit values include signs -void MSG_WriteBits( msg_t *msg, int value, int bits ) { - int i; -// FILE* fp; - - oldsize += bits; - - // this isn't an exact overflow check, but close enough - if ( msg->maxsize - msg->cursize < 4 ) { - msg->overflowed = qtrue; - return; - } - - if ( bits == 0 || bits < -31 || bits > 32 ) { - Com_Error( ERR_DROP, "MSG_WriteBits: bad bits %i", bits ); - } - - // check for overflows - if ( bits != 32 ) { - if ( bits > 0 ) { - if ( value > ( ( 1 << bits ) - 1 ) || value < 0 ) { - overflows++; - } - } else { - int r; - - r = 1 << (bits-1); - - if ( value > r - 1 || value < -r ) { - overflows++; - } - } - } - if ( bits < 0 ) { - bits = -bits; - } - if (msg->oob) { - if (bits==8) { - msg->data[msg->cursize] = value; - msg->cursize += 1; - msg->bit += 8; - } else if (bits==16) { - unsigned short *sp = (unsigned short *)&msg->data[msg->cursize]; - *sp = LittleShort(value); - msg->cursize += 2; - msg->bit += 16; - } else if (bits==32) { - unsigned int *ip = (unsigned int *)&msg->data[msg->cursize]; - *ip = LittleLong(value); - msg->cursize += 4; - msg->bit += 8; - } else { - Com_Error(ERR_DROP, "can't read %d bits\n", bits); - } - } else { -// fp = fopen("c:\\netchan.bin", "a"); - value &= (0xffffffff>>(32-bits)); - if (bits&7) { - int nbits; - nbits = bits&7; - for(i=0;idata, &msg->bit); - value = (value>>1); - } - bits = bits - nbits; - } - if (bits) { - for(i=0;idata, &msg->bit); - value = (value>>8); - } - } - msg->cursize = (msg->bit>>3)+1; -// fclose(fp); - } -} - -int MSG_ReadBits( msg_t *msg, int bits ) { - int value; - int get; - qboolean sgn; - int i, nbits; -// FILE* fp; - - value = 0; - - if ( bits < 0 ) { - bits = -bits; - sgn = qtrue; - } else { - sgn = qfalse; - } - - if (msg->oob) { - if (bits==8) { - value = msg->data[msg->readcount]; - msg->readcount += 1; - msg->bit += 8; - } else if (bits==16) { - unsigned short *sp = (unsigned short *)&msg->data[msg->readcount]; - value = LittleShort(*sp); - msg->readcount += 2; - msg->bit += 16; - } else if (bits==32) { - unsigned int *ip = (unsigned int *)&msg->data[msg->readcount]; - value = LittleLong(*ip); - msg->readcount += 4; - msg->bit += 32; - } else { - Com_Error(ERR_DROP, "can't read %d bits\n", bits); - } - } else { - nbits = 0; - if (bits&7) { - nbits = bits&7; - for(i=0;idata, &msg->bit)<data, &msg->bit); -// fwrite(&get, 1, 1, fp); - value |= (get<<(i+nbits)); - } -// fclose(fp); - } - msg->readcount = (msg->bit>>3)+1; - } - if ( sgn ) { - if ( value & ( 1 << ( bits - 1 ) ) ) { - value |= -1 ^ ( ( 1 << bits ) - 1 ); - } - } - - return value; -} - - - -//================================================================================ - -// -// writing functions -// - -void MSG_WriteChar( msg_t *sb, int c ) { -#ifdef PARANOID - if (c < -128 || c > 127) - Com_Error (ERR_FATAL, "MSG_WriteChar: range error"); -#endif - - MSG_WriteBits( sb, c, 8 ); -} - -void MSG_WriteByte( msg_t *sb, int c ) { -#ifdef PARANOID - if (c < 0 || c > 255) - Com_Error (ERR_FATAL, "MSG_WriteByte: range error"); -#endif - - MSG_WriteBits( sb, c, 8 ); -} - -void MSG_WriteData( msg_t *buf, const void *data, int length ) { - int i; - for(i=0;i (short)0x7fff) - Com_Error (ERR_FATAL, "MSG_WriteShort: range error"); -#endif - - MSG_WriteBits( sb, c, 16 ); -} - -void MSG_WriteLong( msg_t *sb, int c ) { - MSG_WriteBits( sb, c, 32 ); -} - -void MSG_WriteFloat( msg_t *sb, float f ) { - union { - float f; - int l; - } dat; - - dat.f = f; - MSG_WriteBits( sb, dat.l, 32 ); -} - -void MSG_WriteString( msg_t *sb, const char *s ) { - if ( !s ) { - MSG_WriteData (sb, "", 1); - } else { - int l,i; - char string[MAX_STRING_CHARS]; - - l = strlen( s ); - if ( l >= MAX_STRING_CHARS ) { - Com_Printf( "MSG_WriteString: MAX_STRING_CHARS" ); - MSG_WriteData (sb, "", 1); - return; - } - Q_strncpyz( string, s, sizeof( string ) ); - - // get rid of 0xff chars, because old clients don't like them - for ( i = 0 ; i < l ; i++ ) { - if ( ((byte *)string)[i] > 127 ) { - string[i] = '.'; - } - } - - MSG_WriteData (sb, string, l+1); - } -} - -void MSG_WriteBigString( msg_t *sb, const char *s ) { - if ( !s ) { - MSG_WriteData (sb, "", 1); - } else { - int l,i; - char string[BIG_INFO_STRING]; - - l = strlen( s ); - if ( l >= BIG_INFO_STRING ) { - Com_Printf( "MSG_WriteString: BIG_INFO_STRING" ); - MSG_WriteData (sb, "", 1); - return; - } - Q_strncpyz( string, s, sizeof( string ) ); - - // get rid of 0xff chars, because old clients don't like them - for ( i = 0 ; i < l ; i++ ) { - if ( ((byte *)string)[i] > 127 ) { - string[i] = '.'; - } - } - - MSG_WriteData (sb, string, l+1); - } -} - -void MSG_WriteAngle( msg_t *sb, float f ) { - MSG_WriteByte (sb, (int)(f*256/360) & 255); -} - -void MSG_WriteAngle16( msg_t *sb, float f ) { - MSG_WriteShort (sb, ANGLE2SHORT(f)); -} - - -//============================================================ - -// -// reading functions -// - -// returns -1 if no more characters are available -int MSG_ReadChar (msg_t *msg ) { - int c; - - c = (signed char)MSG_ReadBits( msg, 8 ); - if ( msg->readcount > msg->cursize ) { - c = -1; - } - - return c; -} - -int MSG_ReadByte( msg_t *msg ) { - int c; - - c = (unsigned char)MSG_ReadBits( msg, 8 ); - if ( msg->readcount > msg->cursize ) { - c = -1; - } - return c; -} - -int MSG_ReadShort( msg_t *msg ) { - int c; - - c = (short)MSG_ReadBits( msg, 16 ); - if ( msg->readcount > msg->cursize ) { - c = -1; - } - - return c; -} - -int MSG_ReadLong( msg_t *msg ) { - int c; - - c = MSG_ReadBits( msg, 32 ); - if ( msg->readcount > msg->cursize ) { - c = -1; - } - - return c; -} - -float MSG_ReadFloat( msg_t *msg ) { - union { - byte b[4]; - float f; - int l; - } dat; - - dat.l = MSG_ReadBits( msg, 32 ); - if ( msg->readcount > msg->cursize ) { - dat.f = -1; - } - - return dat.f; -} - -char *MSG_ReadString( msg_t *msg ) { - static char string[MAX_STRING_CHARS]; - int l,c; - - l = 0; - do { - c = MSG_ReadByte(msg); // use ReadByte so -1 is out of bounds - if ( c == -1 || c == 0 ) { - break; - } - // translate all fmt spec to avoid crash bugs - if ( c == '%' ) { - c = '.'; - } - // don't allow higher ascii values - if ( c > 127 ) { - c = '.'; - } - - string[l] = c; - l++; - } while (l < sizeof(string)-1); - - string[l] = 0; - - return string; -} - -char *MSG_ReadBigString( msg_t *msg ) { - static char string[BIG_INFO_STRING]; - int l,c; - - l = 0; - do { - c = MSG_ReadByte(msg); // use ReadByte so -1 is out of bounds - if ( c == -1 || c == 0 ) { - break; - } - // translate all fmt spec to avoid crash bugs - if ( c == '%' ) { - c = '.'; - } - - string[l] = c; - l++; - } while (l < sizeof(string)-1); - - string[l] = 0; - - return string; -} - -char *MSG_ReadStringLine( msg_t *msg ) { - static char string[MAX_STRING_CHARS]; - int l,c; - - l = 0; - do { - c = MSG_ReadByte(msg); // use ReadByte so -1 is out of bounds - if (c == -1 || c == 0 || c == '\n') { - break; - } - // translate all fmt spec to avoid crash bugs - if ( c == '%' ) { - c = '.'; - } - string[l] = c; - l++; - } while (l < sizeof(string)-1); - - string[l] = 0; - - return string; -} - -float MSG_ReadAngle16( msg_t *msg ) { - return SHORT2ANGLE(MSG_ReadShort(msg)); -} - -void MSG_ReadData( msg_t *msg, void *data, int len ) { - int i; - - for (i=0 ; iinteger == 4 ) { Com_Printf("%s ", x ); }; - -void MSG_WriteDelta( msg_t *msg, int oldV, int newV, int bits ) { - if ( oldV == newV ) { - MSG_WriteBits( msg, 0, 1 ); - return; - } - MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, newV, bits ); -} - -int MSG_ReadDelta( msg_t *msg, int oldV, int bits ) { - if ( MSG_ReadBits( msg, 1 ) ) { - return MSG_ReadBits( msg, bits ); - } - return oldV; -} - -void MSG_WriteDeltaFloat( msg_t *msg, float oldV, float newV ) { - if ( oldV == newV ) { - MSG_WriteBits( msg, 0, 1 ); - return; - } - MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, *(int *)&newV, 32 ); -} - -float MSG_ReadDeltaFloat( msg_t *msg, float oldV ) { - if ( MSG_ReadBits( msg, 1 ) ) { - float newV; - - *(int *)&newV = MSG_ReadBits( msg, 32 ); - return newV; - } - return oldV; -} - -/* -============================================================================= - -delta functions with keys - -============================================================================= -*/ - -int kbitmask[32] = { - 0x00000001, 0x00000003, 0x00000007, 0x0000000F, - 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, - 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, - 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, - 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, - 0x001FFFFf, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, - 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, - 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, -}; - -void MSG_WriteDeltaKey( msg_t *msg, int key, int oldV, int newV, int bits ) { - if ( oldV == newV ) { - MSG_WriteBits( msg, 0, 1 ); - return; - } - MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, newV ^ key, bits ); -} - -int MSG_ReadDeltaKey( msg_t *msg, int key, int oldV, int bits ) { - if ( MSG_ReadBits( msg, 1 ) ) { - return MSG_ReadBits( msg, bits ) ^ (key & kbitmask[bits]); - } - return oldV; -} - -void MSG_WriteDeltaKeyFloat( msg_t *msg, int key, float oldV, float newV ) { - if ( oldV == newV ) { - MSG_WriteBits( msg, 0, 1 ); - return; - } - MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, (*(int *)&newV) ^ key, 32 ); -} - -float MSG_ReadDeltaKeyFloat( msg_t *msg, int key, float oldV ) { - if ( MSG_ReadBits( msg, 1 ) ) { - float newV; - - *(int *)&newV = MSG_ReadBits( msg, 32 ) ^ key; - return newV; - } - return oldV; -} - - -/* -============================================================================ - -usercmd_t communication - -============================================================================ -*/ - -// ms is allways sent, the others are optional -#define CM_ANGLE1 (1<<0) -#define CM_ANGLE2 (1<<1) -#define CM_ANGLE3 (1<<2) -#define CM_FORWARD (1<<3) -#define CM_SIDE (1<<4) -#define CM_UP (1<<5) -#define CM_BUTTONS (1<<6) -#define CM_WEAPON (1<<7) - -/* -===================== -MSG_WriteDeltaUsercmd -===================== -*/ -void MSG_WriteDeltaUsercmd( msg_t *msg, usercmd_t *from, usercmd_t *to ) { - if ( to->serverTime - from->serverTime < 256 ) { - MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, to->serverTime - from->serverTime, 8 ); - } else { - MSG_WriteBits( msg, 0, 1 ); - MSG_WriteBits( msg, to->serverTime, 32 ); - } - MSG_WriteDelta( msg, from->angles[0], to->angles[0], 16 ); - MSG_WriteDelta( msg, from->angles[1], to->angles[1], 16 ); - MSG_WriteDelta( msg, from->angles[2], to->angles[2], 16 ); - MSG_WriteDelta( msg, from->forwardmove, to->forwardmove, 8 ); - MSG_WriteDelta( msg, from->rightmove, to->rightmove, 8 ); - MSG_WriteDelta( msg, from->upmove, to->upmove, 8 ); - MSG_WriteDelta( msg, from->buttons, to->buttons, 16 ); - MSG_WriteDelta( msg, from->weapon, to->weapon, 8 ); -} - - -/* -===================== -MSG_ReadDeltaUsercmd -===================== -*/ -void MSG_ReadDeltaUsercmd( msg_t *msg, usercmd_t *from, usercmd_t *to ) { - if ( MSG_ReadBits( msg, 1 ) ) { - to->serverTime = from->serverTime + MSG_ReadBits( msg, 8 ); - } else { - to->serverTime = MSG_ReadBits( msg, 32 ); - } - to->angles[0] = MSG_ReadDelta( msg, from->angles[0], 16); - to->angles[1] = MSG_ReadDelta( msg, from->angles[1], 16); - to->angles[2] = MSG_ReadDelta( msg, from->angles[2], 16); - to->forwardmove = MSG_ReadDelta( msg, from->forwardmove, 8); - to->rightmove = MSG_ReadDelta( msg, from->rightmove, 8); - to->upmove = MSG_ReadDelta( msg, from->upmove, 8); - to->buttons = MSG_ReadDelta( msg, from->buttons, 16); - to->weapon = MSG_ReadDelta( msg, from->weapon, 8); -} - -/* -===================== -MSG_WriteDeltaUsercmd -===================== -*/ -void MSG_WriteDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ) { - if ( to->serverTime - from->serverTime < 256 ) { - MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, to->serverTime - from->serverTime, 8 ); - } else { - MSG_WriteBits( msg, 0, 1 ); - MSG_WriteBits( msg, to->serverTime, 32 ); - } - if (from->angles[0] == to->angles[0] && - from->angles[1] == to->angles[1] && - from->angles[2] == to->angles[2] && - from->forwardmove == to->forwardmove && - from->rightmove == to->rightmove && - from->upmove == to->upmove && - from->buttons == to->buttons && - from->weapon == to->weapon) { - MSG_WriteBits( msg, 0, 1 ); // no change - oldsize += 7; - return; - } - key ^= to->serverTime; - MSG_WriteBits( msg, 1, 1 ); - MSG_WriteDeltaKey( msg, key, from->angles[0], to->angles[0], 16 ); - MSG_WriteDeltaKey( msg, key, from->angles[1], to->angles[1], 16 ); - MSG_WriteDeltaKey( msg, key, from->angles[2], to->angles[2], 16 ); - MSG_WriteDeltaKey( msg, key, from->forwardmove, to->forwardmove, 8 ); - MSG_WriteDeltaKey( msg, key, from->rightmove, to->rightmove, 8 ); - MSG_WriteDeltaKey( msg, key, from->upmove, to->upmove, 8 ); - MSG_WriteDeltaKey( msg, key, from->buttons, to->buttons, 16 ); - MSG_WriteDeltaKey( msg, key, from->weapon, to->weapon, 8 ); -} - - -/* -===================== -MSG_ReadDeltaUsercmd -===================== -*/ -void MSG_ReadDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ) { - if ( MSG_ReadBits( msg, 1 ) ) { - to->serverTime = from->serverTime + MSG_ReadBits( msg, 8 ); - } else { - to->serverTime = MSG_ReadBits( msg, 32 ); - } - if ( MSG_ReadBits( msg, 1 ) ) { - key ^= to->serverTime; - to->angles[0] = MSG_ReadDeltaKey( msg, key, from->angles[0], 16); - to->angles[1] = MSG_ReadDeltaKey( msg, key, from->angles[1], 16); - to->angles[2] = MSG_ReadDeltaKey( msg, key, from->angles[2], 16); - to->forwardmove = MSG_ReadDeltaKey( msg, key, from->forwardmove, 8); - to->rightmove = MSG_ReadDeltaKey( msg, key, from->rightmove, 8); - to->upmove = MSG_ReadDeltaKey( msg, key, from->upmove, 8); - to->buttons = MSG_ReadDeltaKey( msg, key, from->buttons, 16); - to->weapon = MSG_ReadDeltaKey( msg, key, from->weapon, 8); - } else { - to->angles[0] = from->angles[0]; - to->angles[1] = from->angles[1]; - to->angles[2] = from->angles[2]; - to->forwardmove = from->forwardmove; - to->rightmove = from->rightmove; - to->upmove = from->upmove; - to->buttons = from->buttons; - to->weapon = from->weapon; - } -} - -/* -============================================================================= - -entityState_t communication - -============================================================================= -*/ - -/* -================= -MSG_ReportChangeVectors_f - -Prints out a table from the current statistics for copying to code -================= -*/ -void MSG_ReportChangeVectors_f( void ) { - int i; - for(i=0;i<256;i++) { - if (pcount[i]) { - Com_Printf("%d used %d\n", i, pcount[i]); - } - } -} - -typedef struct { - char *name; - int offset; - int bits; // 0 = float -} netField_t; - -// using the stringizing operator to save typing... -#define NETF(x) #x,(int)&((entityState_t*)0)->x - -netField_t entityStateFields[] = -{ -{ NETF(pos.trTime), 32 }, -{ NETF(pos.trBase[0]), 0 }, -{ NETF(pos.trBase[1]), 0 }, -{ NETF(pos.trDelta[0]), 0 }, -{ NETF(pos.trDelta[1]), 0 }, -{ NETF(pos.trBase[2]), 0 }, -{ NETF(apos.trBase[1]), 0 }, -{ NETF(pos.trDelta[2]), 0 }, -{ NETF(apos.trBase[0]), 0 }, -{ NETF(event), 10 }, -{ NETF(angles2[1]), 0 }, -{ NETF(eType), 8 }, -{ NETF(torsoAnim), 8 }, -{ NETF(eventParm), 8 }, -{ NETF(legsAnim), 8 }, -{ NETF(groundEntityNum), GENTITYNUM_BITS }, -{ NETF(pos.trType), 8 }, -{ NETF(eFlags), 19 }, -{ NETF(otherEntityNum), GENTITYNUM_BITS }, -{ NETF(weapon), 8 }, -{ NETF(clientNum), 8 }, -{ NETF(angles[1]), 0 }, -{ NETF(pos.trDuration), 32 }, -{ NETF(apos.trType), 8 }, -{ NETF(origin[0]), 0 }, -{ NETF(origin[1]), 0 }, -{ NETF(origin[2]), 0 }, -{ NETF(solid), 24 }, -{ NETF(powerups), 16 }, -{ NETF(modelindex), 8 }, -{ NETF(otherEntityNum2), GENTITYNUM_BITS }, -{ NETF(loopSound), 8 }, -{ NETF(generic1), 8 }, -{ NETF(origin2[2]), 0 }, -{ NETF(origin2[0]), 0 }, -{ NETF(origin2[1]), 0 }, -{ NETF(modelindex2), 8 }, -{ NETF(angles[0]), 0 }, -{ NETF(time), 32 }, -{ NETF(apos.trTime), 32 }, -{ NETF(apos.trDuration), 32 }, -{ NETF(apos.trBase[2]), 0 }, -{ NETF(apos.trDelta[0]), 0 }, -{ NETF(apos.trDelta[1]), 0 }, -{ NETF(apos.trDelta[2]), 0 }, -{ NETF(time2), 32 }, -{ NETF(angles[2]), 0 }, -{ NETF(angles2[0]), 0 }, -{ NETF(angles2[2]), 0 }, -{ NETF(constantLight), 32 }, -{ NETF(frame), 16 } -}; - - -// if (int)f == f and (int)f + ( 1<<(FLOAT_INT_BITS-1) ) < ( 1 << FLOAT_INT_BITS ) -// the float will be sent with FLOAT_INT_BITS, otherwise all 32 bits will be sent -#define FLOAT_INT_BITS 13 -#define FLOAT_INT_BIAS (1<<(FLOAT_INT_BITS-1)) - -/* -================== -MSG_WriteDeltaEntity - -Writes part of a packetentities message, including the entity number. -Can delta from either a baseline or a previous packet_entity -If to is NULL, a remove entity update will be sent -If force is not set, then nothing at all will be generated if the entity is -identical, under the assumption that the in-order delta code will catch it. -================== -*/ -void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entityState_s *to, - qboolean force ) { - int i, lc; - int numFields; - netField_t *field; - int trunc; - float fullFloat; - int *fromF, *toF; - - numFields = sizeof(entityStateFields)/sizeof(entityStateFields[0]); - - // all fields should be 32 bits to avoid any compiler packing issues - // the "number" field is not part of the field list - // if this assert fails, someone added a field to the entityState_t - // struct without updating the message fields - assert( numFields + 1 == sizeof( *from )/4 ); - - // a NULL to is a delta remove message - if ( to == NULL ) { - if ( from == NULL ) { - return; - } - MSG_WriteBits( msg, from->number, GENTITYNUM_BITS ); - MSG_WriteBits( msg, 1, 1 ); - return; - } - - if ( to->number < 0 || to->number >= MAX_GENTITIES ) { - Com_Error (ERR_FATAL, "MSG_WriteDeltaEntity: Bad entity number: %i", to->number ); - } - - lc = 0; - // build the change vector as bytes so it is endien independent - for ( i = 0, field = entityStateFields ; i < numFields ; i++, field++ ) { - fromF = (int *)( (byte *)from + field->offset ); - toF = (int *)( (byte *)to + field->offset ); - if ( *fromF != *toF ) { - lc = i+1; - } - } - - if ( lc == 0 ) { - // nothing at all changed - if ( !force ) { - return; // nothing at all - } - // write two bits for no change - MSG_WriteBits( msg, to->number, GENTITYNUM_BITS ); - MSG_WriteBits( msg, 0, 1 ); // not removed - MSG_WriteBits( msg, 0, 1 ); // no delta - return; - } - - MSG_WriteBits( msg, to->number, GENTITYNUM_BITS ); - MSG_WriteBits( msg, 0, 1 ); // not removed - MSG_WriteBits( msg, 1, 1 ); // we have a delta - - MSG_WriteByte( msg, lc ); // # of changes - - oldsize += numFields; - - for ( i = 0, field = entityStateFields ; i < lc ; i++, field++ ) { - fromF = (int *)( (byte *)from + field->offset ); - toF = (int *)( (byte *)to + field->offset ); - - if ( *fromF == *toF ) { - MSG_WriteBits( msg, 0, 1 ); // no change - continue; - } - - MSG_WriteBits( msg, 1, 1 ); // changed - - if ( field->bits == 0 ) { - // float - fullFloat = *(float *)toF; - trunc = (int)fullFloat; - - if (fullFloat == 0.0f) { - MSG_WriteBits( msg, 0, 1 ); - oldsize += FLOAT_INT_BITS; - } else { - MSG_WriteBits( msg, 1, 1 ); - if ( trunc == fullFloat && trunc + FLOAT_INT_BIAS >= 0 && - trunc + FLOAT_INT_BIAS < ( 1 << FLOAT_INT_BITS ) ) { - // send as small integer - MSG_WriteBits( msg, 0, 1 ); - MSG_WriteBits( msg, trunc + FLOAT_INT_BIAS, FLOAT_INT_BITS ); - } else { - // send as full floating point value - MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, *toF, 32 ); - } - } - } else { - if (*toF == 0) { - MSG_WriteBits( msg, 0, 1 ); - } else { - MSG_WriteBits( msg, 1, 1 ); - // integer - MSG_WriteBits( msg, *toF, field->bits ); - } - } - } -} - -/* -================== -MSG_ReadDeltaEntity - -The entity number has already been read from the message, which -is how the from state is identified. - -If the delta removes the entity, entityState_t->number will be set to MAX_GENTITIES-1 - -Can go from either a baseline or a previous packet_entity -================== -*/ -extern cvar_t *cl_shownet; - -void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to, - int number) { - int i, lc; - int numFields; - netField_t *field; - int *fromF, *toF; - int print; - int trunc; - int startBit, endBit; - - if ( number < 0 || number >= MAX_GENTITIES) { - Com_Error( ERR_DROP, "Bad delta entity number: %i", number ); - } - - if ( msg->bit == 0 ) { - startBit = msg->readcount * 8 - GENTITYNUM_BITS; - } else { - startBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS; - } - - // check for a remove - if ( MSG_ReadBits( msg, 1 ) == 1 ) { - Com_Memset( to, 0, sizeof( *to ) ); - to->number = MAX_GENTITIES - 1; - if ( cl_shownet->integer >= 2 || cl_shownet->integer == -1 ) { - Com_Printf( "%3i: #%-3i remove\n", msg->readcount, number ); - } - return; - } - - // check for no delta - if ( MSG_ReadBits( msg, 1 ) == 0 ) { - *to = *from; - to->number = number; - return; - } - - numFields = sizeof(entityStateFields)/sizeof(entityStateFields[0]); - lc = MSG_ReadByte(msg); - - // shownet 2/3 will interleave with other printed info, -1 will - // just print the delta records` - if ( cl_shownet->integer >= 2 || cl_shownet->integer == -1 ) { - print = 1; - Com_Printf( "%3i: #%-3i ", msg->readcount, to->number ); - } else { - print = 0; - } - - to->number = number; - - for ( i = 0, field = entityStateFields ; i < lc ; i++, field++ ) { - fromF = (int *)( (byte *)from + field->offset ); - toF = (int *)( (byte *)to + field->offset ); - - if ( ! MSG_ReadBits( msg, 1 ) ) { - // no change - *toF = *fromF; - } else { - if ( field->bits == 0 ) { - // float - if ( MSG_ReadBits( msg, 1 ) == 0 ) { - *(float *)toF = 0.0f; - } else { - if ( MSG_ReadBits( msg, 1 ) == 0 ) { - // integral float - trunc = MSG_ReadBits( msg, FLOAT_INT_BITS ); - // bias to allow equal parts positive and negative - trunc -= FLOAT_INT_BIAS; - *(float *)toF = trunc; - if ( print ) { - Com_Printf( "%s:%i ", field->name, trunc ); - } - } else { - // full floating point value - *toF = MSG_ReadBits( msg, 32 ); - if ( print ) { - Com_Printf( "%s:%f ", field->name, *(float *)toF ); - } - } - } - } else { - if ( MSG_ReadBits( msg, 1 ) == 0 ) { - *toF = 0; - } else { - // integer - *toF = MSG_ReadBits( msg, field->bits ); - if ( print ) { - Com_Printf( "%s:%i ", field->name, *toF ); - } - } - } -// pcount[i]++; - } - } - for ( i = lc, field = &entityStateFields[lc] ; i < numFields ; i++, field++ ) { - fromF = (int *)( (byte *)from + field->offset ); - toF = (int *)( (byte *)to + field->offset ); - // no change - *toF = *fromF; - } - - if ( print ) { - if ( msg->bit == 0 ) { - endBit = msg->readcount * 8 - GENTITYNUM_BITS; - } else { - endBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS; - } - Com_Printf( " (%i bits)\n", endBit - startBit ); - } -} - - -/* -============================================================================ - -plyer_state_t communication - -============================================================================ -*/ - -// using the stringizing operator to save typing... -#define PSF(x) #x,(int)&((playerState_t*)0)->x - -netField_t playerStateFields[] = -{ -{ PSF(commandTime), 32 }, -{ PSF(origin[0]), 0 }, -{ PSF(origin[1]), 0 }, -{ PSF(bobCycle), 8 }, -{ PSF(velocity[0]), 0 }, -{ PSF(velocity[1]), 0 }, -{ PSF(viewangles[1]), 0 }, -{ PSF(viewangles[0]), 0 }, -{ PSF(weaponTime), -16 }, -{ PSF(origin[2]), 0 }, -{ PSF(velocity[2]), 0 }, -{ PSF(legsTimer), 8 }, -{ PSF(pm_time), -16 }, -{ PSF(eventSequence), 16 }, -{ PSF(torsoAnim), 8 }, -{ PSF(movementDir), 4 }, -{ PSF(events[0]), 8 }, -{ PSF(legsAnim), 8 }, -{ PSF(events[1]), 8 }, -{ PSF(pm_flags), 16 }, -{ PSF(groundEntityNum), GENTITYNUM_BITS }, -{ PSF(weaponstate), 4 }, -{ PSF(eFlags), 16 }, -{ PSF(externalEvent), 10 }, -{ PSF(gravity), 16 }, -{ PSF(speed), 16 }, -{ PSF(delta_angles[1]), 16 }, -{ PSF(externalEventParm), 8 }, -{ PSF(viewheight), -8 }, -{ PSF(damageEvent), 8 }, -{ PSF(damageYaw), 8 }, -{ PSF(damagePitch), 8 }, -{ PSF(damageCount), 8 }, -{ PSF(generic1), 8 }, -{ PSF(pm_type), 8 }, -{ PSF(delta_angles[0]), 16 }, -{ PSF(delta_angles[2]), 16 }, -{ PSF(torsoTimer), 12 }, -{ PSF(eventParms[0]), 8 }, -{ PSF(eventParms[1]), 8 }, -{ PSF(clientNum), 8 }, -{ PSF(weapon), 5 }, -{ PSF(viewangles[2]), 0 }, -{ PSF(grapplePoint[0]), 0 }, -{ PSF(grapplePoint[1]), 0 }, -{ PSF(grapplePoint[2]), 0 }, -{ PSF(jumppad_ent), 10 }, -{ PSF(loopSound), 16 } -}; - -/* -============= -MSG_WriteDeltaPlayerstate - -============= -*/ -void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to ) { - int i; - playerState_t dummy; - int statsbits; - int persistantbits; - int ammobits; - int powerupbits; - int numFields; - int c; - netField_t *field; - int *fromF, *toF; - float fullFloat; - int trunc, lc; - - if (!from) { - from = &dummy; - Com_Memset (&dummy, 0, sizeof(dummy)); - } - - c = msg->cursize; - - numFields = sizeof( playerStateFields ) / sizeof( playerStateFields[0] ); - - lc = 0; - for ( i = 0, field = playerStateFields ; i < numFields ; i++, field++ ) { - fromF = (int *)( (byte *)from + field->offset ); - toF = (int *)( (byte *)to + field->offset ); - if ( *fromF != *toF ) { - lc = i+1; - } - } - - MSG_WriteByte( msg, lc ); // # of changes - - oldsize += numFields - lc; - - for ( i = 0, field = playerStateFields ; i < lc ; i++, field++ ) { - fromF = (int *)( (byte *)from + field->offset ); - toF = (int *)( (byte *)to + field->offset ); - - if ( *fromF == *toF ) { - MSG_WriteBits( msg, 0, 1 ); // no change - continue; - } - - MSG_WriteBits( msg, 1, 1 ); // changed -// pcount[i]++; - - if ( field->bits == 0 ) { - // float - fullFloat = *(float *)toF; - trunc = (int)fullFloat; - - if ( trunc == fullFloat && trunc + FLOAT_INT_BIAS >= 0 && - trunc + FLOAT_INT_BIAS < ( 1 << FLOAT_INT_BITS ) ) { - // send as small integer - MSG_WriteBits( msg, 0, 1 ); - MSG_WriteBits( msg, trunc + FLOAT_INT_BIAS, FLOAT_INT_BITS ); - } else { - // send as full floating point value - MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, *toF, 32 ); - } - } else { - // integer - MSG_WriteBits( msg, *toF, field->bits ); - } - } - c = msg->cursize - c; - - - // - // send the arrays - // - statsbits = 0; - for (i=0 ; i<16 ; i++) { - if (to->stats[i] != from->stats[i]) { - statsbits |= 1<persistant[i] != from->persistant[i]) { - persistantbits |= 1<ammo[i] != from->ammo[i]) { - ammobits |= 1<powerups[i] != from->powerups[i]) { - powerupbits |= 1<stats[i]); - } else { - MSG_WriteBits( msg, 0, 1 ); // no change - } - - - if ( persistantbits ) { - MSG_WriteBits( msg, 1, 1 ); // changed - MSG_WriteShort( msg, persistantbits ); - for (i=0 ; i<16 ; i++) - if (persistantbits & (1<persistant[i]); - } else { - MSG_WriteBits( msg, 0, 1 ); // no change - } - - - if ( ammobits ) { - MSG_WriteBits( msg, 1, 1 ); // changed - MSG_WriteShort( msg, ammobits ); - for (i=0 ; i<16 ; i++) - if (ammobits & (1<ammo[i]); - } else { - MSG_WriteBits( msg, 0, 1 ); // no change - } - - - if ( powerupbits ) { - MSG_WriteBits( msg, 1, 1 ); // changed - MSG_WriteShort( msg, powerupbits ); - for (i=0 ; i<16 ; i++) - if (powerupbits & (1<powerups[i] ); - } else { - MSG_WriteBits( msg, 0, 1 ); // no change - } -} - - -/* -=================== -MSG_ReadDeltaPlayerstate -=================== -*/ -void MSG_ReadDeltaPlayerstate (msg_t *msg, playerState_t *from, playerState_t *to ) { - int i, lc; - int bits; - netField_t *field; - int numFields; - int startBit, endBit; - int print; - int *fromF, *toF; - int trunc; - playerState_t dummy; - - if ( !from ) { - from = &dummy; - Com_Memset( &dummy, 0, sizeof( dummy ) ); - } - *to = *from; - - if ( msg->bit == 0 ) { - startBit = msg->readcount * 8 - GENTITYNUM_BITS; - } else { - startBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS; - } - - // shownet 2/3 will interleave with other printed info, -2 will - // just print the delta records - if ( cl_shownet->integer >= 2 || cl_shownet->integer == -2 ) { - print = 1; - Com_Printf( "%3i: playerstate ", msg->readcount ); - } else { - print = 0; - } - - numFields = sizeof( playerStateFields ) / sizeof( playerStateFields[0] ); - lc = MSG_ReadByte(msg); - - for ( i = 0, field = playerStateFields ; i < lc ; i++, field++ ) { - fromF = (int *)( (byte *)from + field->offset ); - toF = (int *)( (byte *)to + field->offset ); - - if ( ! MSG_ReadBits( msg, 1 ) ) { - // no change - *toF = *fromF; - } else { - if ( field->bits == 0 ) { - // float - if ( MSG_ReadBits( msg, 1 ) == 0 ) { - // integral float - trunc = MSG_ReadBits( msg, FLOAT_INT_BITS ); - // bias to allow equal parts positive and negative - trunc -= FLOAT_INT_BIAS; - *(float *)toF = trunc; - if ( print ) { - Com_Printf( "%s:%i ", field->name, trunc ); - } - } else { - // full floating point value - *toF = MSG_ReadBits( msg, 32 ); - if ( print ) { - Com_Printf( "%s:%f ", field->name, *(float *)toF ); - } - } - } else { - // integer - *toF = MSG_ReadBits( msg, field->bits ); - if ( print ) { - Com_Printf( "%s:%i ", field->name, *toF ); - } - } - } - } - for ( i=lc,field = &playerStateFields[lc];ioffset ); - toF = (int *)( (byte *)to + field->offset ); - // no change - *toF = *fromF; - } - - - // read the arrays - if (MSG_ReadBits( msg, 1 ) ) { - // parse stats - if ( MSG_ReadBits( msg, 1 ) ) { - LOG("PS_STATS"); - bits = MSG_ReadShort (msg); - for (i=0 ; i<16 ; i++) { - if (bits & (1<stats[i] = MSG_ReadShort(msg); - } - } - } - - // parse persistant stats - if ( MSG_ReadBits( msg, 1 ) ) { - LOG("PS_PERSISTANT"); - bits = MSG_ReadShort (msg); - for (i=0 ; i<16 ; i++) { - if (bits & (1<persistant[i] = MSG_ReadShort(msg); - } - } - } - - // parse ammo - if ( MSG_ReadBits( msg, 1 ) ) { - LOG("PS_AMMO"); - bits = MSG_ReadShort (msg); - for (i=0 ; i<16 ; i++) { - if (bits & (1<ammo[i] = MSG_ReadShort(msg); - } - } - } - - // parse powerups - if ( MSG_ReadBits( msg, 1 ) ) { - LOG("PS_POWERUPS"); - bits = MSG_ReadShort (msg); - for (i=0 ; i<16 ; i++) { - if (bits & (1<powerups[i] = MSG_ReadLong(msg); - } - } - } - } - - if ( print ) { - if ( msg->bit == 0 ) { - endBit = msg->readcount * 8 - GENTITYNUM_BITS; - } else { - endBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS; - } - Com_Printf( " (%i bits)\n", endBit - startBit ); - } -} - -int msg_hData[256] = { -250315, // 0 -41193, // 1 -6292, // 2 -7106, // 3 -3730, // 4 -3750, // 5 -6110, // 6 -23283, // 7 -33317, // 8 -6950, // 9 -7838, // 10 -9714, // 11 -9257, // 12 -17259, // 13 -3949, // 14 -1778, // 15 -8288, // 16 -1604, // 17 -1590, // 18 -1663, // 19 -1100, // 20 -1213, // 21 -1238, // 22 -1134, // 23 -1749, // 24 -1059, // 25 -1246, // 26 -1149, // 27 -1273, // 28 -4486, // 29 -2805, // 30 -3472, // 31 -21819, // 32 -1159, // 33 -1670, // 34 -1066, // 35 -1043, // 36 -1012, // 37 -1053, // 38 -1070, // 39 -1726, // 40 -888, // 41 -1180, // 42 -850, // 43 -960, // 44 -780, // 45 -1752, // 46 -3296, // 47 -10630, // 48 -4514, // 49 -5881, // 50 -2685, // 51 -4650, // 52 -3837, // 53 -2093, // 54 -1867, // 55 -2584, // 56 -1949, // 57 -1972, // 58 -940, // 59 -1134, // 60 -1788, // 61 -1670, // 62 -1206, // 63 -5719, // 64 -6128, // 65 -7222, // 66 -6654, // 67 -3710, // 68 -3795, // 69 -1492, // 70 -1524, // 71 -2215, // 72 -1140, // 73 -1355, // 74 -971, // 75 -2180, // 76 -1248, // 77 -1328, // 78 -1195, // 79 -1770, // 80 -1078, // 81 -1264, // 82 -1266, // 83 -1168, // 84 -965, // 85 -1155, // 86 -1186, // 87 -1347, // 88 -1228, // 89 -1529, // 90 -1600, // 91 -2617, // 92 -2048, // 93 -2546, // 94 -3275, // 95 -2410, // 96 -3585, // 97 -2504, // 98 -2800, // 99 -2675, // 100 -6146, // 101 -3663, // 102 -2840, // 103 -14253, // 104 -3164, // 105 -2221, // 106 -1687, // 107 -3208, // 108 -2739, // 109 -3512, // 110 -4796, // 111 -4091, // 112 -3515, // 113 -5288, // 114 -4016, // 115 -7937, // 116 -6031, // 117 -5360, // 118 -3924, // 119 -4892, // 120 -3743, // 121 -4566, // 122 -4807, // 123 -5852, // 124 -6400, // 125 -6225, // 126 -8291, // 127 -23243, // 128 -7838, // 129 -7073, // 130 -8935, // 131 -5437, // 132 -4483, // 133 -3641, // 134 -5256, // 135 -5312, // 136 -5328, // 137 -5370, // 138 -3492, // 139 -2458, // 140 -1694, // 141 -1821, // 142 -2121, // 143 -1916, // 144 -1149, // 145 -1516, // 146 -1367, // 147 -1236, // 148 -1029, // 149 -1258, // 150 -1104, // 151 -1245, // 152 -1006, // 153 -1149, // 154 -1025, // 155 -1241, // 156 -952, // 157 -1287, // 158 -997, // 159 -1713, // 160 -1009, // 161 -1187, // 162 -879, // 163 -1099, // 164 -929, // 165 -1078, // 166 -951, // 167 -1656, // 168 -930, // 169 -1153, // 170 -1030, // 171 -1262, // 172 -1062, // 173 -1214, // 174 -1060, // 175 -1621, // 176 -930, // 177 -1106, // 178 -912, // 179 -1034, // 180 -892, // 181 -1158, // 182 -990, // 183 -1175, // 184 -850, // 185 -1121, // 186 -903, // 187 -1087, // 188 -920, // 189 -1144, // 190 -1056, // 191 -3462, // 192 -2240, // 193 -4397, // 194 -12136, // 195 -7758, // 196 -1345, // 197 -1307, // 198 -3278, // 199 -1950, // 200 -886, // 201 -1023, // 202 -1112, // 203 -1077, // 204 -1042, // 205 -1061, // 206 -1071, // 207 -1484, // 208 -1001, // 209 -1096, // 210 -915, // 211 -1052, // 212 -995, // 213 -1070, // 214 -876, // 215 -1111, // 216 -851, // 217 -1059, // 218 -805, // 219 -1112, // 220 -923, // 221 -1103, // 222 -817, // 223 -1899, // 224 -1872, // 225 -976, // 226 -841, // 227 -1127, // 228 -956, // 229 -1159, // 230 -950, // 231 -7791, // 232 -954, // 233 -1289, // 234 -933, // 235 -1127, // 236 -3207, // 237 -1020, // 238 -927, // 239 -1355, // 240 -768, // 241 -1040, // 242 -745, // 243 -952, // 244 -805, // 245 -1073, // 246 -740, // 247 -1013, // 248 -805, // 249 -1008, // 250 -796, // 251 -996, // 252 -1057, // 253 -11457, // 254 -13504, // 255 -}; - -void MSG_initHuffman() { - int i,j; - - msgInit = qtrue; - Huff_Init(&msgHuff); - for(i=0;i<256;i++) { - for (j=0;jsock = sock; - chan->remoteAddress = adr; - chan->qport = qport; - chan->incomingSequence = 0; - chan->outgoingSequence = 1; -} - -// TTimo: unused, commenting out to make gcc happy -#if 0 -/* -============== -Netchan_ScramblePacket - -A probably futile attempt to make proxy hacking somewhat -more difficult. -============== -*/ -#define SCRAMBLE_START 6 -static void Netchan_ScramblePacket( msg_t *buf ) { - unsigned seed; - int i, j, c, mask, temp; - int seq[MAX_PACKETLEN]; - - seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 ); - c = buf->cursize; - if ( c <= SCRAMBLE_START ) { - return; - } - if ( c > MAX_PACKETLEN ) { - Com_Error( ERR_DROP, "MAX_PACKETLEN" ); - } - - // generate a sequence of "random" numbers - for (i = 0 ; i < c ; i++) { - seed = (119 * seed + 1); - seq[i] = seed; - } - - // transpose each character - for ( mask = 1 ; mask < c-SCRAMBLE_START ; mask = ( mask << 1 ) + 1 ) { - } - mask >>= 1; - for (i = SCRAMBLE_START ; i < c ; i++) { - j = SCRAMBLE_START + ( seq[i] & mask ); - temp = buf->data[j]; - buf->data[j] = buf->data[i]; - buf->data[i] = temp; - } - - // byte xor the data after the header - for (i = SCRAMBLE_START ; i < c ; i++) { - buf->data[i] ^= seq[i]; - } -} - -static void Netchan_UnScramblePacket( msg_t *buf ) { - unsigned seed; - int i, j, c, mask, temp; - int seq[MAX_PACKETLEN]; - - seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 ); - c = buf->cursize; - if ( c <= SCRAMBLE_START ) { - return; - } - if ( c > MAX_PACKETLEN ) { - Com_Error( ERR_DROP, "MAX_PACKETLEN" ); - } - - // generate a sequence of "random" numbers - for (i = 0 ; i < c ; i++) { - seed = (119 * seed + 1); - seq[i] = seed; - } - - // byte xor the data after the header - for (i = SCRAMBLE_START ; i < c ; i++) { - buf->data[i] ^= seq[i]; - } - - // transpose each character in reverse order - for ( mask = 1 ; mask < c-SCRAMBLE_START ; mask = ( mask << 1 ) + 1 ) { - } - mask >>= 1; - for (i = c-1 ; i >= SCRAMBLE_START ; i--) { - j = SCRAMBLE_START + ( seq[i] & mask ); - temp = buf->data[j]; - buf->data[j] = buf->data[i]; - buf->data[i] = temp; - } -} -#endif - -/* -================= -Netchan_TransmitNextFragment - -Send one fragment of the current message -================= -*/ -void Netchan_TransmitNextFragment( netchan_t *chan ) { - msg_t send; - byte send_buf[MAX_PACKETLEN]; - int fragmentLength; - - // write the packet header - MSG_InitOOB (&send, send_buf, sizeof(send_buf)); // <-- only do the oob here - - MSG_WriteLong( &send, chan->outgoingSequence | FRAGMENT_BIT ); - - // send the qport if we are a client - if ( chan->sock == NS_CLIENT ) { - MSG_WriteShort( &send, qport->integer ); - } - - // copy the reliable message to the packet first - fragmentLength = FRAGMENT_SIZE; - if ( chan->unsentFragmentStart + fragmentLength > chan->unsentLength ) { - fragmentLength = chan->unsentLength - chan->unsentFragmentStart; - } - - MSG_WriteShort( &send, chan->unsentFragmentStart ); - MSG_WriteShort( &send, fragmentLength ); - MSG_WriteData( &send, chan->unsentBuffer + chan->unsentFragmentStart, fragmentLength ); - - // send the datagram - NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); - - if ( showpackets->integer ) { - Com_Printf ("%s send %4i : s=%i fragment=%i,%i\n" - , netsrcString[ chan->sock ] - , send.cursize - , chan->outgoingSequence - , chan->unsentFragmentStart, fragmentLength); - } - - chan->unsentFragmentStart += fragmentLength; - - // this exit condition is a little tricky, because a packet - // that is exactly the fragment length still needs to send - // a second packet of zero length so that the other side - // can tell there aren't more to follow - if ( chan->unsentFragmentStart == chan->unsentLength && fragmentLength != FRAGMENT_SIZE ) { - chan->outgoingSequence++; - chan->unsentFragments = qfalse; - } -} - - -/* -=============== -Netchan_Transmit - -Sends a message to a connection, fragmenting if necessary -A 0 length will still generate a packet. -================ -*/ -void Netchan_Transmit( netchan_t *chan, int length, const byte *data ) { - msg_t send; - byte send_buf[MAX_PACKETLEN]; - - if ( length > MAX_MSGLEN ) { - Com_Error( ERR_DROP, "Netchan_Transmit: length = %i", length ); - } - chan->unsentFragmentStart = 0; - - // fragment large reliable messages - if ( length >= FRAGMENT_SIZE ) { - chan->unsentFragments = qtrue; - chan->unsentLength = length; - Com_Memcpy( chan->unsentBuffer, data, length ); - - // only send the first fragment now - Netchan_TransmitNextFragment( chan ); - - return; - } - - // write the packet header - MSG_InitOOB (&send, send_buf, sizeof(send_buf)); - - MSG_WriteLong( &send, chan->outgoingSequence ); - chan->outgoingSequence++; - - // send the qport if we are a client - if ( chan->sock == NS_CLIENT ) { - MSG_WriteShort( &send, qport->integer ); - } - - MSG_WriteData( &send, data, length ); - - // send the datagram - NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); - - if ( showpackets->integer ) { - Com_Printf( "%s send %4i : s=%i ack=%i\n" - , netsrcString[ chan->sock ] - , send.cursize - , chan->outgoingSequence - 1 - , chan->incomingSequence ); - } -} - -/* -================= -Netchan_Process - -Returns qfalse if the message should not be processed due to being -out of order or a fragment. - -Msg must be large enough to hold MAX_MSGLEN, because if this is the -final fragment of a multi-part message, the entire thing will be -copied out. -================= -*/ -qboolean Netchan_Process( netchan_t *chan, msg_t *msg ) { - int sequence; - int qport; - int fragmentStart, fragmentLength; - qboolean fragmented; - - // XOR unscramble all data in the packet after the header -// Netchan_UnScramblePacket( msg ); - - // get sequence numbers - MSG_BeginReadingOOB( msg ); - sequence = MSG_ReadLong( msg ); - - // check for fragment information - if ( sequence & FRAGMENT_BIT ) { - sequence &= ~FRAGMENT_BIT; - fragmented = qtrue; - } else { - fragmented = qfalse; - } - - // read the qport if we are a server - if ( chan->sock == NS_SERVER ) { - qport = MSG_ReadShort( msg ); - } - - // read the fragment information - if ( fragmented ) { - fragmentStart = MSG_ReadShort( msg ); - fragmentLength = MSG_ReadShort( msg ); - } else { - fragmentStart = 0; // stop warning message - fragmentLength = 0; - } - - if ( showpackets->integer ) { - if ( fragmented ) { - Com_Printf( "%s recv %4i : s=%i fragment=%i,%i\n" - , netsrcString[ chan->sock ] - , msg->cursize - , sequence - , fragmentStart, fragmentLength ); - } else { - Com_Printf( "%s recv %4i : s=%i\n" - , netsrcString[ chan->sock ] - , msg->cursize - , sequence ); - } - } - - // - // discard out of order or duplicated packets - // - if ( sequence <= chan->incomingSequence ) { - if ( showdrop->integer || showpackets->integer ) { - Com_Printf( "%s:Out of order packet %i at %i\n" - , NET_AdrToString( chan->remoteAddress ) - , sequence - , chan->incomingSequence ); - } - return qfalse; - } - - // - // dropped packets don't keep the message from being used - // - chan->dropped = sequence - (chan->incomingSequence+1); - if ( chan->dropped > 0 ) { - if ( showdrop->integer || showpackets->integer ) { - Com_Printf( "%s:Dropped %i packets at %i\n" - , NET_AdrToString( chan->remoteAddress ) - , chan->dropped - , sequence ); - } - } - - - // - // if this is the final framgent of a reliable message, - // bump incoming_reliable_sequence - // - if ( fragmented ) { - // TTimo - // make sure we add the fragments in correct order - // either a packet was dropped, or we received this one too soon - // we don't reconstruct the fragments. we will wait till this fragment gets to us again - // (NOTE: we could probably try to rebuild by out of order chunks if needed) - if ( sequence != chan->fragmentSequence ) { - chan->fragmentSequence = sequence; - chan->fragmentLength = 0; - } - - // if we missed a fragment, dump the message - if ( fragmentStart != chan->fragmentLength ) { - if ( showdrop->integer || showpackets->integer ) { - Com_Printf( "%s:Dropped a message fragment\n" - , NET_AdrToString( chan->remoteAddress ) - , sequence); - } - // we can still keep the part that we have so far, - // so we don't need to clear chan->fragmentLength - return qfalse; - } - - // copy the fragment to the fragment buffer - if ( fragmentLength < 0 || msg->readcount + fragmentLength > msg->cursize || - chan->fragmentLength + fragmentLength > sizeof( chan->fragmentBuffer ) ) { - if ( showdrop->integer || showpackets->integer ) { - Com_Printf ("%s:illegal fragment length\n" - , NET_AdrToString (chan->remoteAddress ) ); - } - return qfalse; - } - - Com_Memcpy( chan->fragmentBuffer + chan->fragmentLength, - msg->data + msg->readcount, fragmentLength ); - - chan->fragmentLength += fragmentLength; - - // if this wasn't the last fragment, don't process anything - if ( fragmentLength == FRAGMENT_SIZE ) { - return qfalse; - } - - if ( chan->fragmentLength > msg->maxsize ) { - Com_Printf( "%s:fragmentLength %i > msg->maxsize\n" - , NET_AdrToString (chan->remoteAddress ), - chan->fragmentLength ); - return qfalse; - } - - // copy the full message over the partial fragment - - // make sure the sequence number is still there - *(int *)msg->data = LittleLong( sequence ); - - Com_Memcpy( msg->data + 4, chan->fragmentBuffer, chan->fragmentLength ); - msg->cursize = chan->fragmentLength + 4; - chan->fragmentLength = 0; - msg->readcount = 4; // past the sequence number - msg->bit = 32; // past the sequence number - - // TTimo - // clients were not acking fragmented messages - chan->incomingSequence = sequence; - - return qtrue; - } - - // - // the message can now be read from the current message pointer - // - chan->incomingSequence = sequence; - - return qtrue; -} - - -//============================================================================== - -/* -=================== -NET_CompareBaseAdr - -Compares without the port -=================== -*/ -qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b) -{ - if (a.type != b.type) - return qfalse; - - if (a.type == NA_LOOPBACK) - return qtrue; - - if (a.type == NA_IP) - { - if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3]) - return qtrue; - return qfalse; - } - - if (a.type == NA_IPX) - { - if ((memcmp(a.ipx, b.ipx, 10) == 0)) - return qtrue; - return qfalse; - } - - - Com_Printf ("NET_CompareBaseAdr: bad address type\n"); - return qfalse; -} - -const char *NET_AdrToString (netadr_t a) -{ - static char s[64]; - - if (a.type == NA_LOOPBACK) { - Com_sprintf (s, sizeof(s), "loopback"); - } else if (a.type == NA_BOT) { - Com_sprintf (s, sizeof(s), "bot"); - } else if (a.type == NA_IP) { - Com_sprintf (s, sizeof(s), "%i.%i.%i.%i:%hu", - a.ip[0], a.ip[1], a.ip[2], a.ip[3], BigShort(a.port)); - } else { - Com_sprintf (s, sizeof(s), "%02x%02x%02x%02x.%02x%02x%02x%02x%02x%02x:%hu", - a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9], - BigShort(a.port)); - } - - return s; -} - - -qboolean NET_CompareAdr (netadr_t a, netadr_t b) -{ - if (a.type != b.type) - return qfalse; - - if (a.type == NA_LOOPBACK) - return qtrue; - - if (a.type == NA_IP) - { - if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port) - return qtrue; - return qfalse; - } - - if (a.type == NA_IPX) - { - if ((memcmp(a.ipx, b.ipx, 10) == 0) && a.port == b.port) - return qtrue; - return qfalse; - } - - Com_Printf ("NET_CompareAdr: bad address type\n"); - return qfalse; -} - - -qboolean NET_IsLocalAddress( netadr_t adr ) { - return adr.type == NA_LOOPBACK; -} - - - -/* -============================================================================= - -LOOPBACK BUFFERS FOR LOCAL PLAYER - -============================================================================= -*/ - -// there needs to be enough loopback messages to hold a complete -// gamestate of maximum size -#define MAX_LOOPBACK 16 - -typedef struct { - byte data[MAX_PACKETLEN]; - int datalen; -} loopmsg_t; - -typedef struct { - loopmsg_t msgs[MAX_LOOPBACK]; - int get, send; -} loopback_t; - -loopback_t loopbacks[2]; - - -qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message) -{ - int i; - loopback_t *loop; - - loop = &loopbacks[sock]; - - if (loop->send - loop->get > MAX_LOOPBACK) - loop->get = loop->send - MAX_LOOPBACK; - - if (loop->get >= loop->send) - return qfalse; - - i = loop->get & (MAX_LOOPBACK-1); - loop->get++; - - Com_Memcpy (net_message->data, loop->msgs[i].data, loop->msgs[i].datalen); - net_message->cursize = loop->msgs[i].datalen; - Com_Memset (net_from, 0, sizeof(*net_from)); - net_from->type = NA_LOOPBACK; - return qtrue; - -} - - -void NET_SendLoopPacket (netsrc_t sock, int length, const void *data, netadr_t to) -{ - int i; - loopback_t *loop; - - loop = &loopbacks[sock^1]; - - i = loop->send & (MAX_LOOPBACK-1); - loop->send++; - - Com_Memcpy (loop->msgs[i].data, data, length); - loop->msgs[i].datalen = length; -} - -//============================================================================= - - -void NET_SendPacket( netsrc_t sock, int length, const void *data, netadr_t to ) { - - // sequenced packets are shown in netchan, so just show oob - if ( showpackets->integer && *(int *)data == -1 ) { - Com_Printf ("send packet %4i\n", length); - } - - if ( to.type == NA_LOOPBACK ) { - NET_SendLoopPacket (sock, length, data, to); - return; - } - if ( to.type == NA_BOT ) { - return; - } - if ( to.type == NA_BAD ) { - return; - } - - Sys_SendPacket( length, data, to ); -} - -/* -=============== -NET_OutOfBandPrint - -Sends a text message in an out-of-band datagram -================ -*/ -void QDECL NET_OutOfBandPrint( netsrc_t sock, netadr_t adr, const char *format, ... ) { - va_list argptr; - char string[MAX_MSGLEN]; - - - // set the header - string[0] = -1; - string[1] = -1; - string[2] = -1; - string[3] = -1; - - va_start( argptr, format ); - vsprintf( string+4, format, argptr ); - va_end( argptr ); - - // send the datagram - NET_SendPacket( sock, strlen( string ), string, adr ); -} - -/* -=============== -NET_OutOfBandPrint - -Sends a data message in an out-of-band datagram (only used for "connect") -================ -*/ -void QDECL NET_OutOfBandData( netsrc_t sock, netadr_t adr, byte *format, int len ) { - byte string[MAX_MSGLEN*2]; - int i; - msg_t mbuf; - - // set the header - string[0] = 0xff; - string[1] = 0xff; - string[2] = 0xff; - string[3] = 0xff; - - for(i=0;itype = NA_LOOPBACK; - return qtrue; - } - - // look for a port number - Q_strncpyz( base, s, sizeof( base ) ); - port = strstr( base, ":" ); - if ( port ) { - *port = 0; - port++; - } - - r = Sys_StringToAdr( base, a ); - - if ( !r ) { - a->type = NA_BAD; - return qfalse; - } - - // inet_addr returns this if out of range - if ( a->ip[0] == 255 && a->ip[1] == 255 && a->ip[2] == 255 && a->ip[3] == 255 ) { - a->type = NA_BAD; - return qfalse; - } - - if ( port ) { - a->port = BigShort( (short)atoi( port ) ); - } else { - a->port = BigShort( PORT_SERVER ); - } - - return qtrue; -} - diff --git a/tools/quake3/bspc/deps/qcommon/puff.h b/tools/quake3/bspc/deps/qcommon/puff.h deleted file mode 100644 index 14070f64..00000000 --- a/tools/quake3/bspc/deps/qcommon/puff.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This is a modified version of Mark Adlers work, - * see below for the original copyright. - * 2006 - Joerg Dietrich - */ - -/* puff.h - Copyright (C) 2002, 2003 Mark Adler, all rights reserved - version 1.7, 3 Mar 2002 - - This software is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Mark Adler madler@alumni.caltech.edu - */ - -#ifndef __PUFF_H -#define __PUFF_H - -#include "q_shared.h" /* for definitions of the types */ - -/* - * See puff.c for purpose and usage. - */ -int32_t puff(uint8_t *dest, /* pointer to destination pointer */ - uint32_t *destlen, /* amount of output space */ - uint8_t *source, /* pointer to source data pointer */ - uint32_t *sourcelen); /* amount of input available */ - -#endif // __PUFF_H diff --git a/tools/quake3/bspc/deps/qcommon/q_platform.h b/tools/quake3/bspc/deps/qcommon/q_platform.h deleted file mode 100644 index c3edbdd6..00000000 --- a/tools/quake3/bspc/deps/qcommon/q_platform.h +++ /dev/null @@ -1,374 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#ifndef __Q_PLATFORM_H -#define __Q_PLATFORM_H - -// this is for determining if we have an asm version of a C function -#ifdef Q3_VM - -#define id386 0 -#define idppc 0 -#define idppc_altivec 0 -#define idsparc 0 - -#else - -#if (defined _M_IX86 || defined __i386__) && !defined(C_ONLY) -#define id386 1 -#else -#define id386 0 -#endif - -#if (defined(powerc) || defined(powerpc) || defined(ppc) || \ - defined(__ppc) || defined(__ppc__)) && !defined(C_ONLY) -#define idppc 1 -#if defined(__VEC__) -#define idppc_altivec 1 -#ifdef MACOS_X // Apple's GCC does this differently than the FSF. -#define VECCONST_UINT8(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ - (vector unsigned char) (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) -#else -#define VECCONST_UINT8(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ - (vector unsigned char) {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p} -#endif -#else -#define idppc_altivec 0 -#endif -#else -#define idppc 0 -#define idppc_altivec 0 -#endif - -#if defined(__sparc__) && !defined(C_ONLY) -#define idsparc 1 -#else -#define idsparc 0 -#endif - -#endif - -#ifndef __ASM_I386__ // don't include the C bits if included from qasm.h - -// for windows fastcall option -#define QDECL - -//================================================================= WIN64/32 === - -#ifdef __WIN64__ - -#undef QDECL -#define QDECL __cdecl - -#if defined( _MSC_VER ) -#define OS_STRING "win_msvc64" -#elif defined __MINGW64__ -#define OS_STRING "win_mingw64" -#endif - -#define ID_INLINE inline -#define PATH_SEP '\\' - -#if defined( __WIN64__ ) -#define ARCH_STRING "x86_64" -#elif defined _M_ALPHA -#define ARCH_STRING "AXP" -#endif - -#define Q3_LITTLE_ENDIAN - -#define DLL_EXT ".dll" - -#elif _WIN32 - -#undef QDECL -#define QDECL __cdecl - -#if defined( _MSC_VER ) -#define OS_STRING "win_msvc" -#elif defined __MINGW32__ -#define OS_STRING "win_mingw" -#endif - -#define ID_INLINE __inline -#define PATH_SEP '\\' - -#if defined( _M_IX86 ) || defined( __i386__ ) -#define ARCH_STRING "x86" -#elif defined _M_ALPHA -#define ARCH_STRING "AXP" -#endif - -#define Q3_LITTLE_ENDIAN - -#define DLL_EXT ".dll" - -#endif - -//============================================================== MAC OS X === - -#if defined(MACOS_X) || defined(__APPLE_CC__) - -// make sure this is defined, just for sanity's sake... -#ifndef MACOS_X -#define MACOS_X -#endif - -#define OS_STRING "macosx" -#define ID_INLINE inline -#define PATH_SEP '/' - -#ifdef __ppc__ -#define ARCH_STRING "ppc" -#define Q3_BIG_ENDIAN -#elif defined __i386__ -#define ARCH_STRING "i386" -#define Q3_LITTLE_ENDIAN -#elif defined __x86_64__ -#define ARCH_STRING "x86_64" -#define Q3_LITTLE_ENDIAN -#endif - -#define DLL_EXT ".dylib" - -#endif - -//================================================================= LINUX === - -#ifdef __linux__ - -#include - -#define OS_STRING "linux" -#define ID_INLINE inline -#define PATH_SEP '/' - -#if defined __i386__ -#define ARCH_STRING "i386" -#elif defined __x86_64__ -#define ARCH_STRING "x86_64" -#elif defined __powerpc64__ -#define ARCH_STRING "ppc64" -#elif defined __powerpc__ -#define ARCH_STRING "ppc" -#elif defined __s390__ -#define ARCH_STRING "s390" -#elif defined __s390x__ -#define ARCH_STRING "s390x" -#elif defined __ia64__ -#define ARCH_STRING "ia64" -#elif defined __alpha__ -#define ARCH_STRING "alpha" -#elif defined __sparc__ -#define ARCH_STRING "sparc" -#elif defined __arm__ -#define ARCH_STRING "arm" -#elif defined __cris__ -#define ARCH_STRING "cris" -#elif defined __hppa__ -#define ARCH_STRING "hppa" -#elif defined __mips__ -#define ARCH_STRING "mips" -#elif defined __sh__ -#define ARCH_STRING "sh" -#endif - -#if __FLOAT_WORD_ORDER == __BIG_ENDIAN -#define Q3_BIG_ENDIAN -#else -#define Q3_LITTLE_ENDIAN -#endif - -#define DLL_EXT ".so" - -#endif - -//=================================================================== BSD === - -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - -#include -#include - -#ifndef __BSD__ - #define __BSD__ -#endif - -#if defined(__FreeBSD__) -#define OS_STRING "freebsd" -#elif defined(__OpenBSD__) -#define OS_STRING "openbsd" -#elif defined(__NetBSD__) -#define OS_STRING "netbsd" -#endif - -#define ID_INLINE inline -#define PATH_SEP '/' - -#ifdef __i386__ -#define ARCH_STRING "i386" -#elif defined __amd64__ -#define ARCH_STRING "amd64" -#elif defined __axp__ -#define ARCH_STRING "alpha" -#endif - -#if BYTE_ORDER == BIG_ENDIAN -#define Q3_BIG_ENDIAN -#else -#define Q3_LITTLE_ENDIAN -#endif - -#define DLL_EXT ".so" - -#endif - -//================================================================= SUNOS === - -#ifdef __sun - -#include -#include - -#define OS_STRING "solaris" -#define ID_INLINE inline -#define PATH_SEP '/' - -#ifdef __i386__ -#define ARCH_STRING "i386" -#elif defined __sparc -#define ARCH_STRING "sparc" -#endif - -#if defined( _BIG_ENDIAN ) -#define Q3_BIG_ENDIAN -#elif defined( _LITTLE_ENDIAN ) -#define Q3_LITTLE_ENDIAN -#endif - -#define DLL_EXT ".so" - -#endif - -//================================================================== IRIX === - -#ifdef __sgi - -#define OS_STRING "irix" -#define ID_INLINE __inline -#define PATH_SEP '/' - -#define ARCH_STRING "mips" - -#define Q3_BIG_ENDIAN // SGI's MIPS are always big endian - -#define DLL_EXT ".so" - -#endif - -//================================================================== Q3VM === - -#ifdef Q3_VM - -#define OS_STRING "q3vm" -#define ID_INLINE -#define PATH_SEP '/' - -#define ARCH_STRING "bytecode" - -#define DLL_EXT ".qvm" - -#endif - -//=========================================================================== - -//catch missing defines in above blocks -#if !defined( OS_STRING ) -#error "Operating system not supported" -#endif - -#if !defined( ARCH_STRING ) -#error "Architecture not supported" -#endif - -#ifndef ID_INLINE -#error "ID_INLINE not defined" -#endif - -#ifndef PATH_SEP -#error "PATH_SEP not defined" -#endif - -#ifndef DLL_EXT -#error "DLL_EXT not defined" -#endif - - -//endianness -short ShortSwap (short l); -int LongSwap (int l); -float FloatSwap (const float *f); - -#if defined( Q3_BIG_ENDIAN ) && defined( Q3_LITTLE_ENDIAN ) -#error "Endianness defined as both big and little" -#elif defined( Q3_BIG_ENDIAN ) - -#define LittleShort(x) ShortSwap(x) -#define LittleLong(x) LongSwap(x) -#define LittleFloat(x) FloatSwap(&x) -#define BigShort -#define BigLong -#define BigFloat - -#elif defined( Q3_LITTLE_ENDIAN ) - -#define LittleShort -#define LittleLong -#define LittleFloat -#define BigShort(x) ShortSwap(x) -#define BigLong(x) LongSwap(x) -#define BigFloat(x) FloatSwap(&x) - -#elif defined( Q3_VM ) - -#define LittleShort -#define LittleLong -#define LittleFloat -#define BigShort -#define BigLong -#define BigFloat - -#else -#error "Endianness not defined" -#endif - - -//platform string -#ifdef NDEBUG -#define PLATFORM_STRING OS_STRING "-" ARCH_STRING -#else -#define PLATFORM_STRING OS_STRING "-" ARCH_STRING "-debug" -#endif - -#endif - -#endif diff --git a/tools/quake3/bspc/deps/qcommon/q_shared.h b/tools/quake3/bspc/deps/qcommon/q_shared.h deleted file mode 100644 index 4374ced8..00000000 --- a/tools/quake3/bspc/deps/qcommon/q_shared.h +++ /dev/null @@ -1,1304 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// -#ifndef __Q_SHARED_H -#define __Q_SHARED_H - -// q_shared.h -- included first by ALL program modules. -// A user mod should never modify this file - -#ifdef STANDALONE - #define PRODUCT_NAME "iofoo3" - #define BASEGAME "foobar" - #define CLIENT_WINDOW_TITLE "changeme" - #define CLIENT_WINDOW_MIN_TITLE "changeme2" - #define GAMENAME_FOR_MASTER "iofoo3" // must NOT contain whitespaces -#else - #define PRODUCT_NAME "ioq3" - #define BASEGAME "baseq3" - #define CLIENT_WINDOW_TITLE "ioquake3" - #define CLIENT_WINDOW_MIN_TITLE "ioq3" - #define GAMENAME_FOR_MASTER "Quake3Arena" -#endif - -#ifdef _MSC_VER - #define PRODUCT_VERSION "1.35" -#endif - -#define Q3_VERSION PRODUCT_NAME " " PRODUCT_VERSION - -#define MAX_TEAMNAME 32 - -#ifdef _MSC_VER - -#pragma warning(disable : 4018) // signed/unsigned mismatch -#pragma warning(disable : 4032) -#pragma warning(disable : 4051) -#pragma warning(disable : 4057) // slightly different base types -#pragma warning(disable : 4100) // unreferenced formal parameter -#pragma warning(disable : 4115) -#pragma warning(disable : 4125) // decimal digit terminates octal escape sequence -#pragma warning(disable : 4127) // conditional expression is constant -#pragma warning(disable : 4136) -#pragma warning(disable : 4152) // nonstandard extension, function/data pointer conversion in expression -//#pragma warning(disable : 4201) -//#pragma warning(disable : 4214) -#pragma warning(disable : 4244) -#pragma warning(disable : 4142) // benign redefinition -//#pragma warning(disable : 4305) // truncation from const double to float -//#pragma warning(disable : 4310) // cast truncates constant value -//#pragma warning(disable: 4505) // unreferenced local function has been removed -#pragma warning(disable : 4514) -#pragma warning(disable : 4702) // unreachable code -#pragma warning(disable : 4711) // selected for automatic inline expansion -#pragma warning(disable : 4220) // varargs matches remaining parameters -//#pragma intrinsic( memset, memcpy ) -#endif - -//Ignore __attribute__ on non-gcc platforms -#ifndef __GNUC__ -#ifndef __attribute__ -#define __attribute__(x) -#endif -#endif - -#if (defined _MSC_VER) -#define Q_EXPORT __declspec(dllexport) -#elif (defined __SUNPRO_C) -#define Q_EXPORT __global -#elif ((__GNUC__ >= 3) && (!__EMX__) && (!sun)) -#define Q_EXPORT __attribute__((visibility("default"))) -#else -#define Q_EXPORT -#endif - -/********************************************************************** - VM Considerations - - The VM can not use the standard system headers because we aren't really - using the compiler they were meant for. We use bg_lib.h which contains - prototypes for the functions we define for our own use in bg_lib.c. - - When writing mods, please add needed headers HERE, do not start including - stuff like in the various .c files that make up each of the VMs - since you will be including system headers files can will have issues. - - Remember, if you use a C library function that is not defined in bg_lib.c, - you will have to add your own version for support in the VM. - - **********************************************************************/ - -#ifdef Q3_VM - -#include "../game/bg_lib.h" - -typedef int intptr_t; - -#else - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// vsnprintf is ISO/IEC 9899:1999 -// abstracting this to make it portable -#ifdef _WIN32 - #define Q_vsnprintf _vsnprintf - #define Q_snprintf _snprintf -#else - #define Q_vsnprintf vsnprintf - #define Q_snprintf snprintf -#endif - -#ifdef _MSC_VER - #include - - typedef __int64 int64_t; - typedef __int32 int32_t; - typedef __int16 int16_t; - typedef __int8 int8_t; - typedef unsigned __int64 uint64_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int8 uint8_t; -#else - #include -#endif - -#endif - - -#include "q_platform.h" - -//============================================================= - -typedef unsigned char byte; - -typedef enum {qfalse, qtrue} qboolean; - -typedef union { - float f; - int i; - unsigned int ui; -} floatint_t; - -typedef int qhandle_t; -typedef int sfxHandle_t; -typedef int fileHandle_t; -typedef int clipHandle_t; - -#define PAD(x,y) (((x)+(y)-1) & ~((y)-1)) - -#ifdef __GNUC__ -#define ALIGN(x) __attribute__((aligned(x))) -#else -#define ALIGN(x) -#endif - -#ifndef NULL -#define NULL ((void *)0) -#endif - -#define STRING(s) #s -// expand constants before stringifying them -#define XSTRING(s) STRING(s) - -#define MAX_QINT 0x7fffffff -#define MIN_QINT (-MAX_QINT-1) - -#define ARRAY_LEN(x) (sizeof(x) / sizeof(*x)) - - -// angle indexes -#define PITCH 0 // up / down -#define YAW 1 // left / right -#define ROLL 2 // fall over - -// the game guarantees that no string from the network will ever -// exceed MAX_STRING_CHARS -#define MAX_STRING_CHARS 1024 // max length of a string passed to Cmd_TokenizeString -#define MAX_STRING_TOKENS 1024 // max tokens resulting from Cmd_TokenizeString -#define MAX_TOKEN_CHARS 1024 // max length of an individual token - -#define MAX_INFO_STRING 1024 -#define MAX_INFO_KEY 1024 -#define MAX_INFO_VALUE 1024 - -#define BIG_INFO_STRING 8192 // used for system info key only -#define BIG_INFO_KEY 8192 -#define BIG_INFO_VALUE 8192 - - -#define MAX_QPATH 64 // max length of a quake game pathname -#ifdef PATH_MAX -#define MAX_OSPATH PATH_MAX -#else -#define MAX_OSPATH 256 // max length of a filesystem pathname -#endif - -#define MAX_NAME_LENGTH 32 // max length of a client name - -#define MAX_SAY_TEXT 150 - -// paramters for command buffer stuffing -typedef enum { - EXEC_NOW, // don't return until completed, a VM should NEVER use this, - // because some commands might cause the VM to be unloaded... - EXEC_INSERT, // insert at current position, but don't run yet - EXEC_APPEND // add to end of the command buffer (normal case) -} cbufExec_t; - - -// -// these aren't needed by any of the VMs. put in another header? -// -#define MAX_MAP_AREA_BYTES 32 // bit vector of area visibility - - -// print levels from renderer (FIXME: set up for game / cgame?) -typedef enum { - PRINT_ALL, - PRINT_DEVELOPER, // only print when "developer 1" - PRINT_WARNING, - PRINT_ERROR -} printParm_t; - - -#ifdef ERR_FATAL -#undef ERR_FATAL // this is be defined in malloc.h -#endif - -// parameters to the main Error routine -typedef enum { - ERR_FATAL, // exit the entire game with a popup window - ERR_DROP, // print to console and disconnect from game - ERR_SERVERDISCONNECT, // don't kill server - ERR_DISCONNECT, // client disconnected from the server - ERR_NEED_CD // pop up the need-cd dialog -} errorParm_t; - - -// font rendering values used by ui and cgame - -#define PROP_GAP_WIDTH 3 -#define PROP_SPACE_WIDTH 8 -#define PROP_HEIGHT 27 -#define PROP_SMALL_SIZE_SCALE 0.75 - -#define BLINK_DIVISOR 200 -#define PULSE_DIVISOR 75 - -#define UI_LEFT 0x00000000 // default -#define UI_CENTER 0x00000001 -#define UI_RIGHT 0x00000002 -#define UI_FORMATMASK 0x00000007 -#define UI_SMALLFONT 0x00000010 -#define UI_BIGFONT 0x00000020 // default -#define UI_GIANTFONT 0x00000040 -#define UI_DROPSHADOW 0x00000800 -#define UI_BLINK 0x00001000 -#define UI_INVERSE 0x00002000 -#define UI_PULSE 0x00004000 - -#if defined(_DEBUG) && !defined(BSPC) - #define HUNK_DEBUG -#endif - -typedef enum { - h_high, - h_low, - h_dontcare -} ha_pref; - -#ifdef HUNK_DEBUG -#define Hunk_Alloc( size, preference ) Hunk_AllocDebug(size, preference, #size, __FILE__, __LINE__) -void *Hunk_AllocDebug( int size, ha_pref preference, char *label, char *file, int line ); -#else -void *Hunk_Alloc( int size, ha_pref preference ); -#endif - -#define Com_Memset memset -#define Com_Memcpy memcpy - -#define CIN_system 1 -#define CIN_loop 2 -#define CIN_hold 4 -#define CIN_silent 8 -#define CIN_shader 16 - -/* -============================================================== - -MATHLIB - -============================================================== -*/ - - -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 int fixed4_t; -typedef int fixed8_t; -typedef int fixed16_t; - -#ifndef M_PI -#define M_PI 3.14159265358979323846f // matches value in gcc v2 math.h -#endif - -#define NUMVERTEXNORMALS 162 -extern vec3_t bytedirs[NUMVERTEXNORMALS]; - -// all drawing is done to a 640*480 virtual screen size -// and will be automatically scaled to the real resolution -#define SCREEN_WIDTH 640 -#define SCREEN_HEIGHT 480 - -#define TINYCHAR_WIDTH (SMALLCHAR_WIDTH) -#define TINYCHAR_HEIGHT (SMALLCHAR_HEIGHT/2) - -#define SMALLCHAR_WIDTH 8 -#define SMALLCHAR_HEIGHT 16 - -#define BIGCHAR_WIDTH 16 -#define BIGCHAR_HEIGHT 16 - -#define GIANTCHAR_WIDTH 32 -#define GIANTCHAR_HEIGHT 48 - -extern vec4_t colorBlack; -extern vec4_t colorRed; -extern vec4_t colorGreen; -extern vec4_t colorBlue; -extern vec4_t colorYellow; -extern vec4_t colorMagenta; -extern vec4_t colorCyan; -extern vec4_t colorWhite; -extern vec4_t colorLtGrey; -extern vec4_t colorMdGrey; -extern vec4_t colorDkGrey; - -#define Q_COLOR_ESCAPE '^' -#define Q_IsColorString(p) ((p) && *(p) == Q_COLOR_ESCAPE && *((p)+1) && isalnum(*((p)+1))) // ^[0-9a-zA-Z] - -#define COLOR_BLACK '0' -#define COLOR_RED '1' -#define COLOR_GREEN '2' -#define COLOR_YELLOW '3' -#define COLOR_BLUE '4' -#define COLOR_CYAN '5' -#define COLOR_MAGENTA '6' -#define COLOR_WHITE '7' -#define ColorIndex(c) (((c) - '0') & 0x07) - -#define S_COLOR_BLACK "^0" -#define S_COLOR_RED "^1" -#define S_COLOR_GREEN "^2" -#define S_COLOR_YELLOW "^3" -#define S_COLOR_BLUE "^4" -#define S_COLOR_CYAN "^5" -#define S_COLOR_MAGENTA "^6" -#define S_COLOR_WHITE "^7" - -extern vec4_t g_color_table[8]; - -#define MAKERGB( v, r, g, b ) v[0]=r;v[1]=g;v[2]=b -#define MAKERGBA( v, r, g, b, a ) v[0]=r;v[1]=g;v[2]=b;v[3]=a - -#define DEG2RAD( a ) ( ( (a) * M_PI ) / 180.0F ) -#define RAD2DEG( a ) ( ( (a) * 180.0f ) / M_PI ) - -struct cplane_s; - -extern vec3_t vec3_origin; -extern vec3_t axisDefault[3]; - -#define nanmask (255<<23) - -#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask) - -#if idppc - -static ID_INLINE float Q_rsqrt( float number ) { - float x = 0.5f * number; - float y; -#ifdef __GNUC__ - asm("frsqrte %0,%1" : "=f" (y) : "f" (number)); -#else - y = __frsqrte( number ); -#endif - return y * (1.5f - (x * y * y)); - } - -#ifdef __GNUC__ -static ID_INLINE float Q_fabs(float x) { - float abs_x; - - asm("fabs %0,%1" : "=f" (abs_x) : "f" (x)); - return abs_x; -} -#else -#define Q_fabs __fabsf -#endif - -#else -float Q_fabs( float f ); -float Q_rsqrt( float f ); // reciprocal square root -#endif - -#define SQRTFAST( x ) ( (x) * Q_rsqrt( x ) ) - -signed char ClampChar( int i ); -signed short ClampShort( int i ); - -// this isn't a real cheap function to call! -int DirToByte( vec3_t dir ); -void ByteToDir( int b, vec3_t dir ); - -#if 1 - -#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]) -#define VectorSubtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2]) -#define VectorAdd(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2]) -#define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2]) -#define VectorScale(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s)) -#define VectorMA(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s)) - -#else - -#define DotProduct(x,y) _DotProduct(x,y) -#define VectorSubtract(a,b,c) _VectorSubtract(a,b,c) -#define VectorAdd(a,b,c) _VectorAdd(a,b,c) -#define VectorCopy(a,b) _VectorCopy(a,b) -#define VectorScale(v, s, o) _VectorScale(v,s,o) -#define VectorMA(v, s, b, o) _VectorMA(v,s,b,o) - -#endif - -#ifdef Q3_VM -#ifdef VectorCopy -#undef VectorCopy -// this is a little hack to get more efficient copies in our interpreter -typedef struct { - float v[3]; -} vec3struct_t; -#define VectorCopy(a,b) (*(vec3struct_t *)b=*(vec3struct_t *)a) -#endif -#endif - -#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 Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) - -#define SnapVector(v) {v[0]=((int)(v[0]));v[1]=((int)(v[1]));v[2]=((int)(v[2]));} -// just in case you do't want to use the macros -vec_t _DotProduct( const vec3_t v1, const vec3_t v2 ); -void _VectorSubtract( const vec3_t veca, const vec3_t vecb, vec3_t out ); -void _VectorAdd( const vec3_t veca, const vec3_t vecb, vec3_t out ); -void _VectorCopy( const vec3_t in, vec3_t out ); -void _VectorScale( const vec3_t in, float scale, vec3_t out ); -void _VectorMA( const vec3_t veca, float scale, const vec3_t vecb, vec3_t vecc ); - -unsigned ColorBytes3 (float r, float g, float b); -unsigned ColorBytes4 (float r, float g, float b, float a); - -float NormalizeColor( const vec3_t in, vec3_t out ); - -float RadiusFromBounds( const vec3_t mins, const vec3_t maxs ); -void ClearBounds( vec3_t mins, vec3_t maxs ); -void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ); - -#if !defined( Q3_VM ) || ( defined( Q3_VM ) && defined( __Q3_VM_MATH ) ) -static ID_INLINE int VectorCompare( const vec3_t v1, const vec3_t v2 ) { - if (v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2]) { - return 0; - } - return 1; -} - -static ID_INLINE vec_t VectorLength( const vec3_t v ) { - return (vec_t)sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); -} - -static ID_INLINE vec_t VectorLengthSquared( const vec3_t v ) { - return (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); -} - -static ID_INLINE vec_t Distance( const vec3_t p1, const vec3_t p2 ) { - vec3_t v; - - VectorSubtract (p2, p1, v); - return VectorLength( v ); -} - -static ID_INLINE vec_t DistanceSquared( const vec3_t p1, const vec3_t p2 ) { - vec3_t v; - - VectorSubtract (p2, p1, v); - return v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; -} - -// fast vector normalize routine that does not check to make sure -// that length != 0, nor does it return length, uses rsqrt approximation -static ID_INLINE void VectorNormalizeFast( vec3_t v ) -{ - float ilength; - - ilength = Q_rsqrt( DotProduct( v, v ) ); - - v[0] *= ilength; - v[1] *= ilength; - v[2] *= ilength; -} - -static ID_INLINE void VectorInverse( vec3_t v ){ - v[0] = -v[0]; - v[1] = -v[1]; - v[2] = -v[2]; -} - -static ID_INLINE void CrossProduct( const vec3_t v1, const 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]; -} - -#else -int VectorCompare( const vec3_t v1, const vec3_t v2 ); - -vec_t VectorLength( const vec3_t v ); - -vec_t VectorLengthSquared( const vec3_t v ); - -vec_t Distance( const vec3_t p1, const vec3_t p2 ); - -vec_t DistanceSquared( const vec3_t p1, const vec3_t p2 ); - -void VectorNormalizeFast( vec3_t v ); - -void VectorInverse( vec3_t v ); - -void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross ); - -#endif - -vec_t VectorNormalize (vec3_t v); // returns vector length -vec_t VectorNormalize2( const vec3_t v, vec3_t out ); -void Vector4Scale( const vec4_t in, vec_t scale, vec4_t out ); -void VectorRotate( vec3_t in, vec3_t matrix[3], vec3_t out ); -int Q_log2(int val); - -float Q_acos(float c); - -int Q_rand( int *seed ); -float Q_random( int *seed ); -float Q_crandom( int *seed ); - -#define random() ((rand () & 0x7fff) / ((float)0x7fff)) -#define crandom() (2.0 * (random() - 0.5)) - -void vectoangles( const vec3_t value1, vec3_t angles); -void AnglesToAxis( const vec3_t angles, vec3_t axis[3] ); - -void AxisClear( vec3_t axis[3] ); -void AxisCopy( vec3_t in[3], vec3_t out[3] ); - -void SetPlaneSignbits( struct cplane_s *out ); -int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct cplane_s *plane); - -qboolean BoundsIntersect(const vec3_t mins, const vec3_t maxs, - const vec3_t mins2, const vec3_t maxs2); -qboolean BoundsIntersectSphere(const vec3_t mins, const vec3_t maxs, - const vec3_t origin, vec_t radius); -qboolean BoundsIntersectPoint(const vec3_t mins, const vec3_t maxs, - const vec3_t origin); - -float AngleMod(float a); -float LerpAngle (float from, float to, float frac); -float AngleSubtract( float a1, float a2 ); -void AnglesSubtract( vec3_t v1, vec3_t v2, vec3_t v3 ); - -float AngleNormalize360 ( float angle ); -float AngleNormalize180 ( float angle ); -float AngleDelta ( float angle1, float angle2 ); - -qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c ); -void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal ); -void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ); -void RotateAroundDirection( vec3_t axis[3], float yaw ); -void MakeNormalVectors( const vec3_t forward, vec3_t right, vec3_t up ); -// perpendicular vector could be replaced by this - -//int PlaneTypeForNormal (vec3_t normal); - -void MatrixMultiply(float in1[3][3], float in2[3][3], float out[3][3]); -void AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); -void PerpendicularVector( vec3_t dst, const vec3_t src ); -int Q_isnan( float x ); - - -//============================================= - -float Com_Clamp( float min, float max, float value ); - -char *COM_SkipPath( char *pathname ); -const char *COM_GetExtension( const char *name ); -void COM_StripExtension(const char *in, char *out, int destsize); -void COM_DefaultExtension( char *path, int maxSize, const char *extension ); - -void COM_BeginParseSession( const char *name ); -int COM_GetCurrentParseLine( void ); -char *COM_Parse( char **data_p ); -char *COM_ParseExt( char **data_p, qboolean allowLineBreak ); -int COM_Compress( char *data_p ); -void COM_ParseError( char *format, ... ) __attribute__ ((format (printf, 1, 2))); -void COM_ParseWarning( char *format, ... ) __attribute__ ((format (printf, 1, 2))); -//int COM_ParseInfos( char *buf, int max, char infos[][MAX_INFO_STRING] ); - -#define MAX_TOKENLENGTH 1024 - -#ifndef TT_STRING -//token types -#define TT_STRING 1 // string -#define TT_LITERAL 2 // literal -#define TT_NUMBER 3 // number -#define TT_NAME 4 // name -#define TT_PUNCTUATION 5 // punctuation -#endif - -typedef struct pc_token_s -{ - int type; - int subtype; - int intvalue; - float floatvalue; - char string[MAX_TOKENLENGTH]; -} pc_token_t; - -// data is an in/out parm, returns a parsed out token - -void COM_MatchToken( char**buf_p, char *match ); - -void SkipBracedSection (char **program); -void SkipRestOfLine ( char **data ); - -void Parse1DMatrix (char **buf_p, int x, float *m); -void Parse2DMatrix (char **buf_p, int y, int x, float *m); -void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m); -int Com_HexStrToInt( const char *str ); - -void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); - -char *Com_SkipTokens( char *s, int numTokens, char *sep ); -char *Com_SkipCharset( char *s, char *sep ); - -void Com_RandomBytes( byte *string, int len ); - -// mode parm for FS_FOpenFile -typedef enum { - FS_READ, - FS_WRITE, - FS_APPEND, - FS_APPEND_SYNC -} fsMode_t; - -typedef enum { - FS_SEEK_CUR, - FS_SEEK_END, - FS_SEEK_SET -} fsOrigin_t; - -//============================================= - -int Q_isprint( int c ); -int Q_islower( int c ); -int Q_isupper( int c ); -int Q_isalpha( int c ); -qboolean Q_isanumber( const char *s ); -qboolean Q_isintegral( float f ); - -// portable case insensitive compare -int Q_stricmp (const char *s1, const char *s2); -int Q_strncmp (const char *s1, const char *s2, int n); -int Q_stricmpn (const char *s1, const char *s2, int n); -char *Q_strlwr( char *s1 ); -char *Q_strupr( char *s1 ); -char *Q_strrchr( const char* string, int c ); -const char *Q_stristr( const char *s, const char *find); - -// buffer size safe library replacements -void Q_strncpyz( char *dest, const char *src, int destsize ); -void Q_strcat( char *dest, int size, const char *src ); - -// strlen that discounts Quake color sequences -int Q_PrintStrlen( const char *string ); -// removes color sequences from string -char *Q_CleanStr( char *string ); -// Count the number of char tocount encountered in string -int Q_CountChar(const char *string, char tocount); - -//============================================= - -// 64-bit integers for global rankings interface -// implemented as a struct for qvm compatibility -typedef struct -{ - byte b0; - byte b1; - byte b2; - byte b3; - byte b4; - byte b5; - byte b6; - byte b7; -} qint64; - -//============================================= -/* -short BigShort(short l); -short LittleShort(short l); -int BigLong (int l); -int LittleLong (int l); -qint64 BigLong64 (qint64 l); -qint64 LittleLong64 (qint64 l); -float BigFloat (const float *l); -float LittleFloat (const float *l); - -void Swap_Init (void); -*/ -char * QDECL va(char *format, ...) __attribute__ ((format (printf, 1, 2))); - -#define TRUNCATE_LENGTH 64 -void Com_TruncateLongString( char *buffer, const char *s ); - -//============================================= - -// -// key / value info strings -// -char *Info_ValueForKey( const char *s, const char *key ); -void Info_RemoveKey( char *s, const char *key ); -void Info_RemoveKey_big( char *s, const char *key ); -void Info_SetValueForKey( char *s, const char *key, const char *value ); -void Info_SetValueForKey_Big( char *s, const char *key, const char *value ); -qboolean Info_Validate( const char *s ); -void Info_NextPair( const char **s, char *key, char *value ); - -// this is only here so the functions in q_shared.c and bg_*.c can link -void QDECL Com_Error( int level, const char *error, ... ) __attribute__ ((format (printf, 2, 3))); -void QDECL Com_Printf( const char *msg, ... ) __attribute__ ((format (printf, 1, 2))); - - -/* -========================================================== - -CVARS (console variables) - -Many variables can be used for cheating purposes, so when -cheats is zero, force all unspecified variables to their -default values. -========================================================== -*/ - -#define CVAR_ARCHIVE 0x0001 // set to cause it to be saved to vars.rc - // used for system variables, not for player - // specific configurations -#define CVAR_USERINFO 0x0002 // sent to server on connect or change -#define CVAR_SERVERINFO 0x0004 // sent in response to front end requests -#define CVAR_SYSTEMINFO 0x0008 // these cvars will be duplicated on all clients -#define CVAR_INIT 0x0010 // don't allow change from console at all, - // but can be set from the command line -#define CVAR_LATCH 0x0020 // will only change when C code next does - // a Cvar_Get(), so it can't be changed - // without proper initialization. modified - // will be set, even though the value hasn't - // changed yet -#define CVAR_ROM 0x0040 // display only, cannot be set by user at all -#define CVAR_USER_CREATED 0x0080 // created by a set command -#define CVAR_TEMP 0x0100 // can be set even when cheats are disabled, but is not archived -#define CVAR_CHEAT 0x0200 // can not be changed if cheats are disabled -#define CVAR_NORESTART 0x0400 // do not clear when a cvar_restart is issued - -#define CVAR_SERVER_CREATED 0x0800 // cvar was created by a server the client connected to. -#define CVAR_VM_CREATED 0x1000 // cvar was created exclusively in one of the VMs. -#define CVAR_NONEXISTENT 0xFFFFFFFF // Cvar doesn't exist. - -// nothing outside the Cvar_*() functions should modify these fields! -typedef struct cvar_s cvar_t; - -struct cvar_s { - char *name; - char *string; - char *resetString; // cvar_restart will reset to this value - char *latchedString; // for CVAR_LATCH vars - int flags; - qboolean modified; // set each time the cvar is changed - int modificationCount; // incremented each time the cvar is changed - float value; // atof( string ) - int integer; // atoi( string ) - qboolean validate; - qboolean integral; - float min; - float max; - - cvar_t *next; - cvar_t *prev; - cvar_t *hashNext; - cvar_t *hashPrev; - int hashIndex; -}; - -#define MAX_CVAR_VALUE_STRING 256 - -typedef int cvarHandle_t; - -// the modules that run in the virtual machine can't access the cvar_t directly, -// so they must ask for structured updates -typedef struct { - cvarHandle_t handle; - int modificationCount; - float value; - int integer; - char string[MAX_CVAR_VALUE_STRING]; -} vmCvar_t; - -/* -============================================================== - -COLLISION DETECTION - -============================================================== -*/ - -#include "surfaceflags.h" // shared with the q3map utility - -// plane types are used to speed some tests -// 0-2 are axial planes -#define PLANE_X 0 -#define PLANE_Y 1 -#define PLANE_Z 2 -#define PLANE_NON_AXIAL 3 - - -/* -================= -PlaneTypeForNormal -================= -*/ - -#define PlaneTypeForNormal(x) (x[0] == 1.0 ? PLANE_X : (x[1] == 1.0 ? PLANE_Y : (x[2] == 1.0 ? PLANE_Z : PLANE_NON_AXIAL) ) ) - -// plane_t structure -// !!! if this is changed, it must be changed in asm code too !!! -typedef struct cplane_s { - vec3_t normal; - float dist; - byte type; // for fast side tests: 0,1,2 = axial, 3 = nonaxial - byte signbits; // signx + (signy<<1) + (signz<<2), used as lookup during collision - byte pad[2]; -} cplane_t; - - -// a trace is returned when a box is swept through the world -typedef struct { - qboolean allsolid; // if true, plane is not valid - qboolean startsolid; // if true, the initial point was in a solid area - float fraction; // time completed, 1.0 = didn't hit anything - vec3_t endpos; // final position - cplane_t plane; // surface normal at impact, transformed to world space - int surfaceFlags; // surface hit - int contents; // contents on other side of surface hit - int entityNum; // entity the contacted sirface is a part of -} trace_t; - -// trace->entityNum can also be 0 to (MAX_GENTITIES-1) -// or ENTITYNUM_NONE, ENTITYNUM_WORLD - - -// markfragments are returned by CM_MarkFragments() -typedef struct { - int firstPoint; - int numPoints; -} markFragment_t; - - - -typedef struct { - vec3_t origin; - vec3_t axis[3]; -} orientation_t; - -//===================================================================== - - -// in order from highest priority to lowest -// if none of the catchers are active, bound key strings will be executed -#define KEYCATCH_CONSOLE 0x0001 -#define KEYCATCH_UI 0x0002 -#define KEYCATCH_MESSAGE 0x0004 -#define KEYCATCH_CGAME 0x0008 - - -// sound channels -// channel 0 never willingly overrides -// other channels will allways override a playing sound on that channel -typedef enum { - CHAN_AUTO, - CHAN_LOCAL, // menu sounds, etc - CHAN_WEAPON, - CHAN_VOICE, - CHAN_ITEM, - CHAN_BODY, - CHAN_LOCAL_SOUND, // chat messages, etc - CHAN_ANNOUNCER // announcer voices, etc -} soundChannel_t; - - -/* -======================================================================== - - ELEMENTS COMMUNICATED ACROSS THE NET - -======================================================================== -*/ - -#define ANGLE2SHORT(x) ((int)((x)*65536/360) & 65535) -#define SHORT2ANGLE(x) ((x)*(360.0/65536)) - -#define SNAPFLAG_RATE_DELAYED 1 -#define SNAPFLAG_NOT_ACTIVE 2 // snapshot used during connection and for zombies -#define SNAPFLAG_SERVERCOUNT 4 // toggled every map_restart so transitions can be detected - -// -// per-level limits -// -#define MAX_CLIENTS 64 // absolute limit -#define MAX_LOCATIONS 64 - -#define GENTITYNUM_BITS 10 // don't need to send any more -#define MAX_GENTITIES (1<serverTime of last executed command - int pm_type; - int bobCycle; // for view bobbing and footstep generation - int pm_flags; // ducked, jump_held, etc - int pm_time; - - vec3_t origin; - vec3_t velocity; - int weaponTime; - int gravity; - int speed; - int delta_angles[3]; // add to command angles to get view direction - // changed by spawns, rotating objects, and teleporters - - int groundEntityNum;// ENTITYNUM_NONE = in air - - int legsTimer; // don't change low priority animations until this runs out - int legsAnim; // mask off ANIM_TOGGLEBIT - - int torsoTimer; // don't change low priority animations until this runs out - int torsoAnim; // mask off ANIM_TOGGLEBIT - - int movementDir; // a number 0 to 7 that represents the reletive angle - // of movement to the view angle (axial and diagonals) - // when at rest, the value will remain unchanged - // used to twist the legs during strafing - - vec3_t grapplePoint; // location of grapple to pull towards if PMF_GRAPPLE_PULL - - int eFlags; // copied to entityState_t->eFlags - - int eventSequence; // pmove generated events - int events[MAX_PS_EVENTS]; - int eventParms[MAX_PS_EVENTS]; - - int externalEvent; // events set on player from another source - int externalEventParm; - int externalEventTime; - - int clientNum; // ranges from 0 to MAX_CLIENTS-1 - int weapon; // copied to entityState_t->weapon - int weaponstate; - - vec3_t viewangles; // for fixed views - int viewheight; - - // damage feedback - int damageEvent; // when it changes, latch the other parms - int damageYaw; - int damagePitch; - int damageCount; - - int stats[MAX_STATS]; - int persistant[MAX_PERSISTANT]; // stats that aren't cleared on death - int powerups[MAX_POWERUPS]; // level.time that the powerup runs out - int ammo[MAX_WEAPONS]; - - int generic1; - int loopSound; - int jumppad_ent; // jumppad entity hit this frame - - // not communicated over the net at all - int ping; // server to game info for scoreboard - int pmove_framecount; // FIXME: don't transmit over the network - int jumppad_frame; - int entityEventSequence; -} playerState_t; - - -//==================================================================== - - -// -// usercmd_t->button bits, many of which are generated by the client system, -// so they aren't game/cgame only definitions -// -#define BUTTON_ATTACK 1 -#define BUTTON_TALK 2 // displays talk balloon and disables actions -#define BUTTON_USE_HOLDABLE 4 -#define BUTTON_GESTURE 8 -#define BUTTON_WALKING 16 // walking can't just be infered from MOVE_RUN - // because a key pressed late in the frame will - // only generate a small move value for that frame - // walking will use different animations and - // won't generate footsteps -#define BUTTON_AFFIRMATIVE 32 -#define BUTTON_NEGATIVE 64 - -#define BUTTON_GETFLAG 128 -#define BUTTON_GUARDBASE 256 -#define BUTTON_PATROL 512 -#define BUTTON_FOLLOWME 1024 - -#define BUTTON_ANY 2048 // any key whatsoever - -#define MOVE_RUN 120 // if forwardmove or rightmove are >= MOVE_RUN, - // then BUTTON_WALKING should be set - -// usercmd_t is sent to the server each client frame -typedef struct usercmd_s { - int serverTime; - int angles[3]; - int buttons; - byte weapon; // weapon - signed char forwardmove, rightmove, upmove; -} usercmd_t; - -//=================================================================== - -// if entityState->solid == SOLID_BMODEL, modelindex is an inline model number -#define SOLID_BMODEL 0xffffff - -typedef enum { - TR_STATIONARY, - TR_INTERPOLATE, // non-parametric, but interpolate between snapshots - TR_LINEAR, - TR_LINEAR_STOP, - TR_SINE, // value = base + sin( time / duration ) * delta - TR_GRAVITY -} trType_t; - -typedef struct { - trType_t trType; - int trTime; - int trDuration; // if non 0, trTime + trDuration = stop time - vec3_t trBase; - vec3_t trDelta; // velocity, etc -} trajectory_t; - -// entityState_t is the information conveyed from the server -// in an update message about entities that the client will -// need to render in some way -// Different eTypes may use the information in different ways -// The messages are delta compressed, so it doesn't really matter if -// the structure size is fairly large - -typedef struct entityState_s { - int number; // entity index - int eType; // entityType_t - int eFlags; - - trajectory_t pos; // for calculating position - trajectory_t apos; // for calculating angles - - int time; - int time2; - - vec3_t origin; - vec3_t origin2; - - vec3_t angles; - vec3_t angles2; - - int otherEntityNum; // shotgun sources, etc - int otherEntityNum2; - - int groundEntityNum; // -1 = in air - - int constantLight; // r + (g<<8) + (b<<16) + (intensity<<24) - int loopSound; // constantly loop this sound - - int modelindex; - int modelindex2; - int clientNum; // 0 to (MAX_CLIENTS - 1), for players and corpses - int frame; - - int solid; // for client side prediction, trap_linkentity sets this properly - - int event; // impulse events -- muzzle flashes, footsteps, etc - int eventParm; - - // for players - int powerups; // bit flags - int weapon; // determines weapon and flash model, etc - int legsAnim; // mask off ANIM_TOGGLEBIT - int torsoAnim; // mask off ANIM_TOGGLEBIT - - int generic1; -} entityState_t; - -typedef enum { - CA_UNINITIALIZED, - CA_DISCONNECTED, // not talking to a server - CA_AUTHORIZING, // not used any more, was checking cd key - CA_CONNECTING, // sending request packets to the server - CA_CHALLENGING, // sending challenge packets to the server - CA_CONNECTED, // netchan_t established, getting gamestate - CA_LOADING, // only during cgame initialization, never during main loop - CA_PRIMED, // got gamestate, waiting for first frame - CA_ACTIVE, // game views should be displayed - CA_CINEMATIC // playing a cinematic or a static pic, not connected to a server -} connstate_t; - -// font support - -#define GLYPH_START 0 -#define GLYPH_END 255 -#define GLYPH_CHARSTART 32 -#define GLYPH_CHAREND 127 -#define GLYPHS_PER_FONT GLYPH_END - GLYPH_START + 1 -typedef struct { - int height; // number of scan lines - int top; // top of glyph in buffer - int bottom; // bottom of glyph in buffer - int pitch; // width for copying - int xSkip; // x adjustment - int imageWidth; // width of actual image - int imageHeight; // height of actual image - float s; // x offset in image where glyph starts - float t; // y offset in image where glyph starts - float s2; - float t2; - qhandle_t glyph; // handle to the shader with the glyph - char shaderName[32]; -} glyphInfo_t; - -typedef struct { - glyphInfo_t glyphs [GLYPHS_PER_FONT]; - float glyphScale; - char name[MAX_QPATH]; -} fontInfo_t; - -#define Square(x) ((x)*(x)) - -// real time -//============================================= - - -typedef struct qtime_s { - int tm_sec; /* seconds after the minute - [0,59] */ - int tm_min; /* minutes after the hour - [0,59] */ - int tm_hour; /* hours since midnight - [0,23] */ - int tm_mday; /* day of the month - [1,31] */ - int tm_mon; /* months since January - [0,11] */ - int tm_year; /* years since 1900 */ - int tm_wday; /* days since Sunday - [0,6] */ - int tm_yday; /* days since January 1 - [0,365] */ - int tm_isdst; /* daylight savings time flag */ -} qtime_t; - - -// server browser sources -// TTimo: AS_MPLAYER is no longer used -#define AS_LOCAL 0 -#define AS_MPLAYER 1 -#define AS_GLOBAL 2 -#define AS_FAVORITES 3 - - -// cinematic states -typedef enum { - FMV_IDLE, - FMV_PLAY, // play - FMV_EOF, // all other conditions, i.e. stop/EOF/abort - FMV_ID_BLT, - FMV_ID_IDLE, - FMV_LOOPED, - FMV_ID_WAIT -} e_status; - -typedef enum _flag_status { - FLAG_ATBASE = 0, - FLAG_TAKEN, // CTF - FLAG_TAKEN_RED, // One Flag CTF - FLAG_TAKEN_BLUE, // One Flag CTF - FLAG_DROPPED -} flagStatus_t; - - - -#define MAX_GLOBAL_SERVERS 4096 -#define MAX_OTHER_SERVERS 128 -#define MAX_PINGREQUESTS 32 -#define MAX_SERVERSTATUSREQUESTS 16 - -#define SAY_ALL 0 -#define SAY_TEAM 1 -#define SAY_TELL 2 - -#define CDKEY_LEN 16 -#define CDCHKSUM_LEN 2 - - -#endif // __Q_SHARED_H diff --git a/tools/quake3/bspc/deps/qcommon/qcommon.h b/tools/quake3/bspc/deps/qcommon/qcommon.h deleted file mode 100644 index 80c3de89..00000000 --- a/tools/quake3/bspc/deps/qcommon/qcommon.h +++ /dev/null @@ -1,1190 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// qcommon.h -- definitions common between client and server, but not game.or ref modules -#ifndef _QCOMMON_H_ -#define _QCOMMON_H_ - -#include "q_shared.h" -#include "cm_public.h" - -//Ignore __attribute__ on non-gcc platforms -#ifndef __GNUC__ -#ifndef __attribute__ -#define __attribute__(x) -#endif -#endif - -//#define PRE_RELEASE_DEMO - -//============================================================================ - -// -// msg.c -// -typedef struct { - qboolean allowoverflow; // if false, do a Com_Error - qboolean overflowed; // set to true if the buffer size failed (with allowoverflow set) - qboolean oob; // set to true if the buffer size failed (with allowoverflow set) - byte *data; - int maxsize; - int cursize; - int readcount; - int bit; // for bitwise reads and writes -} msg_t; - -void MSG_Init (msg_t *buf, byte *data, int length); -void MSG_InitOOB( msg_t *buf, byte *data, int length ); -void MSG_Clear (msg_t *buf); -void MSG_WriteData (msg_t *buf, const void *data, int length); -void MSG_Bitstream( msg_t *buf ); - -// TTimo -// copy a msg_t in case we need to store it as is for a bit -// (as I needed this to keep an msg_t from a static var for later use) -// sets data buffer as MSG_Init does prior to do the copy -void MSG_Copy(msg_t *buf, byte *data, int length, msg_t *src); - -struct usercmd_s; -struct entityState_s; -struct playerState_s; - -void MSG_WriteBits( msg_t *msg, int value, int bits ); - -void MSG_WriteChar (msg_t *sb, int c); -void MSG_WriteByte (msg_t *sb, int c); -void MSG_WriteShort (msg_t *sb, int c); -void MSG_WriteLong (msg_t *sb, int c); -void MSG_WriteFloat (msg_t *sb, float f); -void MSG_WriteString (msg_t *sb, const char *s); -void MSG_WriteBigString (msg_t *sb, const char *s); -void MSG_WriteAngle16 (msg_t *sb, float f); -int MSG_HashKey(const char *string, int maxlen); - -void MSG_BeginReading (msg_t *sb); -void MSG_BeginReadingOOB(msg_t *sb); - -int MSG_ReadBits( msg_t *msg, int bits ); - -int MSG_ReadChar (msg_t *sb); -int MSG_ReadByte (msg_t *sb); -int MSG_ReadShort (msg_t *sb); -int MSG_ReadLong (msg_t *sb); -float MSG_ReadFloat (msg_t *sb); -char *MSG_ReadString (msg_t *sb); -char *MSG_ReadBigString (msg_t *sb); -char *MSG_ReadStringLine (msg_t *sb); -float MSG_ReadAngle16 (msg_t *sb); -void MSG_ReadData (msg_t *sb, void *buffer, int size); -int MSG_LookaheadByte (msg_t *msg); - -void MSG_WriteDeltaUsercmd( msg_t *msg, struct usercmd_s *from, struct usercmd_s *to ); -void MSG_ReadDeltaUsercmd( msg_t *msg, struct usercmd_s *from, struct usercmd_s *to ); - -void MSG_WriteDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ); -void MSG_ReadDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ); - -void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entityState_s *to - , qboolean force ); -void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to, - int number ); - -void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to ); -void MSG_ReadDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to ); - - -void MSG_ReportChangeVectors_f( void ); - -//============================================================================ - -/* -============================================================== - -NET - -============================================================== -*/ - -#define NET_ENABLEV4 0x01 -#define NET_ENABLEV6 0x02 -// if this flag is set, always attempt ipv6 connections instead of ipv4 if a v6 address is found. -#define NET_PRIOV6 0x04 -// disables ipv6 multicast support if set. -#define NET_DISABLEMCAST 0x08 - - -#define PACKET_BACKUP 32 // number of old messages that must be kept on client and - // server for delta comrpession and ping estimation -#define PACKET_MASK (PACKET_BACKUP-1) - -#define MAX_PACKET_USERCMDS 32 // max number of usercmd_t in a packet - -#define PORT_ANY -1 - -#define MAX_RELIABLE_COMMANDS 64 // max string commands buffered for restransmit - -typedef enum { - NA_BAD = 0, // an address lookup failed - NA_BOT, - NA_LOOPBACK, - NA_BROADCAST, - NA_IP, - NA_IP6, - NA_MULTICAST6, - NA_UNSPEC -} netadrtype_t; - -typedef enum { - NS_CLIENT, - NS_SERVER -} netsrc_t; - -#define NET_ADDRSTRMAXLEN 48 // maximum length of an IPv6 address string including trailing '\0' -typedef struct { - netadrtype_t type; - - byte ip[4]; - byte ip6[16]; - - unsigned short port; - unsigned long scope_id; // Needed for IPv6 link-local addresses -} netadr_t; - -void NET_Init( void ); -void NET_Shutdown( void ); -void NET_Restart_f( void ); -void NET_Config( qboolean enableNetworking ); -void NET_FlushPacketQueue(void); -void NET_SendPacket (netsrc_t sock, int length, const void *data, netadr_t to); -void QDECL NET_OutOfBandPrint( netsrc_t net_socket, netadr_t adr, const char *format, ...) __attribute__ ((format (printf, 3, 4))); -void QDECL NET_OutOfBandData( netsrc_t sock, netadr_t adr, byte *format, int len ); - -qboolean NET_CompareAdr (netadr_t a, netadr_t b); -qboolean NET_CompareBaseAdrMask(netadr_t a, netadr_t b, int netmask); -qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b); -qboolean NET_IsLocalAddress (netadr_t adr); -const char *NET_AdrToString (netadr_t a); -const char *NET_AdrToStringwPort (netadr_t a); -int NET_StringToAdr ( const char *s, netadr_t *a, netadrtype_t family); -qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message); -void NET_JoinMulticast6(void); -void NET_LeaveMulticast6(void); -void NET_Sleep(int msec); - - -#define MAX_MSGLEN 16384 // max length of a message, which may - // be fragmented into multiple packets - -#define MAX_DOWNLOAD_WINDOW 8 // max of eight download frames -#define MAX_DOWNLOAD_BLKSIZE 2048 // 2048 byte block chunks - - -/* -Netchan handles packet fragmentation and out of order / duplicate suppression -*/ - -typedef struct { - netsrc_t sock; - - int dropped; // between last packet and previous - - netadr_t remoteAddress; - int qport; // qport value to write when transmitting - - // sequencing variables - int incomingSequence; - int outgoingSequence; - - // incoming fragment assembly buffer - int fragmentSequence; - int fragmentLength; - byte fragmentBuffer[MAX_MSGLEN]; - - // outgoing fragment buffer - // we need to space out the sending of large fragmented messages - qboolean unsentFragments; - int unsentFragmentStart; - int unsentLength; - byte unsentBuffer[MAX_MSGLEN]; -} netchan_t; - -void Netchan_Init( int qport ); -void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport ); - -void Netchan_Transmit( netchan_t *chan, int length, const byte *data ); -void Netchan_TransmitNextFragment( netchan_t *chan ); - -qboolean Netchan_Process( netchan_t *chan, msg_t *msg ); - - -/* -============================================================== - -PROTOCOL - -============================================================== -*/ - -#define PROTOCOL_VERSION 68 -// 1.31 - 67 - -// maintain a list of compatible protocols for demo playing -// NOTE: that stuff only works with two digits protocols -extern int demo_protocols[]; - -#define UPDATE_SERVER_NAME "update.quake3arena.com" -// override on command line, config files etc. -#ifndef MASTER_SERVER_NAME -#define MASTER_SERVER_NAME "master.quake3arena.com" -#endif - -#ifndef STANDALONE - #ifndef AUTHORIZE_SERVER_NAME - #define AUTHORIZE_SERVER_NAME "authorize.quake3arena.com" - #endif - #ifndef PORT_AUTHORIZE - #define PORT_AUTHORIZE 27952 - #endif -#endif - -#define PORT_MASTER 27950 -#define PORT_UPDATE 27951 -#define PORT_SERVER 27960 -#define NUM_SERVER_PORTS 4 // broadcast scan this many ports after - // PORT_SERVER so a single machine can - // run multiple servers - - -// the svc_strings[] array in cl_parse.c should mirror this -// -// server to client -// -enum svc_ops_e { - svc_bad, - svc_nop, - svc_gamestate, - svc_configstring, // [short] [string] only in gamestate messages - svc_baseline, // only in gamestate messages - svc_serverCommand, // [string] to be executed by client game module - svc_download, // [short] size [size bytes] - svc_snapshot, - svc_EOF, - - // svc_extension follows a svc_EOF, followed by another svc_* ... - // this keeps legacy clients compatible. - svc_extension, - svc_voip, // not wrapped in USE_VOIP, so this value is reserved. -}; - - -// -// client to server -// -enum clc_ops_e { - clc_bad, - clc_nop, - clc_move, // [[usercmd_t] - clc_moveNoDelta, // [[usercmd_t] - clc_clientCommand, // [string] message - clc_EOF, - - // clc_extension follows a clc_EOF, followed by another clc_* ... - // this keeps legacy servers compatible. - clc_extension, - clc_voip, // not wrapped in USE_VOIP, so this value is reserved. -}; - -/* -============================================================== - -VIRTUAL MACHINE - -============================================================== -*/ - -typedef struct vm_s vm_t; - -typedef enum { - VMI_NATIVE, - VMI_BYTECODE, - VMI_COMPILED -} vmInterpret_t; - -typedef enum { - TRAP_MEMSET = 100, - TRAP_MEMCPY, - TRAP_STRNCPY, - TRAP_SIN, - TRAP_COS, - TRAP_ATAN2, - TRAP_SQRT, - TRAP_MATRIXMULTIPLY, - TRAP_ANGLEVECTORS, - TRAP_PERPENDICULARVECTOR, - TRAP_FLOOR, - TRAP_CEIL, - - TRAP_TESTPRINTINT, - TRAP_TESTPRINTFLOAT -} sharedTraps_t; - -void VM_Init( void ); -vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), - vmInterpret_t interpret ); -// module should be bare: "cgame", not "cgame.dll" or "vm/cgame.qvm" - -void VM_Free( vm_t *vm ); -void VM_Clear(void); -void VM_Forced_Unload_Start(void); -void VM_Forced_Unload_Done(void); -vm_t *VM_Restart( vm_t *vm ); - -intptr_t QDECL VM_Call( vm_t *vm, int callNum, ... ); - -void VM_Debug( int level ); - -void *VM_ArgPtr( intptr_t intValue ); -void *VM_ExplicitArgPtr( vm_t *vm, intptr_t intValue ); - -#define VMA(x) VM_ArgPtr(args[x]) -static ID_INLINE float _vmf(intptr_t x) -{ - floatint_t fi; - fi.i = (int) x; - return fi.f; -} -#define VMF(x) _vmf(args[x]) - - -/* -============================================================== - -CMD - -Command text buffering 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 entire text -files can be execed. - -*/ - -void Cbuf_Init (void); -// allocates an initial text buffer that will grow as needed - -void Cbuf_AddText( const char *text ); -// Adds command text at the end of the buffer, does NOT add a final \n - -void Cbuf_ExecuteText( int exec_when, const char *text ); -// this can be used in place of either Cbuf_AddText or Cbuf_InsertText - -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, or current args will be destroyed. - -//=========================================================================== - -/* - -Command execution takes a null terminated string, breaks it into tokens, -then searches for a command or variable that matches the first token. - -*/ - -typedef void (*xcommand_t) (void); - -void Cmd_Init (void); - -void Cmd_AddCommand( const char *cmd_name, xcommand_t function ); -// called by the init functions of other parts of the program to -// register commands and functions to call for them. -// The cmd_name is referenced later, so it should not be in temp memory -// if function is NULL, the command will be forwarded to the server -// as a clc_clientCommand instead of executed locally - -void Cmd_RemoveCommand( const char *cmd_name ); - -typedef void (*completionFunc_t)( char *args, int argNum ); - -void Cmd_CommandCompletion( void(*callback)(const char *s) ); -// callback with each valid string -void Cmd_SetCommandCompletionFunc( const char *command, - completionFunc_t complete ); -void Cmd_CompleteArgument( const char *command, char *args, int argNum ); -void Cmd_CompleteCfgName( char *args, int argNum ); - -int Cmd_Argc (void); -char *Cmd_Argv (int arg); -void Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ); -char *Cmd_Args (void); -char *Cmd_ArgsFrom( int arg ); -void Cmd_ArgsBuffer( char *buffer, int bufferLength ); -char *Cmd_Cmd (void); -void Cmd_Args_Sanitize( 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. - -void Cmd_TokenizeString( const char *text ); -void Cmd_TokenizeStringIgnoreQuotes( const char *text_in ); -// Takes a null terminated string. Does not need to be /n terminated. -// breaks the string up into arg tokens. - -void Cmd_ExecuteString( const char *text ); -// Parses a single line of text into arguments and tries to execute it -// as if it was typed at the console - - -/* -============================================================== - -CVAR - -============================================================== -*/ - -/* - -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. - -The user can access cvars from the console in three ways: -r_draworder prints the current value -r_draworder 0 sets the current value to 0 -set r_draworder 0 as above, but creates the cvar if not present - -Cvars are restricted from having the same names as commands to keep this -interface from being ambiguous. - -The are also occasionally used to communicated information between different -modules of the program. - -*/ - -cvar_t *Cvar_Get( const char *var_name, const char *value, int flags ); -// creates the variable if it doesn't exist, or returns the existing one -// if it exists, the value will not be changed, but flags will be ORed in -// that allows variables to be unarchived without needing bitflags -// if value is "", the value will not override a previously set value. - -void Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ); -// basically a slightly modified Cvar_Get for the interpreted modules - -void Cvar_Update( vmCvar_t *vmCvar ); -// updates an interpreted modules' version of a cvar - -void Cvar_Set( const char *var_name, const char *value ); -// will create the variable with no flags if it doesn't exist - -void Cvar_SetLatched( const char *var_name, const char *value); -// don't set the cvar immediately - -void Cvar_SetValue( const char *var_name, float value ); -// expands value to a string and calls Cvar_Set - -float Cvar_VariableValue( const char *var_name ); -int Cvar_VariableIntegerValue( const char *var_name ); -// returns 0 if not defined or non numeric - -char *Cvar_VariableString( const char *var_name ); -void Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); -// returns an empty string if not defined - -int Cvar_Flags(const char *var_name); -// returns CVAR_NONEXISTENT if cvar doesn't exist or the flags of that particular CVAR. - -void Cvar_CommandCompletion( void(*callback)(const char *s) ); -// callback with each valid string - -void Cvar_Reset( const char *var_name ); -void Cvar_ForceReset(const char *var_name); - -void Cvar_SetCheatState( void ); -// reset all testing vars to a safe value - -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( fileHandle_t f ); -// writes lines containing "set variable value" for all variables -// with the archive flag set to true. - -void Cvar_Init( void ); - -char *Cvar_InfoString( int bit ); -char *Cvar_InfoString_Big( int bit ); -// returns an info string containing all the cvars that have the given bit set -// in their flags ( CVAR_USERINFO, CVAR_SERVERINFO, CVAR_SYSTEMINFO, etc ) -void Cvar_InfoStringBuffer( int bit, char *buff, int buffsize ); -void Cvar_CheckRange( cvar_t *cv, float minVal, float maxVal, qboolean shouldBeIntegral ); - -void Cvar_Restart(qboolean unsetVM); -void Cvar_Restart_f( void ); - -void Cvar_CompleteCvarName( char *args, int argNum ); - -extern int cvar_modifiedFlags; -// whenever a cvar is modifed, its flags will be OR'd into this, so -// a single check can determine if any CVAR_USERINFO, CVAR_SERVERINFO, -// etc, variables have been modified since the last check. The bit -// can then be cleared to allow another change detection. - -/* -============================================================== - -FILESYSTEM - -No stdio calls should be used by any part of the game, because -we need to deal with all sorts of directory and seperator char -issues. -============================================================== -*/ - -// referenced flags -// these are in loop specific order so don't change the order -#define FS_GENERAL_REF 0x01 -#define FS_UI_REF 0x02 -#define FS_CGAME_REF 0x04 -#define FS_QAGAME_REF 0x08 -// number of id paks that will never be autodownloaded from baseq3 -#define NUM_ID_PAKS 9 - -#define MAX_FILE_HANDLES 64 - -#ifdef DEDICATED -# define Q3CONFIG_CFG "q3config_server.cfg" -#else -# define Q3CONFIG_CFG "q3config.cfg" -#endif - -qboolean FS_Initialized( void ); - -void FS_InitFilesystem ( void ); -void FS_Shutdown( qboolean closemfp ); - -qboolean FS_ConditionalRestart( int checksumFeed ); -void FS_Restart( int checksumFeed ); -// shutdown and restart the filesystem so changes to fs_gamedir can take effect - -void FS_AddGameDirectory( const char *path, const char *dir ); - -char **FS_ListFiles( const char *directory, const char *extension, int *numfiles ); -// directory should not have either a leading or trailing / -// if extension is "/", only subdirectories will be returned -// the returned files will not include any directories or / - -void FS_FreeFileList( char **list ); - -qboolean FS_FileExists( const char *file ); - -qboolean FS_CreatePath (char *OSPath); -char *FS_BuildOSPath( const char *base, const char *game, const char *qpath ); -qboolean FS_CompareZipChecksum(const char *zipfile); - -int FS_LoadStack( void ); - -int FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ); -int FS_GetModList( char *listbuf, int bufsize ); - -fileHandle_t FS_FOpenFileWrite( const char *qpath ); -fileHandle_t FS_FOpenFileAppend( const char *filename ); -// will properly create any needed paths and deal with seperater character issues - -fileHandle_t FS_SV_FOpenFileWrite( const char *filename ); -int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp ); -void FS_SV_Rename( const char *from, const char *to ); -int FS_FOpenFileRead( const char *qpath, fileHandle_t *file, qboolean uniqueFILE ); -// if uniqueFILE is true, then a new FILE will be fopened even if the file -// is found in an already open pak file. If uniqueFILE is false, you must call -// FS_FCloseFile instead of fclose, otherwise the pak FILE would be improperly closed -// It is generally safe to always set uniqueFILE to true, because the majority of -// file IO goes through FS_ReadFile, which Does The Right Thing already. - -int FS_FileIsInPAK(const char *filename, int *pChecksum ); -// returns 1 if a file is in the PAK file, otherwise -1 - -int FS_Write( const void *buffer, int len, fileHandle_t f ); - -int FS_Read2( void *buffer, int len, fileHandle_t f ); -int FS_Read( void *buffer, int len, fileHandle_t f ); -// properly handles partial reads and reads from other dlls - -void FS_FCloseFile( fileHandle_t f ); -// note: you can't just fclose from another DLL, due to MS libc issues - -int FS_ReadFile( const char *qpath, void **buffer ); -// returns the length of the file -// a null buffer will just return the file length without loading -// as a quick check for existance. -1 length == not present -// A 0 byte will always be appended at the end, so string ops are safe. -// the buffer should be considered read-only, because it may be cached -// for other uses. - -void FS_ForceFlush( fileHandle_t f ); -// forces flush on files we're writing to. - -void FS_FreeFile( void *buffer ); -// frees the memory returned by FS_ReadFile - -void FS_WriteFile( const char *qpath, const void *buffer, int size ); -// writes a complete file, creating any subdirectories needed - -int FS_filelength( fileHandle_t f ); -// doesn't work for files that are opened from a pack file - -int FS_FTell( fileHandle_t f ); -// where are we? - -void FS_Flush( fileHandle_t f ); - -void QDECL FS_Printf( fileHandle_t f, const char *fmt, ... ) __attribute__ ((format (printf, 2, 3))); -// like fprintf - -int FS_FOpenFileByMode( const char *qpath, fileHandle_t *f, fsMode_t mode ); -// opens a file for reading, writing, or appending depending on the value of mode - -int FS_Seek( fileHandle_t f, long offset, int origin ); -// seek on a file (doesn't work for zip files!!!!!!!!) - -qboolean FS_FilenameCompare( const char *s1, const char *s2 ); - -const char *FS_GamePureChecksum( void ); -// Returns the checksum of the pk3 from which the server loaded the qagame.qvm - -const char *FS_LoadedPakNames( void ); -const char *FS_LoadedPakChecksums( void ); -const char *FS_LoadedPakPureChecksums( void ); -// Returns a space separated string containing the checksums of all loaded pk3 files. -// Servers with sv_pure set will get this string and pass it to clients. - -const char *FS_ReferencedPakNames( void ); -const char *FS_ReferencedPakChecksums( void ); -const char *FS_ReferencedPakPureChecksums( void ); -// Returns a space separated string containing the checksums of all loaded -// AND referenced pk3 files. Servers with sv_pure set will get this string -// back from clients for pure validation - -void FS_ClearPakReferences( int flags ); -// clears referenced booleans on loaded pk3s - -void FS_PureServerSetReferencedPaks( const char *pakSums, const char *pakNames ); -void FS_PureServerSetLoadedPaks( const char *pakSums, const char *pakNames ); -// If the string is empty, all data sources will be allowed. -// If not empty, only pk3 files that match one of the space -// separated checksums will be checked for files, with the -// sole exception of .cfg files. - -qboolean FS_CheckDirTraversal(const char *checkdir); -qboolean FS_idPak( char *pak, char *base ); -qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring ); - -void FS_Rename( const char *from, const char *to ); - -void FS_Remove( const char *osPath ); -void FS_HomeRemove( const char *homePath ); - -void FS_FilenameCompletion( const char *dir, const char *ext, - qboolean stripExt, void(*callback)(const char *s) ); - -/* -============================================================== - -Edit fields and command line history/completion - -============================================================== -*/ - -#define MAX_EDIT_LINE 256 -typedef struct { - int cursor; - int scroll; - int widthInChars; - char buffer[MAX_EDIT_LINE]; -} field_t; - -void Field_Clear( field_t *edit ); -void Field_AutoComplete( field_t *edit ); -void Field_CompleteKeyname( void ); -void Field_CompleteFilename( const char *dir, - const char *ext, qboolean stripExt ); -void Field_CompleteCommand( char *cmd, - qboolean doCommands, qboolean doCvars ); - -/* -============================================================== - -MISC - -============================================================== -*/ - -// centralizing the declarations for cl_cdkey -// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470 -extern char cl_cdkey[34]; - -// returned by Sys_GetProcessorFeatures -typedef enum -{ - CF_RDTSC = 1 << 0, - CF_MMX = 1 << 1, - CF_MMX_EXT = 1 << 2, - CF_3DNOW = 1 << 3, - CF_3DNOW_EXT = 1 << 4, - CF_SSE = 1 << 5, - CF_SSE2 = 1 << 6, - CF_ALTIVEC = 1 << 7 -} cpuFeatures_t; - -// centralized and cleaned, that's the max string you can send to a Com_Printf / Com_DPrintf (above gets truncated) -#define MAXPRINTMSG 4096 - - -typedef enum { - // SE_NONE must be zero - SE_NONE = 0, // evTime is still valid - SE_KEY, // evValue is a key code, evValue2 is the down flag - SE_CHAR, // evValue is an ascii char - SE_MOUSE, // evValue and evValue2 are reletive signed x / y moves - SE_JOYSTICK_AXIS, // evValue is an axis number and evValue2 is the current state (-127 to 127) - SE_CONSOLE, // evPtr is a char* - SE_PACKET // evPtr is a netadr_t followed by data bytes to evPtrLength -} sysEventType_t; - -typedef struct { - int evTime; - sysEventType_t evType; - int evValue, evValue2; - int evPtrLength; // bytes of data pointed to by evPtr, for journaling - void *evPtr; // this must be manually freed if not NULL -} sysEvent_t; - -void Com_QueueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ); -int Com_EventLoop( void ); -sysEvent_t Com_GetSystemEvent( void ); - -char *CopyString( const char *in ); -void Info_Print( const char *s ); - -void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)(char *)); -void Com_EndRedirect( void ); -void QDECL Com_Printf( const char *fmt, ... ) __attribute__ ((format (printf, 1, 2))); -void QDECL Com_DPrintf( const char *fmt, ... ) __attribute__ ((format (printf, 1, 2))); -void QDECL Com_Error( int code, const char *fmt, ... ) __attribute__ ((format (printf, 2, 3))); -void Com_Quit_f( void ); -void Com_GameRestart(int checksumFeed, qboolean clientRestart); - -int Com_Milliseconds( void ); // will be journaled properly -unsigned Com_BlockChecksum( const void *buffer, int length ); -char *Com_MD5File(const char *filename, int length, const char *prefix, int prefix_len); -int Com_Filter(char *filter, char *name, int casesensitive); -int Com_FilterPath(char *filter, char *name, int casesensitive); -int Com_RealTime(qtime_t *qtime); -qboolean Com_SafeMode( void ); - -void Com_StartupVariable( const char *match ); -// checks for and removes command line "+set var arg" constructs -// if match is NULL, all set commands will be executed, otherwise -// only a set with the exact name. Only used during startup. - - -extern cvar_t *com_developer; -extern cvar_t *com_dedicated; -extern cvar_t *com_speeds; -extern cvar_t *com_timescale; -extern cvar_t *com_sv_running; -extern cvar_t *com_cl_running; -extern cvar_t *com_version; -extern cvar_t *com_blood; -extern cvar_t *com_buildScript; // for building release pak files -extern cvar_t *com_journal; -extern cvar_t *com_cameraMode; -extern cvar_t *com_ansiColor; -extern cvar_t *com_unfocused; -extern cvar_t *com_maxfpsUnfocused; -extern cvar_t *com_minimized; -extern cvar_t *com_maxfpsMinimized; -extern cvar_t *com_altivec; - -// both client and server must agree to pause -extern cvar_t *cl_paused; -extern cvar_t *sv_paused; - -extern cvar_t *cl_packetdelay; -extern cvar_t *sv_packetdelay; - -// com_speeds times -extern int time_game; -extern int time_frontend; -extern int time_backend; // renderer backend time - -extern int com_frameTime; -extern int com_frameMsec; - -extern qboolean com_errorEntered; - -extern fileHandle_t com_journalFile; -extern fileHandle_t com_journalDataFile; - -typedef enum { - TAG_FREE, - TAG_GENERAL, - TAG_BOTLIB, - TAG_RENDERER, - TAG_SMALL, - TAG_STATIC -} memtag_t; - -/* - ---- low memory ---- -server vm -server clipmap ----mark--- -renderer initialization (shaders, etc) -UI vm -cgame vm -renderer map -renderer models - ----free--- - -temp file loading ---- high memory --- - -*/ - -#if defined(_DEBUG) && !defined(BSPC) - #define ZONE_DEBUG -#endif - -#ifdef ZONE_DEBUG -#define Z_TagMalloc(size, tag) Z_TagMallocDebug(size, tag, #size, __FILE__, __LINE__) -#define Z_Malloc(size) Z_MallocDebug(size, #size, __FILE__, __LINE__) -#define S_Malloc(size) S_MallocDebug(size, #size, __FILE__, __LINE__) -void *Z_TagMallocDebug( int size, int tag, char *label, char *file, int line ); // NOT 0 filled memory -void *Z_MallocDebug( int size, char *label, char *file, int line ); // returns 0 filled memory -void *S_MallocDebug( int size, char *label, char *file, int line ); // returns 0 filled memory -#else -void *Z_TagMalloc( int size, int tag ); // NOT 0 filled memory -void *Z_Malloc( int size ); // returns 0 filled memory -void *S_Malloc( int size ); // NOT 0 filled memory only for small allocations -#endif -void Z_Free( void *ptr ); -void Z_FreeTags( int tag ); -int Z_AvailableMemory( void ); -void Z_LogHeap( void ); - -void Hunk_Clear( void ); -void Hunk_ClearToMark( void ); -void Hunk_SetMark( void ); -qboolean Hunk_CheckMark( void ); -void Hunk_ClearTempMemory( void ); -void *Hunk_AllocateTempMemory( int size ); -void Hunk_FreeTempMemory( void *buf ); -int Hunk_MemoryRemaining( void ); -void Hunk_Log( void); -void Hunk_Trash( void ); - -void Com_TouchMemory( void ); - -// commandLine should not include the executable name (argv[0]) -void Com_Init( char *commandLine ); -void Com_Frame( void ); -void Com_Shutdown( void ); - - -/* -============================================================== - -CLIENT / SERVER SYSTEMS - -============================================================== -*/ - -// -// client interface -// -void CL_InitKeyCommands( void ); -// the keyboard binding interface must be setup before execing -// config files, but the rest of client startup will happen later - -void CL_Init( void ); -void CL_Disconnect( qboolean showMainMenu ); -void CL_Shutdown( char *finalmsg ); -void CL_Frame( int msec ); -qboolean CL_GameCommand( void ); -void CL_KeyEvent (int key, qboolean down, unsigned time); - -void CL_CharEvent( int key ); -// char events are for field typing, not game control - -void CL_MouseEvent( int dx, int dy, int time ); - -void CL_JoystickEvent( int axis, int value, int time ); - -void CL_PacketEvent( netadr_t from, msg_t *msg ); - -void CL_ConsolePrint( char *text ); - -void CL_MapLoading( void ); -// do a screen update before starting to load a map -// when the server is going to load a new map, the entire hunk -// will be cleared, so the client must shutdown cgame, ui, and -// the renderer - -void CL_ForwardCommandToServer( const char *string ); -// adds the current command line as a clc_clientCommand 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 CL_CDDialog( void ); -// bring up the "need a cd to play" dialog - -void CL_ShutdownAll( void ); -// shutdown all the client stuff - -void CL_FlushMemory( void ); -// dump all memory on an error - -void CL_StartHunkUsers( qboolean rendererOnly ); -// start all the client stuff using the hunk - -void CL_Snd_Restart(void); -// Restart sound subsystem - -void Key_KeynameCompletion( void(*callback)(const char *s) ); -// for keyname autocompletion - -void Key_WriteBindings( fileHandle_t f ); -// for writing the config files - -void S_ClearSoundBuffer( void ); -// call before filesystem access - -void SCR_DebugGraph (float value, int color); // FIXME: move logging to common? - -// -// server interface -// -void SV_Init( void ); -void SV_Shutdown( char *finalmsg ); -void SV_Frame( int msec ); -void SV_PacketEvent( netadr_t from, msg_t *msg ); -qboolean SV_GameCommand( void ); - - -// -// UI interface -// -qboolean UI_GameCommand( void ); -qboolean UI_usesUniqueCDKey(void); - -/* -============================================================== - -NON-PORTABLE SYSTEM SERVICES - -============================================================== -*/ - -typedef enum { - AXIS_SIDE, - AXIS_FORWARD, - AXIS_UP, - AXIS_ROLL, - AXIS_YAW, - AXIS_PITCH, - MAX_JOYSTICK_AXIS -} joystickAxis_t; - -void Sys_Init (void); - -// general development dll loading for virtual machine testing -void * QDECL Sys_LoadDll( const char *name, char *fqpath , intptr_t (QDECL **entryPoint)(int, ...), - intptr_t (QDECL *systemcalls)(intptr_t, ...) ); -void Sys_UnloadDll( void *dllHandle ); - -void Sys_UnloadGame( void ); -void *Sys_GetGameAPI( void *parms ); - -void Sys_UnloadCGame( void ); -void *Sys_GetCGameAPI( void ); - -void Sys_UnloadUI( void ); -void *Sys_GetUIAPI( void ); - -//bot libraries -void Sys_UnloadBotLib( void ); -void *Sys_GetBotLibAPI( void *parms ); - -char *Sys_GetCurrentUser( void ); - -void QDECL Sys_Error( const char *error, ...) __attribute__ ((format (printf, 1, 2))); -void Sys_Quit (void); -char *Sys_GetClipboardData( void ); // note that this isn't journaled... - -void Sys_Print( const char *msg ); - -// Sys_Milliseconds should only be used for profiling purposes, -// any game related timing information should come from event timestamps -int Sys_Milliseconds (void); - -void Sys_SnapVector( float *v ); - -qboolean Sys_RandomBytes( byte *string, int len ); - -// the system console is shown when a dedicated server is running -void Sys_DisplaySystemConsole( qboolean show ); - -cpuFeatures_t Sys_GetProcessorFeatures( void ); - -void Sys_SetErrorText( const char *text ); - -void Sys_SendPacket( int length, const void *data, netadr_t to ); -qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ); - -qboolean Sys_StringToAdr( const char *s, netadr_t *a, netadrtype_t family ); -//Does NOT parse port numbers, only base addresses. - -qboolean Sys_IsLANAddress (netadr_t adr); -void Sys_ShowIP(void); - -qboolean Sys_Mkdir( const char *path ); -char *Sys_Cwd( void ); -void Sys_SetDefaultInstallPath(const char *path); -char *Sys_DefaultInstallPath(void); - -#ifdef MACOS_X -char *Sys_DefaultAppPath(void); -#endif - -void Sys_SetDefaultHomePath(const char *path); -char *Sys_DefaultHomePath(void); -const char *Sys_TempPath(void); -const char *Sys_Dirname( char *path ); -const char *Sys_Basename( char *path ); -char *Sys_ConsoleInput(void); - -char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs ); -void Sys_FreeFileList( char **list ); -void Sys_Sleep(int msec); - -qboolean Sys_LowPhysicalMemory( void ); - -void Sys_SetEnv(const char *name, const char *value); - -typedef enum -{ - DR_YES = 0, - DR_NO = 1, - DR_OK = 0, - DR_CANCEL = 1 -} dialogResult_t; - -typedef enum -{ - DT_INFO, - DT_WARNING, - DT_ERROR, - DT_YES_NO, - DT_OK_CANCEL -} dialogType_t; - -dialogResult_t Sys_Dialog( dialogType_t type, const char *message, const char *title ); - -qboolean Sys_WritePIDFile( void ); - -/* This is based on the Adaptive Huffman algorithm described in Sayood's Data - * Compression book. The ranks are not actually stored, but implicitly defined - * by the location of a node within a doubly-linked list */ - -#define NYT HMAX /* NYT = Not Yet Transmitted */ -#define INTERNAL_NODE (HMAX+1) - -typedef struct nodetype { - struct nodetype *left, *right, *parent; /* tree structure */ - struct nodetype *next, *prev; /* doubly-linked list */ - struct nodetype **head; /* highest ranked node in block */ - int weight; - int symbol; -} node_t; - -#define HMAX 256 /* Maximum symbol */ - -typedef struct { - int blocNode; - int blocPtrs; - - node_t* tree; - node_t* lhead; - node_t* ltail; - node_t* loc[HMAX+1]; - node_t** freelist; - - node_t nodeList[768]; - node_t* nodePtrs[768]; -} huff_t; - -typedef struct { - huff_t compressor; - huff_t decompressor; -} huffman_t; - -void Huff_Compress(msg_t *buf, int offset); -void Huff_Decompress(msg_t *buf, int offset); -void Huff_Init(huffman_t *huff); -void Huff_addRef(huff_t* huff, byte ch); -int Huff_Receive (node_t *node, int *ch, byte *fin); -void Huff_transmit (huff_t *huff, int ch, byte *fout); -void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset); -void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset); -void Huff_putBit( int bit, byte *fout, int *offset); -int Huff_getBit( byte *fout, int *offset); - -// don't use if you don't know what you're doing. -int Huff_getBloc(void); -void Huff_setBloc(int _bloc); - - -extern huffman_t clientHuffTables; - -#define SV_ENCODE_START 4 -#define SV_DECODE_START 12 -#define CL_ENCODE_START 12 -#define CL_DECODE_START 4 - -// flags for sv_allowDownload and cl_allowDownload -#define DLF_ENABLE 1 -#define DLF_NO_REDIRECT 2 -#define DLF_NO_UDP 4 -#define DLF_NO_DISCONNECT 8 - -#endif // _QCOMMON_H_ diff --git a/tools/quake3/bspc/deps/qcommon/qfiles.h b/tools/quake3/bspc/deps/qcommon/qfiles.h deleted file mode 100644 index 1c1910a3..00000000 --- a/tools/quake3/bspc/deps/qcommon/qfiles.h +++ /dev/null @@ -1,581 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#ifndef __QFILES_H__ -#define __QFILES_H__ - -// -// qfiles.h: quake file formats -// This file must be identical in the quake and utils directories -// - -//Ignore __attribute__ on non-gcc platforms -#ifndef __GNUC__ -#ifndef __attribute__ -#define __attribute__(x) -#endif -#endif - -// surface geometry should not exceed these limits -#define SHADER_MAX_VERTEXES 1000 -#define SHADER_MAX_INDEXES (6*SHADER_MAX_VERTEXES) - - -// the maximum size of game relative pathnames -#define MAX_QPATH 64 - -/* -======================================================================== - -QVM files - -======================================================================== -*/ - -#define VM_MAGIC 0x12721444 -#define VM_MAGIC_VER2 0x12721445 -typedef struct { - int vmMagic; - - int instructionCount; - - int codeOffset; - int codeLength; - - int dataOffset; - int dataLength; - int litLength; // ( dataLength - litLength ) should be byteswapped on load - int bssLength; // zero filled memory appended to datalength - - //!!! below here is VM_MAGIC_VER2 !!! - int jtrgLength; // number of jump table targets -} vmHeader_t; - -/* -======================================================================== - -.MD3 triangle model file format - -======================================================================== -*/ - -#define MD3_IDENT (('3'<<24)+('P'<<16)+('D'<<8)+'I') -#define MD3_VERSION 15 - -// limits -#define MD3_MAX_LODS 3 -#define MD3_MAX_TRIANGLES 8192 // per surface -#define MD3_MAX_VERTS 4096 // per surface -#define MD3_MAX_SHADERS 256 // per surface -#define MD3_MAX_FRAMES 1024 // per model -#define MD3_MAX_SURFACES 32 // per model -#define MD3_MAX_TAGS 16 // per frame - -// vertex scales -#define MD3_XYZ_SCALE (1.0/64) - -typedef struct md3Frame_s { - vec3_t bounds[2]; - vec3_t localOrigin; - float radius; - char name[16]; -} md3Frame_t; - -typedef struct md3Tag_s { - char name[MAX_QPATH]; // tag name - vec3_t origin; - vec3_t axis[3]; -} md3Tag_t; - -/* -** md3Surface_t -** -** CHUNK SIZE -** header sizeof( md3Surface_t ) -** shaders sizeof( md3Shader_t ) * numShaders -** triangles[0] sizeof( md3Triangle_t ) * numTriangles -** st sizeof( md3St_t ) * numVerts -** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numFrames -*/ -typedef struct { - int ident; // - - char name[MAX_QPATH]; // polyset name - - int flags; - int numFrames; // all surfaces in a model should have the same - - int numShaders; // all surfaces in a model should have the same - int numVerts; - - int numTriangles; - int ofsTriangles; - - int ofsShaders; // offset from start of md3Surface_t - int ofsSt; // texture coords are common for all frames - int ofsXyzNormals; // numVerts * numFrames - - int ofsEnd; // next surface follows -} md3Surface_t; - -typedef struct { - char name[MAX_QPATH]; - int shaderIndex; // for in-game use -} md3Shader_t; - -typedef struct { - int indexes[3]; -} md3Triangle_t; - -typedef struct { - float st[2]; -} md3St_t; - -typedef struct { - short xyz[3]; - short normal; -} md3XyzNormal_t; - -typedef struct { - int ident; - int version; - - char name[MAX_QPATH]; // model name - - int flags; - - int numFrames; - int numTags; - int numSurfaces; - - int numSkins; - - int ofsFrames; // offset for first frame - int ofsTags; // numFrames * numTags - int ofsSurfaces; // first surface, others follow - - int ofsEnd; // end of file -} md3Header_t; - -/* -============================================================================== - -MD4 file format - -============================================================================== -*/ - -#define MD4_IDENT (('4'<<24)+('P'<<16)+('D'<<8)+'I') -#define MD4_VERSION 1 -#define MD4_MAX_BONES 128 - -typedef struct { - int boneIndex; // these are indexes into the boneReferences, - float boneWeight; // not the global per-frame bone list - vec3_t offset; -} md4Weight_t; - -typedef struct { - vec3_t normal; - vec2_t texCoords; - int numWeights; - md4Weight_t weights[1]; // variable sized -} md4Vertex_t; - -typedef struct { - int indexes[3]; -} md4Triangle_t; - -typedef struct { - int ident; - - char name[MAX_QPATH]; // polyset name - char shader[MAX_QPATH]; - int shaderIndex; // for in-game use - - int ofsHeader; // this will be a negative number - - int numVerts; - int ofsVerts; - - int numTriangles; - int ofsTriangles; - - // Bone references are a set of ints representing all the bones - // present in any vertex weights for this surface. This is - // needed because a model may have surfaces that need to be - // drawn at different sort times, and we don't want to have - // to re-interpolate all the bones for each surface. - int numBoneReferences; - int ofsBoneReferences; - - int ofsEnd; // next surface follows -} md4Surface_t; - -typedef struct { - float matrix[3][4]; -} md4Bone_t; - -typedef struct { - vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame - vec3_t localOrigin; // midpoint of bounds, used for sphere cull - float radius; // dist from localOrigin to corner - md4Bone_t bones[1]; // [numBones] -} md4Frame_t; - -typedef struct { - int numSurfaces; - int ofsSurfaces; // first surface, others follow - int ofsEnd; // next lod follows -} md4LOD_t; - -typedef struct { - int ident; - int version; - - char name[MAX_QPATH]; // model name - - // frames and bones are shared by all levels of detail - int numFrames; - int numBones; - int ofsBoneNames; // char name[ MAX_QPATH ] - int ofsFrames; // md4Frame_t[numFrames] - - // each level of detail has completely separate sets of surfaces - int numLODs; - int ofsLODs; - - int ofsEnd; // end of file -} md4Header_t; - -/* - * Here are the definitions for Ravensoft's model format of md4. Raven stores their - * playermodels in .mdr files, in some games, which are pretty much like the md4 - * format implemented by ID soft. It seems like ID's original md4 stuff is not used at all. - * MDR is being used in EliteForce, JediKnight2 and Soldiers of Fortune2 (I think). - * So this comes in handy for anyone who wants to make it possible to load player - * models from these games. - * This format has bone tags, which is similar to the thing you have in md3 I suppose. - * Raven has released their version of md3view under GPL enabling me to add support - * to this codebase. Thanks to Steven Howes aka Skinner for helping with example - * source code. - * - * - Thilo Schulz (arny@ats.s.bawue.de) - */ - -// If you want to enable support for Raven's .mdr / md4 format, uncomment the next -// line. -//#define RAVENMD4 - -#ifdef RAVENMD4 - -#define MDR_IDENT (('5'<<24)+('M'<<16)+('D'<<8)+'R') -#define MDR_VERSION 2 -#define MDR_MAX_BONES 128 - -typedef struct { - int boneIndex; // these are indexes into the boneReferences, - float boneWeight; // not the global per-frame bone list - vec3_t offset; -} mdrWeight_t; - -typedef struct { - vec3_t normal; - vec2_t texCoords; - int numWeights; - mdrWeight_t weights[1]; // variable sized -} mdrVertex_t; - -typedef struct { - int indexes[3]; -} mdrTriangle_t; - -typedef struct { - int ident; - - char name[MAX_QPATH]; // polyset name - char shader[MAX_QPATH]; - int shaderIndex; // for in-game use - - int ofsHeader; // this will be a negative number - - int numVerts; - int ofsVerts; - - int numTriangles; - int ofsTriangles; - - // Bone references are a set of ints representing all the bones - // present in any vertex weights for this surface. This is - // needed because a model may have surfaces that need to be - // drawn at different sort times, and we don't want to have - // to re-interpolate all the bones for each surface. - int numBoneReferences; - int ofsBoneReferences; - - int ofsEnd; // next surface follows -} mdrSurface_t; - -typedef struct { - float matrix[3][4]; -} mdrBone_t; - -typedef struct { - vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame - vec3_t localOrigin; // midpoint of bounds, used for sphere cull - float radius; // dist from localOrigin to corner - char name[16]; - mdrBone_t bones[1]; // [numBones] -} mdrFrame_t; - -typedef struct { - unsigned char Comp[24]; // MC_COMP_BYTES is in MatComp.h, but don't want to couple -} mdrCompBone_t; - -typedef struct { - vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame - vec3_t localOrigin; // midpoint of bounds, used for sphere cull - float radius; // dist from localOrigin to corner - mdrCompBone_t bones[1]; // [numBones] -} mdrCompFrame_t; - -typedef struct { - int numSurfaces; - int ofsSurfaces; // first surface, others follow - int ofsEnd; // next lod follows -} mdrLOD_t; - -typedef struct { - int boneIndex; - char name[32]; -} mdrTag_t; - -typedef struct { - int ident; - int version; - - char name[MAX_QPATH]; // model name - - // frames and bones are shared by all levels of detail - int numFrames; - int numBones; - int ofsFrames; // mdrFrame_t[numFrames] - - // each level of detail has completely separate sets of surfaces - int numLODs; - int ofsLODs; - - int numTags; - int ofsTags; - - int ofsEnd; // end of file -} mdrHeader_t; - -#endif - -/* -============================================================================== - - .BSP file format - -============================================================================== -*/ - - -#define BSP_IDENT (('P'<<24)+('S'<<16)+('B'<<8)+'I') - // little-endian "IBSP" - -#define BSP_VERSION 46 -#define BSP_VERSION_QL 47 // for quakelive :ss - -// there shouldn't be any problem with increasing these values at the -// expense of more memory allocation in the utilities -#define MAX_MAP_MODELS 0x400 -#define MAX_MAP_BRUSHES 0x8000 -#define MAX_MAP_ENTITIES 0x800 -#define MAX_MAP_ENTSTRING 0x40000 -#define MAX_MAP_SHADERS 0x400 - -#define MAX_MAP_AREAS 0x100 // MAX_MAP_AREA_BYTES in q_shared must match! -#define MAX_MAP_FOGS 0x100 -#define MAX_MAP_PLANES 0x20000 -#define MAX_MAP_NODES 0x20000 -#define MAX_MAP_BRUSHSIDES 0x20000 -#define MAX_MAP_LEAFS 0x20000 -#define MAX_MAP_LEAFFACES 0x20000 -#define MAX_MAP_LEAFBRUSHES 0x40000 -#define MAX_MAP_PORTALS 0x20000 -#define MAX_MAP_LIGHTING 0x800000 -#define MAX_MAP_LIGHTGRID 0x800000 -#define MAX_MAP_VISIBILITY 0x200000 - -#define MAX_MAP_DRAW_SURFS 0x20000 -#define MAX_MAP_DRAW_VERTS 0x80000 -#define MAX_MAP_DRAW_INDEXES 0x80000 - - -// key / value pair sizes in the entities lump -#define MAX_KEY 32 -#define MAX_VALUE 1024 - -// the editor uses these predefined yaw angles to orient entities up or down -#define ANGLE_UP -1 -#define ANGLE_DOWN -2 - -#define LIGHTMAP_WIDTH 128 -#define LIGHTMAP_HEIGHT 128 - -#define MAX_WORLD_COORD ( 128*1024 ) -#define MIN_WORLD_COORD ( -128*1024 ) -#define WORLD_SIZE ( MAX_WORLD_COORD - MIN_WORLD_COORD ) - -//============================================================================= - - -typedef struct { - int fileofs, filelen; -} lump_t; - -#define LUMP_ENTITIES 0 -#define LUMP_SHADERS 1 -#define LUMP_PLANES 2 -#define LUMP_NODES 3 -#define LUMP_LEAFS 4 -#define LUMP_LEAFSURFACES 5 -#define LUMP_LEAFBRUSHES 6 -#define LUMP_MODELS 7 -#define LUMP_BRUSHES 8 -#define LUMP_BRUSHSIDES 9 -#define LUMP_DRAWVERTS 10 -#define LUMP_DRAWINDEXES 11 -#define LUMP_FOGS 12 -#define LUMP_SURFACES 13 -#define LUMP_LIGHTMAPS 14 -#define LUMP_LIGHTGRID 15 -#define LUMP_VISIBILITY 16 -#define HEADER_LUMPS 17 - -typedef struct { - int ident; - int version; - - lump_t lumps[HEADER_LUMPS]; -} dheader_t; - -typedef struct { - float mins[3], maxs[3]; - int firstSurface, numSurfaces; - int firstBrush, numBrushes; -} dmodel_t; - -typedef struct { - char shader[MAX_QPATH]; - int surfaceFlags; - int contentFlags; -} dshader_t; - -// planes x^1 is allways the opposite of plane x - -typedef struct { - float normal[3]; - float dist; -} dplane_t; - -typedef struct { - int planeNum; - int children[2]; // negative numbers are -(leafs+1), not nodes - int mins[3]; // for frustom culling - int maxs[3]; -} dnode_t; - -typedef struct { - int cluster; // -1 = opaque cluster (do I still store these?) - int area; - - int mins[3]; // for frustum culling - int maxs[3]; - - int firstLeafSurface; - int numLeafSurfaces; - - int firstLeafBrush; - int numLeafBrushes; -} dleaf_t; - -typedef struct { - int planeNum; // positive plane side faces out of the leaf - int shaderNum; -} dbrushside_t; - -typedef struct { - int firstSide; - int numSides; - int shaderNum; // the shader that determines the contents flags -} dbrush_t; - -typedef struct { - char shader[MAX_QPATH]; - int brushNum; - int visibleSide; // the brush side that ray tests need to clip against (-1 == none) -} dfog_t; - -typedef struct { - vec3_t xyz; - float st[2]; - float lightmap[2]; - vec3_t normal; - byte color[4]; -} drawVert_t; - -#define drawVert_t_cleared(x) drawVert_t (x) = {{0, 0, 0}, {0, 0}, {0, 0}, {0, 0, 0}, {0, 0, 0, 0}} - -typedef enum { - MST_BAD, - MST_PLANAR, - MST_PATCH, - MST_TRIANGLE_SOUP, - MST_FLARE -} mapSurfaceType_t; - -typedef struct { - int shaderNum; - int fogNum; - int surfaceType; - - int firstVert; - int numVerts; - - int firstIndex; - int numIndexes; - - int lightmapNum; - int lightmapX, lightmapY; - int lightmapWidth, lightmapHeight; - - vec3_t lightmapOrigin; - vec3_t lightmapVecs[3]; // for patches, [0] and [1] are lodbounds - - int patchWidth; - int patchHeight; -} dsurface_t; - - -#endif diff --git a/tools/quake3/bspc/deps/qcommon/surfaceflags.h b/tools/quake3/bspc/deps/qcommon/surfaceflags.h deleted file mode 100644 index b7c10a17..00000000 --- a/tools/quake3/bspc/deps/qcommon/surfaceflags.h +++ /dev/null @@ -1,80 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// -// This file must be identical in the quake and utils directories - -// contents flags are seperate bits -// a given brush can contribute multiple content bits - -// these definitions also need to be in q_shared.h! - -#define CONTENTS_SOLID 1 // an eye is never valid in a solid -#define CONTENTS_LAVA 8 -#define CONTENTS_SLIME 16 -#define CONTENTS_WATER 32 -#define CONTENTS_FOG 64 - -#define CONTENTS_NOTTEAM1 0x0080 -#define CONTENTS_NOTTEAM2 0x0100 -#define CONTENTS_NOBOTCLIP 0x0200 - -#define CONTENTS_AREAPORTAL 0x8000 - -#define CONTENTS_PLAYERCLIP 0x10000 -#define CONTENTS_MONSTERCLIP 0x20000 -//bot specific contents types -#define CONTENTS_TELEPORTER 0x40000 -#define CONTENTS_JUMPPAD 0x80000 -#define CONTENTS_CLUSTERPORTAL 0x100000 -#define CONTENTS_DONOTENTER 0x200000 -#define CONTENTS_BOTCLIP 0x400000 -#define CONTENTS_MOVER 0x800000 - -#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity - -#define CONTENTS_BODY 0x2000000 // should never be on a brush, only in game -#define CONTENTS_CORPSE 0x4000000 -#define CONTENTS_DETAIL 0x8000000 // brushes not used for the bsp -#define CONTENTS_STRUCTURAL 0x10000000 // brushes used for the bsp -#define CONTENTS_TRANSLUCENT 0x20000000 // don't consume surface fragments inside -#define CONTENTS_TRIGGER 0x40000000 -#define CONTENTS_NODROP 0x80000000 // don't leave bodies or items (death fog, lava) - -#define SURF_NODAMAGE 0x1 // never give falling damage -#define SURF_SLICK 0x2 // effects game physics -#define SURF_SKY 0x4 // lighting from environment map -#define SURF_LADDER 0x8 -#define SURF_NOIMPACT 0x10 // don't make missile explosions -#define SURF_NOMARKS 0x20 // don't leave missile marks -#define SURF_FLESH 0x40 // make flesh sounds and effects -#define SURF_NODRAW 0x80 // don't generate a drawsurface at all -#define SURF_HINT 0x100 // make a primary bsp splitter -#define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes -#define SURF_NOLIGHTMAP 0x400 // surface doesn't need a lightmap -#define SURF_POINTLIGHT 0x800 // generate lighting info at vertexes -#define SURF_METALSTEPS 0x1000 // clanking footsteps -#define SURF_NOSTEPS 0x2000 // no footstep sounds -#define SURF_NONSOLID 0x4000 // don't collide against curves with this set -#define SURF_LIGHTFILTER 0x8000 // act as a light filter during q3map -light -#define SURF_ALPHASHADOW 0x10000 // do per-pixel light shadow casting in q3map -#define SURF_NODLIGHT 0x20000 // don't dlight even if solid (solid lava, skies) -#define SURF_DUST 0x40000 // leave a dust trail when walking on this surface diff --git a/tools/quake3/bspc/deps/qcommon/unzip.c b/tools/quake3/bspc/deps/qcommon/unzip.c deleted file mode 100644 index 6c52e1f2..00000000 --- a/tools/quake3/bspc/deps/qcommon/unzip.c +++ /dev/null @@ -1,4299 +0,0 @@ -/***************************************************************************** - * name: unzip.c - * - * desc: IO on .zip files using portions of zlib - * - * $Archive: /MissionPack/code/qcommon/unzip.c $ - * - *****************************************************************************/ - -#include "qcommon/qcommon.h" -#include "unzip.h" - -/* unzip.h -- IO for uncompress .zip files using zlib - Version 0.15 beta, Mar 19th, 1998, - - Copyright (C) 1998 Gilles Vollant - - This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g - WinZip, InfoZip tools and compatible. - Encryption and multi volume ZipFile (span) are not supported. - Old compressions used by old PKZip 1.x are not supported - - THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE - CAN CHANGE IN FUTURE VERSION !! - I WAIT FEEDBACK at mail info@winimage.com - Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution - - Condition of use and distribution are the same than zlib : - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - -*/ -/* for more info about .ZIP format, see - ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip - PkWare has also a specification at : - ftp://ftp.pkware.com/probdesc.zip */ - -/* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.1.3, July 9th, 1998 - - Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu - - - The data format used by the zlib library is described by RFCs (Request for - Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt - (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). -*/ - -/* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-1998 Jean-loup Gailly. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - - -#ifndef _ZCONF_H -#define _ZCONF_H - -/* Maximum value for memLevel in deflateInit2 */ -#ifndef MAX_MEM_LEVEL -# ifdef MAXSEG_64K -# define MAX_MEM_LEVEL 8 -# else -# define MAX_MEM_LEVEL 9 -# endif -#endif - -/* Maximum value for windowBits in deflateInit2 and inflateInit2. - * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files - * created by gzip. (Files created by minigzip can still be extracted by - * gzip.) - */ -#ifndef MAX_WBITS -# define MAX_WBITS 15 /* 32K LZ77 window */ -#endif - -/* The memory requirements for deflate are (in bytes): - (1 << (windowBits+2)) + (1 << (memLevel+9)) - that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) - plus a few kilobytes for small objects. For example, if you want to reduce - the default memory requirements from 256K to 128K, compile with - make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" - Of course this will generally degrade compression (there's no free lunch). - - The memory requirements for inflate are (in bytes) 1 << windowBits - that is, 32K for windowBits=15 (default value) plus a few kilobytes - for small objects. -*/ - - /* Type declarations */ - -#ifndef OF /* function prototypes */ -#define OF(args) args -#endif - -typedef unsigned char Byte; /* 8 bits */ -typedef unsigned int uInt; /* 16 bits or more */ -typedef unsigned long uLong; /* 32 bits or more */ -typedef Byte *voidp; - -#ifndef SEEK_SET -# define SEEK_SET 0 /* Seek from beginning of file. */ -# define SEEK_CUR 1 /* Seek from current position. */ -# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ -#endif - -#endif /* _ZCONF_H */ - -#define ZLIB_VERSION "1.1.3" - -/* - The 'zlib' compression library provides in-memory compression and - decompression functions, including integrity checks of the uncompressed - data. This version of the library supports only one compression method - (deflation) but other algorithms will be added later and will have the same - stream interface. - - Compression can be done in a single step if the buffers are large - enough (for example if an input file is mmap'ed), or can be done by - repeated calls of the compression function. In the latter case, the - application must provide more input and/or consume the output - (providing more output space) before each call. - - The library also supports reading and writing files in gzip (.gz) format - with an interface similar to that of stdio. - - The library does not install any signal handler. The decoder checks - the consistency of the compressed data, so the library should never - crash even in case of corrupted input. -*/ - -/* - The application must update next_in and avail_in when avail_in has - dropped to zero. It must update next_out and avail_out when avail_out - has dropped to zero. The application must initialize zalloc, zfree and - opaque before calling the init function. All other fields are set by the - compression library and must not be updated by the application. - - The opaque value provided by the application will be passed as the first - parameter for calls of zalloc and zfree. This can be useful for custom - memory management. The compression library attaches no meaning to the - opaque value. - - zalloc must return Z_NULL if there is not enough memory for the object. - If zlib is used in a multi-threaded application, zalloc and zfree must be - thread safe. - - On 16-bit systems, the functions zalloc and zfree must be able to allocate - exactly 65536 bytes, but will not be required to allocate more than this - if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, - pointers returned by zalloc for objects of exactly 65536 bytes *must* - have their offset normalized to zero. The default allocation function - provided by this library ensures this (see zutil.c). To reduce memory - requirements and avoid any allocation of 64K objects, at the expense of - compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). - - The fields total_in and total_out can be used for statistics or - progress reports. After compression, total_in holds the total size of - the uncompressed data and may be saved for use in the decompressor - (particularly if the decompressor wants to decompress everything in - a single step). -*/ - - /* constants */ - -#define Z_NO_FLUSH 0 -#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ -#define Z_SYNC_FLUSH 2 -#define Z_FULL_FLUSH 3 -#define Z_FINISH 4 -/* Allowed flush values; see deflate() below for details */ - -#define Z_OK 0 -#define Z_STREAM_END 1 -#define Z_NEED_DICT 2 -#define Z_ERRNO (-1) -#define Z_STREAM_ERROR (-2) -#define Z_DATA_ERROR (-3) -#define Z_MEM_ERROR (-4) -#define Z_BUF_ERROR (-5) -#define Z_VERSION_ERROR (-6) -/* Return codes for the compression/decompression functions. Negative - * values are errors, positive values are used for special but normal events. - */ - -#define Z_NO_COMPRESSION 0 -#define Z_BEST_SPEED 1 -#define Z_BEST_COMPRESSION 9 -#define Z_DEFAULT_COMPRESSION (-1) -/* compression levels */ - -#define Z_FILTERED 1 -#define Z_HUFFMAN_ONLY 2 -#define Z_DEFAULT_STRATEGY 0 -/* compression strategy; see deflateInit2() below for details */ - -#define Z_BINARY 0 -#define Z_ASCII 1 -#define Z_UNKNOWN 2 -/* Possible values of the data_type field */ - -#define Z_DEFLATED 8 -/* The deflate compression method (the only one supported in this version) */ - -#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ - -#define zlib_version zlibVersion() -/* for compatibility with versions < 1.0.2 */ - - /* basic functions */ - -// static const char * zlibVersion OF((void)); -/* The application can compare zlibVersion and ZLIB_VERSION for consistency. - If the first character differs, the library code actually used is - not compatible with the zlib.h header file used by the application. - This check is automatically made by deflateInit and inflateInit. - */ - -/* -int deflateInit OF((z_streamp strm, int level)); - - Initializes the internal stream state for compression. The fields - zalloc, zfree and opaque must be initialized before by the caller. - If zalloc and zfree are set to Z_NULL, deflateInit updates them to - use default allocation functions. - - The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: - 1 gives best speed, 9 gives best compression, 0 gives no compression at - all (the input data is simply copied a block at a time). - Z_DEFAULT_COMPRESSION requests a default compromise between speed and - compression (currently equivalent to level 6). - - deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if level is not a valid compression level, - Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible - with the version assumed by the caller (ZLIB_VERSION). - msg is set to null if there is no error message. deflateInit does not - perform any compression: this will be done by deflate(). -*/ - - -// static int deflate OF((z_streamp strm, int flush)); -/* - deflate compresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce some - output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. deflate performs one or both of the - following actions: - - - Compress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in and avail_in are updated and - processing will resume at this point for the next call of deflate(). - - - Provide more output starting at next_out and update next_out and avail_out - accordingly. This action is forced if the parameter flush is non zero. - Forcing flush frequently degrades the compression ratio, so this parameter - should be set only when necessary (in interactive applications). - Some output may be provided even if flush is not set. - - Before the call of deflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming - more output, and updating avail_in or avail_out accordingly; avail_out - should never be zero before the call. The application can consume the - compressed output when it wants, for example when the output buffer is full - (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK - and with zero avail_out, it must be called again after making room in the - output buffer because there might be more output pending. - - If the parameter flush is set to Z_SYNC_FLUSH, all pending output is - flushed to the output buffer and the output is aligned on a byte boundary, so - that the decompressor can get all input data available so far. (In particular - avail_in is zero after the call if enough output space has been provided - before the call.) Flushing may degrade compression for some compression - algorithms and so it should be used only when necessary. - - If flush is set to Z_FULL_FLUSH, all output is flushed as with - Z_SYNC_FLUSH, and the compression state is reset so that decompression can - restart from this point if previous compressed data has been damaged or if - random access is desired. Using Z_FULL_FLUSH too often can seriously degrade - the compression. - - If deflate returns with avail_out == 0, this function must be called again - with the same value of the flush parameter and more output space (updated - avail_out), until the flush is complete (deflate returns with non-zero - avail_out). - - If the parameter flush is set to Z_FINISH, pending input is processed, - pending output is flushed and deflate returns with Z_STREAM_END if there - was enough output space; if deflate returns with Z_OK, this function must be - called again with Z_FINISH and more output space (updated avail_out) but no - more input data, until it returns with Z_STREAM_END or an error. After - deflate has returned Z_STREAM_END, the only possible operations on the - stream are deflateReset or deflateEnd. - - Z_FINISH can be used immediately after deflateInit if all the compression - is to be done in a single step. In this case, avail_out must be at least - 0.1% larger than avail_in plus 12 bytes. If deflate does not return - Z_STREAM_END, then it must be called again as described above. - - deflate() sets strm->adler to the adler32 checksum of all input read - so (that is, total_in bytes). - - deflate() may update data_type if it can make a good guess about - the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered - binary. This field is only for information purposes and does not affect - the compression algorithm in any manner. - - deflate() returns Z_OK if some progress has been made (more input - processed or more output produced), Z_STREAM_END if all input has been - consumed and all output has been produced (only when flush is set to - Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible - (for example avail_in or avail_out was zero). -*/ - - -// static int deflateEnd OF((z_streamp strm)); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. - - deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the - stream state was inconsistent, Z_DATA_ERROR if the stream was freed - prematurely (some input or output was discarded). In the error case, - msg may be set but then points to a static string (which must not be - deallocated). -*/ - - -/* -int inflateInit OF((z_streamp strm)); - - Initializes the internal stream state for decompression. The fields - next_in, avail_in, zalloc, zfree and opaque must be initialized before by - the caller. If next_in is not Z_NULL and avail_in is large enough (the exact - value depends on the compression method), inflateInit determines the - compression method from the zlib header and allocates all data structures - accordingly; otherwise the allocation will be deferred to the first call of - inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to - use default allocation functions. - - inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller. msg is set to null if there is no error - message. inflateInit does not perform any decompression apart from reading - the zlib header if present: this will be done by inflate(). (So next_in and - avail_in may be modified, but next_out and avail_out are unchanged.) -*/ - - -static int inflate OF((z_streamp strm, int flush)); -/* - inflate decompresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may some - introduce some output latency (reading input without producing any output) - except when forced to flush. - - The detailed semantics are as follows. inflate performs one or both of the - following actions: - - - Decompress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in is updated and processing - will resume at this point for the next call of inflate(). - - - Provide more output starting at next_out and update next_out and avail_out - accordingly. inflate() provides as much output as possible, until there - is no more input data or no more space in the output buffer (see below - about the flush parameter). - - Before the call of inflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming - more output, and updating the next_* and avail_* values accordingly. - The application can consume the uncompressed output when it wants, for - example when the output buffer is full (avail_out == 0), or after each - call of inflate(). If inflate returns Z_OK and with zero avail_out, it - must be called again after making room in the output buffer because there - might be more output pending. - - If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much - output as possible to the output buffer. The flushing behavior of inflate is - not specified for values of the flush parameter other than Z_SYNC_FLUSH - and Z_FINISH, but the current implementation actually flushes as much output - as possible anyway. - - inflate() should normally be called until it returns Z_STREAM_END or an - error. However if all decompression is to be performed in a single step - (a single call of inflate), the parameter flush should be set to - Z_FINISH. In this case all pending input is processed and all pending - output is flushed; avail_out must be large enough to hold all the - uncompressed data. (The size of the uncompressed data may have been saved - by the compressor for this purpose.) The next operation on this stream must - be inflateEnd to deallocate the decompression state. The use of Z_FINISH - is never required, but can be used to inform inflate that a faster routine - may be used for the single inflate() call. - - If a preset dictionary is needed at this point (see inflateSetDictionary - below), inflate sets strm-adler to the adler32 checksum of the - dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise - it sets strm->adler to the adler32 checksum of all output produced - so (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or - an error code as described below. At the end of the stream, inflate() - checks that its computed adler32 checksum is equal to that saved by the - compressor and returns Z_STREAM_END only if the checksum is correct. - - inflate() returns Z_OK if some progress has been made (more input processed - or more output produced), Z_STREAM_END if the end of the compressed data has - been reached and all uncompressed output has been produced, Z_NEED_DICT if a - preset dictionary is needed at this point, Z_DATA_ERROR if the input data was - corrupted (input stream not conforming to the zlib format or incorrect - adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent - (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if no progress is possible or if there was not - enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR - case, the application may then call inflateSync to look for a good - compression block. -*/ - - -static int inflateEnd OF((z_streamp strm)); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. - - inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state - was inconsistent. In the error case, msg may be set but then points to a - static string (which must not be deallocated). -*/ - - /* Advanced functions */ - -/* - The following functions are needed only in some special applications. -*/ - -/* -int deflateInit2 OF((z_streamp strm, - int level, - int method, - int windowBits, - int memLevel, - int strategy)); - - This is another version of deflateInit with more compression options. The - fields next_in, zalloc, zfree and opaque must be initialized before by - the caller. - - The method parameter is the compression method. It must be Z_DEFLATED in - this version of the library. - - The windowBits parameter is the base two logarithm of the window size - (the size of the history buffer). It should be in the range 8..15 for this - version of the library. Larger values of this parameter result in better - compression at the expense of memory usage. The default value is 15 if - deflateInit is used instead. - - The memLevel parameter specifies how much memory should be allocated - for the internal compression state. memLevel=1 uses minimum memory but - is slow and reduces compression ratio; memLevel=9 uses maximum memory - for optimal speed. The default value is 8. See zconf.h for total memory - usage as a function of windowBits and memLevel. - - The strategy parameter is used to tune the compression algorithm. Use the - value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a - filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no - string match). Filtered data consists mostly of small values with a - somewhat random distribution. In this case, the compression algorithm is - tuned to compress them better. The effect of Z_FILTERED is to force more - Huffman coding and less string matching; it is somewhat intermediate - between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects - the compression ratio but not the correctness of the compressed output even - if it is not set appropriately. - - deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid - method). msg is set to null if there is no error message. deflateInit2 does - not perform any compression: this will be done by deflate(). -*/ - -/* -static int deflateSetDictionary OF((z_streamp strm, - const Byte *dictionary, - uInt dictLength)); -*/ -/* - Initializes the compression dictionary from the given byte sequence - without producing any compressed output. This function must be called - immediately after deflateInit, deflateInit2 or deflateReset, before any - call of deflate. The compressor and decompressor must use exactly the same - dictionary (see inflateSetDictionary). - - The dictionary should consist of strings (byte sequences) that are likely - to be encountered later in the data to be compressed, with the most commonly - used strings preferably put towards the end of the dictionary. Using a - dictionary is most useful when the data to be compressed is short and can be - predicted with good accuracy; the data can then be compressed better than - with the default empty dictionary. - - Depending on the size of the compression data structures selected by - deflateInit or deflateInit2, a part of the dictionary may in effect be - discarded, for example if the dictionary is larger than the window size in - deflate or deflate2. Thus the strings most likely to be useful should be - put at the end of the dictionary, not at the front. - - Upon return of this function, strm->adler is set to the Adler32 value - of the dictionary; the decompressor may later use this value to determine - which dictionary has been used by the compressor. (The Adler32 value - applies to the whole dictionary even if only a subset of the dictionary is - actually used by the compressor.) - - deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is - inconsistent (for example if deflate has already been called for this stream - or if the compression method is bsort). deflateSetDictionary does not - perform any compression: this will be done by deflate(). -*/ - -/* -static int deflateCopy OF((z_streamp dest, - z_streamp source)); -*/ -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when several compression strategies will be - tried, for example when there are several ways of pre-processing the input - data with a filter. The streams that will be discarded should then be freed - by calling deflateEnd. Note that deflateCopy duplicates the internal - compression state which can be quite large, so this strategy is slow and - can consume lots of memory. - - deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and - destination. -*/ - -// static int deflateReset OF((z_streamp strm)); -/* - This function is equivalent to deflateEnd followed by deflateInit, - but does not free and reallocate all the internal compression state. - The stream will keep the same compression level and any other attributes - that may have been set by deflateInit2. - - deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). -*/ - -/* -static int deflateParams OF((z_streamp strm, - int level, - int strategy)); -*/ -/* - Dynamically update the compression level and compression strategy. The - interpretation of level and strategy is as in deflateInit2. This can be - used to switch between compression and straight copy of the input data, or - to switch to a different kind of input data requiring a different - strategy. If the compression level is changed, the input available so far - is compressed with the old level (and may be flushed); the new level will - take effect only at the next call of deflate(). - - Before the call of deflateParams, the stream state must be set as for - a call of deflate(), since the currently available input may have to - be compressed and flushed. In particular, strm->avail_out must be non-zero. - - deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source - stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR - if strm->avail_out was zero. -*/ - -/* -int inflateInit2 OF((z_streamp strm, - int windowBits)); - - This is another version of inflateInit with an extra parameter. The - fields next_in, avail_in, zalloc, zfree and opaque must be initialized - before by the caller. - - The windowBits parameter is the base two logarithm of the maximum window - size (the size of the history buffer). It should be in the range 8..15 for - this version of the library. The default value is 15 if inflateInit is used - instead. If a compressed stream with a larger window size is given as - input, inflate() will return with the error code Z_DATA_ERROR instead of - trying to allocate a larger window. - - inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative - memLevel). msg is set to null if there is no error message. inflateInit2 - does not perform any decompression apart from reading the zlib header if - present: this will be done by inflate(). (So next_in and avail_in may be - modified, but next_out and avail_out are unchanged.) -*/ - -/* -static int inflateSetDictionary OF((z_streamp strm, - const Byte *dictionary, - uInt dictLength)); -*/ -/* - Initializes the decompression dictionary from the given uncompressed byte - sequence. This function must be called immediately after a call of inflate - if this call returned Z_NEED_DICT. The dictionary chosen by the compressor - can be determined from the Adler32 value returned by this call of - inflate. The compressor and decompressor must use exactly the same - dictionary (see deflateSetDictionary). - - inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is - inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect Adler32 value). inflateSetDictionary does not - perform any decompression: this will be done by subsequent calls of - inflate(). -*/ - -// static int inflateSync OF((z_streamp strm)); -/* - Skips invalid compressed data until a full flush point (see above the - description of deflate with Z_FULL_FLUSH) can be found, or until all - available input is skipped. No output is provided. - - inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR - if no more input was provided, Z_DATA_ERROR if no flush point has been found, - or Z_STREAM_ERROR if the stream structure was inconsistent. In the success - case, the application may save the current current value of total_in which - indicates where valid compressed data was found. In the error case, the - application may repeatedly call inflateSync, providing more input each time, - until success or end of the input data. -*/ - -static int inflateReset OF((z_streamp strm)); -/* - This function is equivalent to inflateEnd followed by inflateInit, - but does not free and reallocate all the internal decompression state. - The stream will keep attributes that may have been set by inflateInit2. - - inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). -*/ - - - /* utility functions */ - -/* - The following utility functions are implemented on top of the - basic stream-oriented functions. To simplify the interface, some - default options are assumed (compression level and memory usage, - standard memory allocation functions). The source code of these - utility functions can easily be modified if you need special options. -*/ - -/* -static int compress OF((Byte *dest, uLong *destLen, - const Byte *source, uLong sourceLen)); -*/ -/* - Compresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be at least 0.1% larger than - sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the - compressed buffer. - This function can be used to compress a whole file at once if the - input file is mmap'ed. - compress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer. -*/ - -/* -static int compress2 OF((Byte *dest, uLong *destLen, - const Byte *source, uLong sourceLen, - int level)); -*/ -/* - Compresses the source buffer into the destination buffer. The level - parameter has the same meaning as in deflateInit. sourceLen is the byte - length of the source buffer. Upon entry, destLen is the total size of the - destination buffer, which must be at least 0.1% larger than sourceLen plus - 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. - - compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_BUF_ERROR if there was not enough room in the output buffer, - Z_STREAM_ERROR if the level parameter is invalid. -*/ - -/* -static int uncompress OF((Byte *dest, uLong *destLen, - const Byte *source, uLong sourceLen)); -*/ -/* - Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be large enough to hold the - entire uncompressed data. (The size of the uncompressed data must have - been saved previously by the compressor and transmitted to the decompressor - by some mechanism outside the scope of this compression library.) - Upon exit, destLen is the actual size of the compressed buffer. - This function can be used to decompress a whole file at once if the - input file is mmap'ed. - - uncompress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted. -*/ - - -typedef voidp gzFile; - -gzFile gzopen OF((const char *path, const char *mode)); -/* - Opens a gzip (.gz) file for reading or writing. The mode parameter - is as in fopen ("rb" or "wb") but can also include a compression level - ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for - Huffman only compression as in "wb1h". (See the description - of deflateInit2 for more information about the strategy parameter.) - - gzopen can be used to read a file which is not in gzip format; in this - case gzread will directly read from the file without decompression. - - gzopen returns NULL if the file could not be opened or if there was - insufficient memory to allocate the (de)compression state; errno - can be checked to distinguish the two cases (if errno is zero, the - zlib error is Z_MEM_ERROR). */ - -gzFile gzdopen OF((int fd, const char *mode)); -/* - gzdopen() associates a gzFile with the file descriptor fd. File - descriptors are obtained from calls like open, dup, creat, pipe or - fileno (in the file has been previously opened with fopen). - The mode parameter is as in gzopen. - The next call of gzclose on the returned gzFile will also close the - file descriptor fd, just like fclose(fdopen(fd), mode) closes the file - descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). - gzdopen returns NULL if there was insufficient memory to allocate - the (de)compression state. -*/ - -int gzsetparams OF((gzFile file, int level, int strategy)); -/* - Dynamically update the compression level or strategy. See the description - of deflateInit2 for the meaning of these parameters. - gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not - opened for writing. -*/ - -int gzread OF((gzFile file, voidp buf, unsigned len)); -/* - Reads the given number of uncompressed bytes from the compressed file. - If the input file was not in gzip format, gzread copies the given number - of bytes into the buffer. - gzread returns the number of uncompressed bytes actually read (0 for - end of file, -1 for error). */ - -int gzwrite OF((gzFile file, - const voidp buf, unsigned len)); -/* - Writes the given number of uncompressed bytes into the compressed file. - gzwrite returns the number of uncompressed bytes actually written - (0 in case of error). -*/ - -int QDECL gzprintf OF((gzFile file, const char *format, ...)); -/* - Converts, formats, and writes the args to the compressed file under - control of the format string, as in fprintf. gzprintf returns the number of - uncompressed bytes actually written (0 in case of error). -*/ - -int gzputs OF((gzFile file, const char *s)); -/* - Writes the given null-terminated string to the compressed file, excluding - the terminating null character. - gzputs returns the number of characters written, or -1 in case of error. -*/ - -char * gzgets OF((gzFile file, char *buf, int len)); -/* - Reads bytes from the compressed file until len-1 characters are read, or - a newline character is read and transferred to buf, or an end-of-file - condition is encountered. The string is then terminated with a null - character. - gzgets returns buf, or Z_NULL in case of error. -*/ - -int gzputc OF((gzFile file, int c)); -/* - Writes c, converted to an unsigned char, into the compressed file. - gzputc returns the value that was written, or -1 in case of error. -*/ - -int gzgetc OF((gzFile file)); -/* - Reads one byte from the compressed file. gzgetc returns this byte - or -1 in case of end of file or error. -*/ - -int gzflush OF((gzFile file, int flush)); -/* - Flushes all pending output into the compressed file. The parameter - flush is as in the deflate() function. The return value is the zlib - error number (see function gzerror below). gzflush returns Z_OK if - the flush parameter is Z_FINISH and all output could be flushed. - gzflush should be called only when strictly necessary because it can - degrade compression. -*/ - -long gzseek OF((gzFile file, - long offset, int whence)); -/* - Sets the starting position for the next gzread or gzwrite on the - given compressed file. The offset represents a number of bytes in the - uncompressed data stream. The whence parameter is defined as in lseek(2); - the value SEEK_END is not supported. - If the file is opened for reading, this function is emulated but can be - extremely slow. If the file is opened for writing, only forward seeks are - supported; gzseek then compresses a sequence of zeroes up to the new - starting position. - - gzseek returns the resulting offset location as measured in bytes from - the beginning of the uncompressed stream, or -1 in case of error, in - particular if the file is opened for writing and the new starting position - would be before the current position. -*/ - -int gzrewind OF((gzFile file)); -/* - Rewinds the given file. This function is supported only for reading. - - gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) -*/ - -long gztell OF((gzFile file)); -/* - Returns the starting position for the next gzread or gzwrite on the - given compressed file. This position represents a number of bytes in the - uncompressed data stream. - - gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) -*/ - -int gzeof OF((gzFile file)); -/* - Returns 1 when EOF has previously been detected reading the given - input stream, otherwise zero. -*/ - -int gzclose OF((gzFile file)); -/* - Flushes all pending output if necessary, closes the compressed file - and deallocates all the (de)compression state. The return value is the zlib - error number (see function gzerror below). -*/ - -// static const char * gzerror OF((gzFile file, int *errnum)); -/* - Returns the error message for the last error which occurred on the - given compressed file. errnum is set to zlib error number. If an - error occurred in the file system and not in the compression library, - errnum is set to Z_ERRNO and the application may consult errno - to get the exact error code. -*/ - - /* checksum functions */ - -/* - These functions are not related to compression but are exported - anyway because they might be useful in applications using the - compression library. -*/ - -static uLong adler32 OF((uLong adler, const Byte *buf, uInt len)); - -/* - Update a running Adler-32 checksum with the bytes buf[0..len-1] and - return the updated checksum. If buf is NULL, this function returns - the required initial value for the checksum. - An Adler-32 checksum is almost as reliable as a CRC32 but can be computed - much faster. Usage example: - - uLong adler = adler32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) { - adler = adler32(adler, buffer, length); - } - if (adler != original_adler) error(); -*/ - - /* various hacks, don't look :) */ - -/* deflateInit and inflateInit are macros to allow checking the zlib version - * and the compiler's view of z_stream: - */ -/* -static int deflateInit_ OF((z_streamp strm, int level, - const char *version, int stream_size)); -static int inflateInit_ OF((z_streamp strm, - const char *version, int stream_size)); -static int deflateInit2_ OF((z_streamp strm, int level, int method, - int windowBits, int memLevel, - int strategy, const char *version, - int stream_size)); -*/ -static int inflateInit2_ OF((z_streamp strm, int windowBits, - const char *version, int stream_size)); - -#define deflateInit(strm, level) \ - deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) -#define inflateInit(strm) \ - inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) -#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ - deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, sizeof(z_stream)) -#define inflateInit2(strm, windowBits) \ - inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) - - -// static const char * zError OF((int err)); -// static int inflateSyncPoint OF((z_streamp z)); -// static const uLong * get_crc_table OF((void)); - -typedef unsigned char uch; -typedef unsigned short ush; -typedef unsigned long ulg; - -// static const char *z_errmsg[10]; /* indexed by 2-zlib_error */ -/* (size given to avoid silly warnings with Visual C++) */ - -#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] - -#define ERR_RETURN(strm,err) \ - return (strm->msg = (char*)ERR_MSG(err), (err)) -/* To be used only when the state is known to be valid */ - - /* common constants */ - -#ifndef DEF_WBITS -# define DEF_WBITS MAX_WBITS -#endif -/* default windowBits for decompression. MAX_WBITS is for compression only */ - -#if MAX_MEM_LEVEL >= 8 -# define DEF_MEM_LEVEL 8 -#else -# define DEF_MEM_LEVEL MAX_MEM_LEVEL -#endif -/* default memLevel */ - -#define STORED_BLOCK 0 -#define STATIC_TREES 1 -#define DYN_TREES 2 -/* The three kinds of block type */ - -#define MIN_MATCH 3 -#define MAX_MATCH 258 -/* The minimum and maximum match lengths */ - -#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ - - /* target dependencies */ - - /* Common defaults */ - -#ifndef OS_CODE -# define OS_CODE 0x03 /* assume Unix */ -#endif - -#ifndef F_OPEN -# define F_OPEN(name, mode) fopen((name), (mode)) -#endif - - /* functions */ - -#ifdef HAVE_STRERROR - extern char *strerror OF((int)); -# define zstrerror(errnum) strerror(errnum) -#else -# define zstrerror(errnum) "" -#endif - -#define zmemcpy Com_Memcpy -#define zmemcmp memcmp -#define zmemzero(dest, len) Com_Memset(dest, 0, len) - -/* Diagnostic functions */ -#ifdef _ZIP_DEBUG_ - int z_verbose = 0; -# define Assert(cond,msg) assert(cond); - //{if(!(cond)) Sys_Error(msg);} -# define Trace(x) {if (z_verbose>=0) Sys_Error x ;} -# define Tracev(x) {if (z_verbose>0) Sys_Error x ;} -# define Tracevv(x) {if (z_verbose>1) Sys_Error x ;} -# define Tracec(c,x) {if (z_verbose>0 && (c)) Sys_Error x ;} -# define Tracecv(c,x) {if (z_verbose>1 && (c)) Sys_Error x ;} -#else -# define Assert(cond,msg) -# define Trace(x) -# define Tracev(x) -# define Tracevv(x) -# define Tracec(c,x) -# define Tracecv(c,x) -#endif - - -typedef uLong (*check_func) OF((uLong check, const Byte *buf, uInt len)); -static voidp zcalloc OF((voidp opaque, unsigned items, unsigned size)); -static void zcfree OF((voidp opaque, voidp ptr)); - -#define ZALLOC(strm, items, size) \ - (*((strm)->zalloc))((strm)->opaque, (items), (size)) -#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidp)(addr)) -#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} - - -#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ - !defined(CASESENSITIVITYDEFAULT_NO) -#define CASESENSITIVITYDEFAULT_NO -#endif - - -#ifndef UNZ_BUFSIZE -#define UNZ_BUFSIZE (65536) -#endif - -#ifndef UNZ_MAXFILENAMEINZIP -#define UNZ_MAXFILENAMEINZIP (256) -#endif - -#ifndef ALLOC -# define ALLOC(size) (Z_Malloc(size)) -#endif -#ifndef TRYFREE -# define TRYFREE(p) {if (p) Z_Free(p);} -#endif - -#define SIZECENTRALDIRITEM (0x2e) -#define SIZEZIPLOCALHEADER (0x1e) - - - -/* =========================================================================== - Read a byte from a gz_stream; update next_in and avail_in. Return EOF - for end of file. - IN assertion: the stream s has been sucessfully opened for reading. -*/ - -/* -static int unzlocal_getByte(FILE *fin,int *pi) -{ - unsigned char c; - int err = fread(&c, 1, 1, fin); - if (err==1) - { - *pi = (int)c; - return UNZ_OK; - } - else - { - if (ferror(fin)) - return UNZ_ERRNO; - else - return UNZ_EOF; - } -} -*/ - -/* =========================================================================== - Reads a long in LSB order from the given gz_stream. Sets -*/ -static int unzlocal_getShort (FILE* fin, uLong *pX) -{ - short v; - - fread( &v, sizeof(v), 1, fin ); - - *pX = LittleShort( v); - return UNZ_OK; - -/* - uLong x ; - int i; - int err; - - err = unzlocal_getByte(fin,&i); - x = (uLong)i; - - if (err==UNZ_OK) - err = unzlocal_getByte(fin,&i); - x += ((uLong)i)<<8; - - if (err==UNZ_OK) - *pX = x; - else - *pX = 0; - return err; -*/ -} - -static int unzlocal_getLong (FILE *fin, uLong *pX) -{ - int v; - - fread( &v, sizeof(v), 1, fin ); - - *pX = LittleLong( v); - return UNZ_OK; - -/* - uLong x ; - int i; - int err; - - err = unzlocal_getByte(fin,&i); - x = (uLong)i; - - if (err==UNZ_OK) - err = unzlocal_getByte(fin,&i); - x += ((uLong)i)<<8; - - if (err==UNZ_OK) - err = unzlocal_getByte(fin,&i); - x += ((uLong)i)<<16; - - if (err==UNZ_OK) - err = unzlocal_getByte(fin,&i); - x += ((uLong)i)<<24; - - if (err==UNZ_OK) - *pX = x; - else - *pX = 0; - return err; -*/ -} - - -/* My own strcmpi / strcasecmp */ -static int strcmpcasenosensitive_internal (const char* fileName1,const char* fileName2) -{ - for (;;) - { - char c1=*(fileName1++); - char c2=*(fileName2++); - if ((c1>='a') && (c1<='z')) - c1 -= 0x20; - if ((c2>='a') && (c2<='z')) - c2 -= 0x20; - if (c1=='\0') - return ((c2=='\0') ? 0 : -1); - if (c2=='\0') - return 1; - if (c1c2) - return 1; - } -} - - -#ifdef CASESENSITIVITYDEFAULT_NO -#define CASESENSITIVITYDEFAULTVALUE 2 -#else -#define CASESENSITIVITYDEFAULTVALUE 1 -#endif - -#ifndef STRCMPCASENOSENTIVEFUNCTION -#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal -#endif - -/* - Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi - or strcasecmp) - If iCaseSenisivity = 0, case sensitivity is defaut of your operating system - (like 1 on Unix, 2 on Windows) - -*/ -extern int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity) -{ - if (iCaseSensitivity==0) - iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; - - if (iCaseSensitivity==1) - return strcmp(fileName1,fileName2); - - return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); -} - -#define BUFREADCOMMENT (0x400) - -/* - Locate the Central directory of a zipfile (at the end, just before - the global comment) -*/ -extern uLong unzlocal_SearchCentralDir(FILE *fin) -{ - unsigned char* buf; - uLong uSizeFile; - uLong uBackRead; - uLong uMaxBack=0xffff; /* maximum size of global comment */ - uLong uPosFound=0; - - if (fseek(fin,0,SEEK_END) != 0) - return 0; - - - uSizeFile = ftell( fin ); - - if (uMaxBack>uSizeFile) - uMaxBack = uSizeFile; - - buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); - if (buf==NULL) - return 0; - - uBackRead = 4; - while (uBackReaduMaxBack) - uBackRead = uMaxBack; - else - uBackRead+=BUFREADCOMMENT; - uReadPos = uSizeFile-uBackRead ; - - uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? - (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); - if (fseek(fin,uReadPos,SEEK_SET)!=0) - break; - - if (fread(buf,(uInt)uReadSize,1,fin)!=1) - break; - - for (i=(int)uReadSize-3; (i--)>0;) - if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && - ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) - { - uPosFound = uReadPos+i; - break; - } - - if (uPosFound!=0) - break; - } - TRYFREE(buf); - return uPosFound; -} - -extern unzFile unzReOpen (const char* path, unzFile file) -{ - unz_s *s; - FILE * fin; - - fin=fopen(path,"rb"); - if (fin==NULL) - return NULL; - - s=(unz_s*)ALLOC(sizeof(unz_s)); - Com_Memcpy(s, (unz_s*)file, sizeof(unz_s)); - - s->file = fin; - return (unzFile)s; -} - -/* - Open a Zip file. path contain the full pathname (by example, - on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer - "zlib/zlib109.zip". - If the zipfile cannot be opened (file don't exist or in not valid), the - return value is NULL. - Else, the return value is a unzFile Handle, usable with other function - of this unzip package. -*/ -extern unzFile unzOpen (const char* path) -{ - unz_s us; - unz_s *s; - uLong central_pos,uL; - FILE * fin ; - - uLong number_disk; /* number of the current dist, used for - spaning ZIP, unsupported, always 0*/ - uLong number_disk_with_CD; /* number the the disk with central dir, used - for spaning ZIP, unsupported, always 0*/ - uLong number_entry_CD; /* total number of entries in - the central dir - (same than number_entry on nospan) */ - - int err=UNZ_OK; - - fin=fopen(path,"rb"); - if (fin==NULL) - return NULL; - - central_pos = unzlocal_SearchCentralDir(fin); - if (central_pos==0) - err=UNZ_ERRNO; - - if (fseek(fin,central_pos,SEEK_SET)!=0) - err=UNZ_ERRNO; - - /* the signature, already checked */ - if (unzlocal_getLong(fin,&uL)!=UNZ_OK) - err=UNZ_ERRNO; - - /* number of this disk */ - if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) - err=UNZ_ERRNO; - - /* number of the disk with the start of the central directory */ - if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) - err=UNZ_ERRNO; - - /* total number of entries in the central dir on this disk */ - if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) - err=UNZ_ERRNO; - - /* total number of entries in the central dir */ - if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) - err=UNZ_ERRNO; - - if ((number_entry_CD!=us.gi.number_entry) || - (number_disk_with_CD!=0) || - (number_disk!=0)) - err=UNZ_BADZIPFILE; - - /* size of the central directory */ - if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) - err=UNZ_ERRNO; - - /* offset of start of central directory with respect to the - starting disk number */ - if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) - err=UNZ_ERRNO; - - /* zipfile comment length */ - if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) - err=UNZ_ERRNO; - - if ((central_pospfile_in_zip_read!=NULL) - unzCloseCurrentFile(file); - - fclose(s->file); - TRYFREE(s); - return UNZ_OK; -} - - -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. */ -extern int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) -{ - unz_s* s; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - *pglobal_info=s->gi; - return UNZ_OK; -} - - -/* - Translate date/time from Dos format to tm_unz (readable more easilty) -*/ -static void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) -{ - uLong uDate; - uDate = (uLong)(ulDosDate>>16); - ptm->tm_mday = (uInt)(uDate&0x1f) ; - ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; - ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; - - ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); - ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; - ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; -} - -/* - Get Info about the current file in the zipfile, with internal only info -*/ -static int unzlocal_GetCurrentFileInfoInternal (unzFile file, - unz_file_info *pfile_info, - unz_file_info_internal - *pfile_info_internal, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize) -{ - unz_s* s; - unz_file_info file_info; - unz_file_info_internal file_info_internal; - int err=UNZ_OK; - uLong uMagic; - long lSeek=0; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (fseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) - err=UNZ_ERRNO; - - - /* we check the magic */ - if (err==UNZ_OK) { - if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) - err=UNZ_ERRNO; - else if (uMagic!=0x02014b50) - err=UNZ_BADZIPFILE; - } - if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) - err=UNZ_ERRNO; - - unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); - - if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) - err=UNZ_ERRNO; - - lSeek+=file_info.size_filename; - if ((err==UNZ_OK) && (szFileName!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_filename0) && (fileNameBufferSize>0)) - if (fread(szFileName,(uInt)uSizeRead,1,s->file)!=1) - err=UNZ_ERRNO; - lSeek -= uSizeRead; - } - - - if ((err==UNZ_OK) && (extraField!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) - lSeek=0; - else - err=UNZ_ERRNO; - } - if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) { - if (fread(extraField,(uInt)uSizeRead,1,s->file)!=1) - err=UNZ_ERRNO; - } - lSeek += file_info.size_file_extra - uSizeRead; - } - else - lSeek+=file_info.size_file_extra; - - - if ((err==UNZ_OK) && (szComment!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) - lSeek=0; - else - err=UNZ_ERRNO; - } - if ((file_info.size_file_comment>0) && (commentBufferSize>0)) { - if (fread(szComment,(uInt)uSizeRead,1,s->file)!=1) - err=UNZ_ERRNO; - } - lSeek+=file_info.size_file_comment - uSizeRead; - } - else - lSeek+=file_info.size_file_comment; - - if ((err==UNZ_OK) && (pfile_info!=NULL)) - *pfile_info=file_info; - - if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) - *pfile_info_internal=file_info_internal; - - return err; -} - - - -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. -*/ -extern int unzGetCurrentFileInfo ( unzFile file, unz_file_info *pfile_info, - char *szFileName, uLong fileNameBufferSize, - void *extraField, uLong extraFieldBufferSize, - char *szComment, uLong commentBufferSize) -{ - return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, - szFileName,fileNameBufferSize, - extraField,extraFieldBufferSize, - szComment,commentBufferSize); -} - -/* - Set the current file of the zipfile to the first file. - return UNZ_OK if there is no problem -*/ -extern int unzGoToFirstFile (unzFile file) -{ - int err=UNZ_OK; - unz_s* s; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - s->pos_in_central_dir=s->offset_central_dir; - s->num_file=0; - err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, - &s->cur_file_info_internal, - NULL,0,NULL,0,NULL,0); - s->current_file_ok = (err == UNZ_OK); - return err; -} - - -/* - Set the current file of the zipfile to the next file. - return UNZ_OK if there is no problem - return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. -*/ -extern int unzGoToNextFile (unzFile file) -{ - unz_s* s; - int err; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_END_OF_LIST_OF_FILE; - if (s->num_file+1==s->gi.number_entry) - return UNZ_END_OF_LIST_OF_FILE; - - s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + - s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; - s->num_file++; - err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, - &s->cur_file_info_internal, - NULL,0,NULL,0,NULL,0); - s->current_file_ok = (err == UNZ_OK); - return err; -} - -/* - Get the position of the info of the current file in the zip. - return UNZ_OK if there is no problem -*/ -extern int unzGetCurrentFileInfoPosition (unzFile file, unsigned long *pos ) -{ - unz_s* s; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - - *pos = s->pos_in_central_dir; - return UNZ_OK; -} - -/* - Set the position of the info of the current file in the zip. - return UNZ_OK if there is no problem -*/ -extern int unzSetCurrentFileInfoPosition (unzFile file, unsigned long pos ) -{ - unz_s* s; - int err; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - - s->pos_in_central_dir = pos; - err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, - &s->cur_file_info_internal, - NULL,0,NULL,0,NULL,0); - s->current_file_ok = (err == UNZ_OK); - return UNZ_OK; -} - -/* - Try locate the file szFileName in the zipfile. - For the iCaseSensitivity signification, see unzipStringFileNameCompare - - return value : - UNZ_OK if the file is found. It becomes the current file. - UNZ_END_OF_LIST_OF_FILE if the file is not found -*/ -extern int unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) -{ - unz_s* s; - int err; - - - uLong num_fileSaved; - uLong pos_in_central_dirSaved; - - - if (file==NULL) - return UNZ_PARAMERROR; - - if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) - return UNZ_PARAMERROR; - - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_END_OF_LIST_OF_FILE; - - num_fileSaved = s->num_file; - pos_in_central_dirSaved = s->pos_in_central_dir; - - err = unzGoToFirstFile(file); - - while (err == UNZ_OK) - { - char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; - unzGetCurrentFileInfo(file,NULL, - szCurrentFileName,sizeof(szCurrentFileName)-1, - NULL,0,NULL,0); - if (unzStringFileNameCompare(szCurrentFileName, - szFileName,iCaseSensitivity)==0) - return UNZ_OK; - err = unzGoToNextFile(file); - } - - s->num_file = num_fileSaved ; - s->pos_in_central_dir = pos_in_central_dirSaved ; - return err; -} - - -/* - Read the static header of the current zipfile - Check the coherency of the static header and info in the end of central - directory about this file - store in *piSizeVar the size of extra info in static header - (filename and size of extra field data) -*/ -static int unzlocal_CheckCurrentFileCoherencyHeader (unz_s* s, uInt* piSizeVar, - uLong *poffset_local_extrafield, - uInt *psize_local_extrafield) -{ - uLong uMagic,uData,uFlags; - uLong size_filename; - uLong size_extra_field; - int err=UNZ_OK; - - *piSizeVar = 0; - *poffset_local_extrafield = 0; - *psize_local_extrafield = 0; - - if (fseek(s->file,s->cur_file_info_internal.offset_curfile + - s->byte_before_the_zipfile,SEEK_SET)!=0) - return UNZ_ERRNO; - - - if (err==UNZ_OK) { - if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) - err=UNZ_ERRNO; - else if (uMagic!=0x04034b50) - err=UNZ_BADZIPFILE; - } - if (unzlocal_getShort(s->file,&uData) != UNZ_OK) - err=UNZ_ERRNO; -/* - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) - err=UNZ_BADZIPFILE; -*/ - if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&uData) != UNZ_OK) - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) - err=UNZ_BADZIPFILE; - - if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && - (s->cur_file_info.compression_method!=Z_DEFLATED)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */ - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - - if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) - err=UNZ_BADZIPFILE; - - *piSizeVar += (uInt)size_filename; - - if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) - err=UNZ_ERRNO; - *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + - SIZEZIPLOCALHEADER + size_filename; - *psize_local_extrafield = (uInt)size_extra_field; - - *piSizeVar += (uInt)size_extra_field; - - return err; -} - -/* - Open for reading data the current file in the zipfile. - If there is no error and the file is opened, the return value is UNZ_OK. -*/ -extern int unzOpenCurrentFile (unzFile file) -{ - int err=UNZ_OK; - int Store; - uInt iSizeVar; - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - uLong offset_local_extrafield; /* offset of the static extra field */ - uInt size_local_extrafield; /* size of the static extra field */ - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_PARAMERROR; - - if (s->pfile_in_zip_read != NULL) - unzCloseCurrentFile(file); - - if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, - &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) - return UNZ_BADZIPFILE; - - pfile_in_zip_read_info = (file_in_zip_read_info_s*) - ALLOC(sizeof(file_in_zip_read_info_s)); - if (pfile_in_zip_read_info==NULL) - return UNZ_INTERNALERROR; - - pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); - pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; - pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; - pfile_in_zip_read_info->pos_local_extrafield=0; - - if (pfile_in_zip_read_info->read_buffer==NULL) - { - TRYFREE(pfile_in_zip_read_info); - return UNZ_INTERNALERROR; - } - - pfile_in_zip_read_info->stream_initialised=0; - - if ((s->cur_file_info.compression_method!=0) && - (s->cur_file_info.compression_method!=Z_DEFLATED)) - err=UNZ_BADZIPFILE; - Store = s->cur_file_info.compression_method==0; - - pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; - pfile_in_zip_read_info->crc32=0; - pfile_in_zip_read_info->compression_method = - s->cur_file_info.compression_method; - pfile_in_zip_read_info->file=s->file; - pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; - - pfile_in_zip_read_info->stream.total_out = 0; - - if (!Store) - { - pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; - pfile_in_zip_read_info->stream.zfree = (free_func)0; - pfile_in_zip_read_info->stream.opaque = (voidp)0; - - err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); - if (err == Z_OK) - pfile_in_zip_read_info->stream_initialised=1; - /* windowBits is passed < 0 to tell that there is no zlib header. - * Note that in this case inflate *requires* an extra "dummy" byte - * after the compressed stream in order to complete decompression and - * return Z_STREAM_END. - * In unzip, i don't wait absolutely Z_STREAM_END because I known the - * size of both compressed and uncompressed data - */ - } - pfile_in_zip_read_info->rest_read_compressed = - s->cur_file_info.compressed_size ; - pfile_in_zip_read_info->rest_read_uncompressed = - s->cur_file_info.uncompressed_size ; - - - pfile_in_zip_read_info->pos_in_zipfile = - s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + - iSizeVar; - - pfile_in_zip_read_info->stream.avail_in = (uInt)0; - - - s->pfile_in_zip_read = pfile_in_zip_read_info; - return UNZ_OK; -} - - -/* - Read bytes from the current file. - buf contain buffer where data must be copied - len the size of buf. - - return the number of byte copied if somes bytes are copied - return 0 if the end of file was reached - return <0 with error code if there is an error - (UNZ_ERRNO for IO error, or zLib error for uncompress error) -*/ -extern int unzReadCurrentFile (unzFile file, void *buf, unsigned len) -{ - int err=UNZ_OK; - uInt iRead = 0; - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - - if ((pfile_in_zip_read_info->read_buffer == NULL)) - return UNZ_END_OF_LIST_OF_FILE; - if (len==0) - return 0; - - pfile_in_zip_read_info->stream.next_out = (Byte*)buf; - - pfile_in_zip_read_info->stream.avail_out = (uInt)len; - - if (len>pfile_in_zip_read_info->rest_read_uncompressed) - pfile_in_zip_read_info->stream.avail_out = - (uInt)pfile_in_zip_read_info->rest_read_uncompressed; - - while (pfile_in_zip_read_info->stream.avail_out>0) - { - if ((pfile_in_zip_read_info->stream.avail_in==0) && - (pfile_in_zip_read_info->rest_read_compressed>0)) - { - uInt uReadThis = UNZ_BUFSIZE; - if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; - if (uReadThis == 0) - return UNZ_EOF; - if (s->cur_file_info.compressed_size == pfile_in_zip_read_info->rest_read_compressed) - if (fseek(pfile_in_zip_read_info->file, - pfile_in_zip_read_info->pos_in_zipfile + - pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) - return UNZ_ERRNO; - if (fread(pfile_in_zip_read_info->read_buffer,uReadThis,1, - pfile_in_zip_read_info->file)!=1) - return UNZ_ERRNO; - pfile_in_zip_read_info->pos_in_zipfile += uReadThis; - - pfile_in_zip_read_info->rest_read_compressed-=uReadThis; - - pfile_in_zip_read_info->stream.next_in = - (Byte*)pfile_in_zip_read_info->read_buffer; - pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; - } - - if (pfile_in_zip_read_info->compression_method==0) - { - uInt uDoCopy,i ; - if (pfile_in_zip_read_info->stream.avail_out < - pfile_in_zip_read_info->stream.avail_in) - uDoCopy = pfile_in_zip_read_info->stream.avail_out ; - else - uDoCopy = pfile_in_zip_read_info->stream.avail_in ; - - for (i=0;istream.next_out+i) = - *(pfile_in_zip_read_info->stream.next_in+i); - -// pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, -// pfile_in_zip_read_info->stream.next_out, -// uDoCopy); - pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; - pfile_in_zip_read_info->stream.avail_in -= uDoCopy; - pfile_in_zip_read_info->stream.avail_out -= uDoCopy; - pfile_in_zip_read_info->stream.next_out += uDoCopy; - pfile_in_zip_read_info->stream.next_in += uDoCopy; - pfile_in_zip_read_info->stream.total_out += uDoCopy; - iRead += uDoCopy; - } - else - { - uLong uTotalOutBefore,uTotalOutAfter; - const Byte *bufBefore; - uLong uOutThis; - int flush=Z_SYNC_FLUSH; - - uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; - bufBefore = pfile_in_zip_read_info->stream.next_out; - - /* - if ((pfile_in_zip_read_info->rest_read_uncompressed == - pfile_in_zip_read_info->stream.avail_out) && - (pfile_in_zip_read_info->rest_read_compressed == 0)) - flush = Z_FINISH; - */ - err=inflate(&pfile_in_zip_read_info->stream,flush); - - uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; - uOutThis = uTotalOutAfter-uTotalOutBefore; - -// pfile_in_zip_read_info->crc32 = -// crc32(pfile_in_zip_read_info->crc32,bufBefore, -// (uInt)(uOutThis)); - - pfile_in_zip_read_info->rest_read_uncompressed -= - uOutThis; - - iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); - - if (err==Z_STREAM_END) - return (iRead==0) ? UNZ_EOF : iRead; - if (err!=Z_OK) - break; - } - } - - if (err==Z_OK) - return iRead; - return err; -} - - -/* - Give the current position in uncompressed data -*/ -extern long unztell (unzFile file) -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - return (long)pfile_in_zip_read_info->stream.total_out; -} - - -/* - return 1 if the end of file was reached, 0 elsewhere -*/ -extern int unzeof (unzFile file) -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - if (pfile_in_zip_read_info->rest_read_uncompressed == 0) - return 1; - else - return 0; -} - - - -/* - Read extra field from the current file (opened by unzOpenCurrentFile) - This is the static-header version of the extra field (sometimes, there is - more info in the static-header version than in the central-header) - - if buf==NULL, it return the size of the static extra field that can be read - - if buf!=NULL, len is the size of the buffer, the extra header is copied in - buf. - the return value is the number of bytes copied in buf, or (if <0) - the error code -*/ -extern int unzGetLocalExtrafield (unzFile file,void *buf,unsigned len) -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - uInt read_now; - uLong size_to_read; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - size_to_read = (pfile_in_zip_read_info->size_local_extrafield - - pfile_in_zip_read_info->pos_local_extrafield); - - if (buf==NULL) - return (int)size_to_read; - - if (len>size_to_read) - read_now = (uInt)size_to_read; - else - read_now = (uInt)len ; - - if (read_now==0) - return 0; - - if (fseek(pfile_in_zip_read_info->file, - pfile_in_zip_read_info->offset_local_extrafield + - pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) - return UNZ_ERRNO; - - if (fread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) - return UNZ_ERRNO; - - return (int)read_now; -} - -/* - Close the file in zip opened with unzipOpenCurrentFile - Return UNZ_CRCERROR if all the file was read but the CRC is not good -*/ -extern int unzCloseCurrentFile (unzFile file) -{ - int err=UNZ_OK; - - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - -/* - if (pfile_in_zip_read_info->rest_read_uncompressed == 0) - { - if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) - err=UNZ_CRCERROR; - } -*/ - - TRYFREE(pfile_in_zip_read_info->read_buffer); - pfile_in_zip_read_info->read_buffer = NULL; - if (pfile_in_zip_read_info->stream_initialised) - inflateEnd(&pfile_in_zip_read_info->stream); - - pfile_in_zip_read_info->stream_initialised = 0; - TRYFREE(pfile_in_zip_read_info); - - s->pfile_in_zip_read=NULL; - - return err; -} - - -/* - Get the global comment string of the ZipFile, in the szComment buffer. - uSizeBuf is the size of the szComment buffer. - return the number of byte copied or an error code <0 -*/ -extern int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) -{ - unz_s* s; - uLong uReadThis ; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - - uReadThis = uSizeBuf; - if (uReadThis>s->gi.size_comment) - uReadThis = s->gi.size_comment; - - if (fseek(s->file,s->central_pos+22,SEEK_SET)!=0) - return UNZ_ERRNO; - - if (uReadThis>0) - { - *szComment='\0'; - if (fread(szComment,(uInt)uReadThis,1,s->file)!=1) - return UNZ_ERRNO; - } - - if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) - *(szComment+s->gi.size_comment)='\0'; - return (int)uReadThis; -} - -/* infblock.h -- header to use infblock.c - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -struct inflate_blocks_state; -typedef struct inflate_blocks_state inflate_blocks_statef; - -static inflate_blocks_statef * inflate_blocks_new OF(( - z_streamp z, - check_func c, /* check function */ - uInt w)); /* window size */ - -static int inflate_blocks OF(( - inflate_blocks_statef *, - z_streamp , - int)); /* initial return code */ - -static void inflate_blocks_reset OF(( - inflate_blocks_statef *, - z_streamp , - uLong *)); /* check value on output */ - -static int inflate_blocks_free OF(( - inflate_blocks_statef *, - z_streamp)); - -#if 0 -static void inflate_set_dictionary OF(( - inflate_blocks_statef *s, - const Byte *d, /* dictionary */ - uInt n)); /* dictionary length */ - -static int inflate_blocks_sync_point OF(( - inflate_blocks_statef *s)); -#endif - -/* simplify the use of the inflate_huft type with some defines */ -#define exop word.what.Exop -#define bits word.what.Bits - -/* Table for deflate from PKZIP's appnote.txt. */ -static const uInt border[] = { /* Order of the bit length code lengths */ - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - -/* inftrees.h -- header to use inftrees.c - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -/* Huffman code lookup table entry--this entry is four bytes for machines - that have 16-bit pointers (e.g. PC's in the small or medium model). */ - -typedef struct inflate_huft_s inflate_huft; - -struct inflate_huft_s { - union { - struct { - Byte Exop; /* number of extra bits or operation */ - Byte Bits; /* number of bits in this code or subcode */ - } what; - uInt pad; /* pad structure to a power of 2 (4 bytes for */ - } word; /* 16-bit, 8 bytes for 32-bit int's) */ - uInt base; /* literal, length base, distance base, - or table offset */ -}; - -/* Maximum size of dynamic tree. The maximum found in a long but non- - exhaustive search was 1004 huft structures (850 for length/literals - and 154 for distances, the latter actually the result of an - exhaustive search). The actual maximum is not known, but the - value below is more than safe. */ -#define MANY 1440 - -static int inflate_trees_bits OF(( - uInt *, /* 19 code lengths */ - uInt *, /* bits tree desired/actual depth */ - inflate_huft * *, /* bits tree result */ - inflate_huft *, /* space for trees */ - z_streamp)); /* for messages */ - -static int inflate_trees_dynamic OF(( - uInt, /* number of literal/length codes */ - uInt, /* number of distance codes */ - uInt *, /* that many (total) code lengths */ - uInt *, /* literal desired/actual bit depth */ - uInt *, /* distance desired/actual bit depth */ - inflate_huft * *, /* literal/length tree result */ - inflate_huft * *, /* distance tree result */ - inflate_huft *, /* space for trees */ - z_streamp)); /* for messages */ - -static int inflate_trees_fixed OF(( - uInt *, /* literal desired/actual bit depth */ - uInt *, /* distance desired/actual bit depth */ - inflate_huft * *, /* literal/length tree result */ - inflate_huft * *, /* distance tree result */ - z_streamp)); /* for memory allocation */ - - -/* infcodes.h -- header to use infcodes.c - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -struct inflate_codes_state; -typedef struct inflate_codes_state inflate_codes_statef; - -static inflate_codes_statef *inflate_codes_new OF(( - uInt, uInt, - inflate_huft *, inflate_huft *, - z_streamp )); - -static int inflate_codes OF(( - inflate_blocks_statef *, - z_streamp , - int)); - -static void inflate_codes_free OF(( - inflate_codes_statef *, - z_streamp )); - -/* infutil.h -- types and macros common to blocks and codes - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -#ifndef _INFUTIL_H -#define _INFUTIL_H - -typedef enum { - TYPE, /* get type bits (3, including end bit) */ - LENS, /* get lengths for stored */ - STORED, /* processing stored block */ - TABLE, /* get table lengths */ - BTREE, /* get bit lengths tree for a dynamic block */ - DTREE, /* get length, distance trees for a dynamic block */ - CODES, /* processing fixed or dynamic block */ - DRY, /* output remaining window bytes */ - DONE, /* finished last block, done */ - BAD} /* got a data error--stuck here */ -inflate_block_mode; - -/* inflate blocks semi-private state */ -struct inflate_blocks_state { - - /* mode */ - inflate_block_mode mode; /* current inflate_block mode */ - - /* mode dependent information */ - union { - uInt left; /* if STORED, bytes left to copy */ - struct { - uInt table; /* table lengths (14 bits) */ - uInt index; /* index into blens (or border) */ - uInt *blens; /* bit lengths of codes */ - uInt bb; /* bit length tree depth */ - inflate_huft *tb; /* bit length decoding tree */ - } trees; /* if DTREE, decoding info for trees */ - struct { - inflate_codes_statef - *codes; - } decode; /* if CODES, current state */ - } sub; /* submode */ - uInt last; /* true if this block is the last block */ - - /* mode independent information */ - uInt bitk; /* bits in bit buffer */ - uLong bitb; /* bit buffer */ - inflate_huft *hufts; /* single malloc for tree space */ - Byte *window; /* sliding window */ - Byte *end; /* one byte after sliding window */ - Byte *read; /* window read pointer */ - Byte *write; /* window write pointer */ - check_func checkfn; /* check function */ - uLong check; /* check on output */ - -}; - - -/* defines for inflate input/output */ -/* update pointers and return */ -#define UPDBITS {s->bitb=b;s->bitk=k;} -#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} -#define UPDOUT {s->write=q;} -#define UPDATE {UPDBITS UPDIN UPDOUT} -#define LEAVE {UPDATE return inflate_flush(s,z,r);} -/* get bytes and bits */ -#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} -#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} -#define NEXTBYTE (n--,*p++) -#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} -/* output bytes */ -#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) -#define LOADOUT {q=s->write;m=(uInt)WAVAIL;} -#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} -#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} -#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} -#define OUTBYTE(a) {*q++=(Byte)(a);m--;} -/* load static pointers */ -#define LOAD {LOADIN LOADOUT} - -/* masks for lower bits (size given to avoid silly warnings with Visual C++) */ -static uInt inflate_mask[17]; - -/* copy as much as possible from the sliding window to the output area */ -static int inflate_flush OF(( - inflate_blocks_statef *, - z_streamp , - int)); - -#endif - - -/* - Notes beyond the 1.93a appnote.txt: - - 1. Distance pointers never point before the beginning of the output - stream. - 2. Distance pointers can point back across blocks, up to 32k away. - 3. There is an implied maximum of 7 bits for the bit length table and - 15 bits for the actual data. - 4. If only one code exists, then it is encoded using one bit. (Zero - would be more efficient, but perhaps a little confusing.) If two - codes exist, they are coded using one bit each (0 and 1). - 5. There is no way of sending zero distance codes--a dummy must be - sent if there are none. (History: a pre 2.0 version of PKZIP would - store blocks with no distance codes, but this was discovered to be - too harsh a criterion.) Valid only for 1.93a. 2.04c does allow - zero distance codes, which is sent as one code of zero bits in - length. - 6. There are up to 286 literal/length codes. Code 256 represents the - end-of-block. Note however that the static length tree defines - 288 codes just to fill out the Huffman codes. Codes 286 and 287 - cannot be used though, since there is no length base or extra bits - defined for them. Similarily, there are up to 30 distance codes. - However, static trees define 32 codes (all 5 bits) to fill out the - Huffman codes, but the last two had better not show up in the data. - 7. Unzip can check dynamic Huffman blocks for complete code sets. - The exception is that a single code would not be complete (see #4). - 8. The five bits following the block type is really the number of - literal codes sent minus 257. - 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits - (1+6+6). Therefore, to output three times the length, you output - three codes (1+1+1), whereas to output four times the same length, - you only need two codes (1+3). Hmm. - 10. In the tree reconstruction algorithm, Code = Code + Increment - only if BitLength(i) is not zero. (Pretty obvious.) - 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) - 12. Note: length code 284 can represent 227-258, but length code 285 - really is 258. The last length deserves its own, short code - since it gets used a lot in very redundant files. The length - 258 is special since 258 - 3 (the min match length) is 255. - 13. The literal/length and distance code bit lengths are read as a - single stream of lengths. It is possible (and advantageous) for - a repeat code (16, 17, or 18) to go across the boundary between - the two sets of lengths. - */ - - -void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) -{ - if (c != Z_NULL) - *c = s->check; - if (s->mode == BTREE || s->mode == DTREE) - ZFREE(z, s->sub.trees.blens); - if (s->mode == CODES) - inflate_codes_free(s->sub.decode.codes, z); - s->mode = TYPE; - s->bitk = 0; - s->bitb = 0; - s->read = s->write = s->window; - if (s->checkfn != Z_NULL) - z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); - Tracev(("inflate: blocks reset\n")); -} - - -inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) -{ - inflate_blocks_statef *s; - - if ((s = (inflate_blocks_statef *)ZALLOC - (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) - return s; - if ((s->hufts = - (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) - { - ZFREE(z, s); - return Z_NULL; - } - if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) - { - ZFREE(z, s->hufts); - ZFREE(z, s); - return Z_NULL; - } - s->end = s->window + w; - s->checkfn = c; - s->mode = TYPE; - Tracev(("inflate: blocks allocated\n")); - inflate_blocks_reset(s, z, Z_NULL); - return s; -} - - -int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) -{ - uInt t; /* temporary storage */ - uLong b; /* bit buffer */ - uInt k; /* bits in bit buffer */ - Byte *p; /* input data pointer */ - uInt n; /* bytes available there */ - Byte *q; /* output window write pointer */ - uInt m; /* bytes to end of window or read pointer */ - - /* copy input/output information to locals (UPDATE macro restores) */ - LOAD - - /* process input based on current state */ - while (1) switch (s->mode) - { - case TYPE: - NEEDBITS(3) - t = (uInt)b & 7; - s->last = t & 1; - switch (t >> 1) - { - case 0: /* stored */ - Tracev(("inflate: stored block%s\n", - s->last ? " (last)" : "")); - DUMPBITS(3) - t = k & 7; /* go to byte boundary */ - DUMPBITS(t) - s->mode = LENS; /* get length of stored block */ - break; - case 1: /* fixed */ - Tracev(("inflate: fixed codes block%s\n", - s->last ? " (last)" : "")); - { - uInt bl, bd; - inflate_huft *tl, *td; - inflate_trees_fixed(&bl, &bd, &tl, &td, z); - s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); - if (s->sub.decode.codes == Z_NULL) - { - r = Z_MEM_ERROR; - LEAVE - } - } - DUMPBITS(3) - s->mode = CODES; - break; - case 2: /* dynamic */ - Tracev(("inflate: dynamic codes block%s\n", - s->last ? " (last)" : "")); - DUMPBITS(3) - s->mode = TABLE; - break; - case 3: /* illegal */ - DUMPBITS(3) - s->mode = BAD; - z->msg = (char*)"invalid block type"; - r = Z_DATA_ERROR; - LEAVE - } - break; - case LENS: - NEEDBITS(32) - if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) - { - s->mode = BAD; - z->msg = (char*)"invalid stored block lengths"; - r = Z_DATA_ERROR; - LEAVE - } - s->sub.left = (uInt)b & 0xffff; - b = k = 0; /* dump bits */ - Tracev(("inflate: stored length %u\n", s->sub.left)); - s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE); - break; - case STORED: - if (n == 0) - LEAVE - NEEDOUT - t = s->sub.left; - if (t > n) t = n; - if (t > m) t = m; - zmemcpy(q, p, t); - p += t; n -= t; - q += t; m -= t; - if ((s->sub.left -= t) != 0) - break; - Tracev(("inflate: stored end, %lu total out\n", - z->total_out + (q >= s->read ? q - s->read : - (s->end - s->read) + (q - s->window)))); - s->mode = s->last ? DRY : TYPE; - break; - case TABLE: - NEEDBITS(14) - s->sub.trees.table = t = (uInt)b & 0x3fff; -#ifndef PKZIP_BUG_WORKAROUND - if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) - { - s->mode = BAD; - z->msg = (char*)"too many length or distance symbols"; - r = Z_DATA_ERROR; - LEAVE - } -#endif - t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); - if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) - { - r = Z_MEM_ERROR; - LEAVE - } - DUMPBITS(14) - s->sub.trees.index = 0; - Tracev(("inflate: table sizes ok\n")); - s->mode = BTREE; - case BTREE: - while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) - { - NEEDBITS(3) - s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; - DUMPBITS(3) - } - while (s->sub.trees.index < 19) - s->sub.trees.blens[border[s->sub.trees.index++]] = 0; - s->sub.trees.bb = 7; - t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, - &s->sub.trees.tb, s->hufts, z); - if (t != Z_OK) - { - ZFREE(z, s->sub.trees.blens); - r = t; - if (r == Z_DATA_ERROR) - s->mode = BAD; - LEAVE - } - s->sub.trees.index = 0; - Tracev(("inflate: bits tree ok\n")); - s->mode = DTREE; - case DTREE: - while (t = s->sub.trees.table, - s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) - { - inflate_huft *h; - uInt i, j, c; - - t = s->sub.trees.bb; - NEEDBITS(t) - h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); - t = h->bits; - c = h->base; - if (c < 16) - { - DUMPBITS(t) - s->sub.trees.blens[s->sub.trees.index++] = c; - } - else /* c == 16..18 */ - { - i = c == 18 ? 7 : c - 14; - j = c == 18 ? 11 : 3; - NEEDBITS(t + i) - DUMPBITS(t) - j += (uInt)b & inflate_mask[i]; - DUMPBITS(i) - i = s->sub.trees.index; - t = s->sub.trees.table; - if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || - (c == 16 && i < 1)) - { - ZFREE(z, s->sub.trees.blens); - s->mode = BAD; - z->msg = (char*)"invalid bit length repeat"; - r = Z_DATA_ERROR; - LEAVE - } - c = c == 16 ? s->sub.trees.blens[i - 1] : 0; - do { - s->sub.trees.blens[i++] = c; - } while (--j); - s->sub.trees.index = i; - } - } - s->sub.trees.tb = Z_NULL; - { - uInt bl, bd; - inflate_huft *tl, *td; - inflate_codes_statef *c; - - tl = NULL; - td = NULL; - bl = 9; /* must be <= 9 for lookahead assumptions */ - bd = 6; /* must be <= 9 for lookahead assumptions */ - t = s->sub.trees.table; - t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), - s->sub.trees.blens, &bl, &bd, &tl, &td, - s->hufts, z); - ZFREE(z, s->sub.trees.blens); - if (t != Z_OK) - { - if (t == (uInt)Z_DATA_ERROR) - s->mode = BAD; - r = t; - LEAVE - } - Tracev(("inflate: trees ok\n")); - if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) - { - r = Z_MEM_ERROR; - LEAVE - } - s->sub.decode.codes = c; - } - s->mode = CODES; - case CODES: - UPDATE - if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) - return inflate_flush(s, z, r); - r = Z_OK; - inflate_codes_free(s->sub.decode.codes, z); - LOAD - Tracev(("inflate: codes end, %lu total out\n", - z->total_out + (q >= s->read ? q - s->read : - (s->end - s->read) + (q - s->window)))); - if (!s->last) - { - s->mode = TYPE; - break; - } - s->mode = DRY; - case DRY: - FLUSH - if (s->read != s->write) - LEAVE - s->mode = DONE; - case DONE: - r = Z_STREAM_END; - LEAVE - case BAD: - r = Z_DATA_ERROR; - LEAVE - default: - r = Z_STREAM_ERROR; - LEAVE - } -} - - -int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) -{ - inflate_blocks_reset(s, z, Z_NULL); - ZFREE(z, s->window); - ZFREE(z, s->hufts); - ZFREE(z, s); - Tracev(("inflate: blocks freed\n")); - return Z_OK; -} - -#if 0 -void inflate_set_dictionary(inflate_blocks_statef *s, const Byte *d, uInt n) -{ - zmemcpy(s->window, d, n); - s->read = s->write = s->window + n; -} - -/* Returns true if inflate is currently at the end of a block generated - * by Z_SYNC_FLUSH or Z_FULL_FLUSH. - * IN assertion: s != Z_NULL - */ -int inflate_blocks_sync_point(inflate_blocks_statef *s) -{ - return s->mode == LENS; -} -#endif - - -/* And'ing with mask[n] masks the lower n bits */ -static uInt inflate_mask[17] = { - 0x0000, - 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, - 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff -}; - - -/* copy as much as possible from the sliding window to the output area */ -int inflate_flush(inflate_blocks_statef *s, z_streamp z, int r) -{ - uInt n; - Byte *p; - Byte *q; - - /* static copies of source and destination pointers */ - p = z->next_out; - q = s->read; - - /* compute number of bytes to copy as as end of window */ - n = (uInt)((q <= s->write ? s->write : s->end) - q); - if (n > z->avail_out) n = z->avail_out; - if (n && r == Z_BUF_ERROR) r = Z_OK; - - /* update counters */ - z->avail_out -= n; - z->total_out += n; - - /* update check information */ - if (s->checkfn != Z_NULL) - z->adler = s->check = (*s->checkfn)(s->check, q, n); - - /* copy as as end of window */ - zmemcpy(p, q, n); - p += n; - q += n; - - /* see if more to copy at beginning of window */ - if (q == s->end) - { - /* wrap pointers */ - q = s->window; - if (s->write == s->end) - s->write = s->window; - - /* compute bytes to copy */ - n = (uInt)(s->write - q); - if (n > z->avail_out) n = z->avail_out; - if (n && r == Z_BUF_ERROR) r = Z_OK; - - /* update counters */ - z->avail_out -= n; - z->total_out += n; - - /* update check information */ - if (s->checkfn != Z_NULL) - z->adler = s->check = (*s->checkfn)(s->check, q, n); - - /* copy */ - zmemcpy(p, q, n); - p += n; - q += n; - } - - /* update pointers */ - z->next_out = p; - s->read = q; - - /* done */ - return r; -} - -/* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -static const char inflate_copyright[] = - " inflate 1.1.3 Copyright 1995-1998 Mark Adler "; -/* - If you use the zlib library in a product, an acknowledgment is welcome - in the documentation of your product. If for some reason you cannot - include such an acknowledgment, I would appreciate that you keep this - copyright string in the executable of your product. - */ - -/* simplify the use of the inflate_huft type with some defines */ -#define exop word.what.Exop -#define bits word.what.Bits - - -static int huft_build OF(( - uInt *, /* code lengths in bits */ - uInt, /* number of codes */ - uInt, /* number of "simple" codes */ - const uInt *, /* list of base values for non-simple codes */ - const uInt *, /* list of extra bits for non-simple codes */ - inflate_huft **, /* result: starting table */ - uInt *, /* maximum lookup bits (returns actual) */ - inflate_huft *, /* space for trees */ - uInt *, /* hufts used in space */ - uInt * )); /* space for values */ - -/* Tables for deflate from PKZIP's appnote.txt. */ -static const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */ - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; - /* see note #13 above about 258 */ -static const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */ - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, - 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */ -static const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */ - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577}; -static const uInt cpdext[30] = { /* Extra bits for distance codes */ - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, - 12, 12, 13, 13}; - -/* - Huffman code decoding is performed using a multi-level table lookup. - The fastest way to decode is to simply build a lookup table whose - size is determined by the longest code. However, the time it takes - to build this table can also be a factor if the data being decoded - is not very long. The most common codes are necessarily the - shortest codes, so those codes dominate the decoding time, and hence - the speed. The idea is you can have a shorter table that decodes the - shorter, more probable codes, and then point to subsidiary tables for - the longer codes. The time it costs to decode the longer codes is - then traded against the time it takes to make longer tables. - - This results of this trade are in the variables lbits and dbits - below. lbits is the number of bits the first level table for literal/ - length codes can decode in one step, and dbits is the same thing for - the distance codes. Subsequent tables are also less than or equal to - those sizes. These values may be adjusted either when all of the - codes are shorter than that, in which case the longest code length in - bits is used, or when the shortest code is *longer* than the requested - table size, in which case the length of the shortest code in bits is - used. - - There are two different values for the two tables, since they code a - different number of possibilities each. The literal/length table - codes 286 possible values, or in a flat code, a little over eight - bits. The distance table codes 30 possible values, or a little less - than five bits, flat. The optimum values for speed end up being - about one bit more than those, so lbits is 8+1 and dbits is 5+1. - The optimum values may differ though from machine to machine, and - possibly even between compilers. Your mileage may vary. - */ - - -/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ -#define BMAX 15 /* maximum bit length of any code */ - -static int huft_build(uInt *b, uInt n, uInt s, const uInt *d, const uInt *e, inflate_huft ** t, uInt *m, inflate_huft *hp, uInt *hn, uInt *v) -//uInt *b; /* code lengths in bits (all assumed <= BMAX) */ -//uInt n; /* number of codes (assumed <= 288) */ -//uInt s; /* number of simple-valued codes (0..s-1) */ -//const uInt *d; /* list of base values for non-simple codes */ -//const uInt *e; /* list of extra bits for non-simple codes */ -//inflate_huft ** t; /* result: starting table */ -//uInt *m; /* maximum lookup bits, returns actual */ -//inflate_huft *hp; /* space for trees */ -//uInt *hn; /* hufts used in space */ -//uInt *v; /* working area: values in order of bit length */ -/* Given a list of code lengths and a maximum table size, make a set of - tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR - if the given code set is incomplete (the tables are still built in this - case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of - lengths), or Z_MEM_ERROR if not enough memory. */ -{ - - uInt a; /* counter for codes of length k */ - uInt c[BMAX+1]; /* bit length count table */ - uInt f; /* i repeats in table every f entries */ - int g; /* maximum code length */ - int h; /* table level */ - register uInt i; /* counter, current code */ - register uInt j; /* counter */ - register int k; /* number of bits in current code */ - int l; /* bits per table (returned in m) */ - uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */ - register uInt *p; /* pointer into c[], b[], or v[] */ - inflate_huft *q; /* points to current table */ - struct inflate_huft_s r; /* table entry for structure assignment */ - inflate_huft *u[BMAX]; /* table stack */ - register int w; /* bits before this table == (l * h) */ - uInt x[BMAX+1]; /* bit offsets, then code stack */ - uInt *xp; /* pointer into x */ - int y; /* number of dummy codes added */ - uInt z; /* number of entries in current table */ - - - /* Generate counts for each bit length */ - p = c; -#define C0 *p++ = 0; -#define C2 C0 C0 C0 C0 -#define C4 C2 C2 C2 C2 - C4 /* clear c[]--assume BMAX+1 is 16 */ - p = b; i = n; - do { - c[*p++]++; /* assume all entries <= BMAX */ - } while (--i); - if (c[0] == n) /* null input--all zero length codes */ - { - *t = (inflate_huft *)Z_NULL; - *m = 0; - return Z_OK; - } - - - /* Find minimum and maximum length, bound *m by those */ - l = *m; - for (j = 1; j <= BMAX; j++) - if (c[j]) - break; - k = j; /* minimum code length */ - if ((uInt)l < j) - l = j; - for (i = BMAX; i; i--) - if (c[i]) - break; - g = i; /* maximum code length */ - if ((uInt)l > i) - l = i; - *m = l; - - - /* Adjust last length count to fill out codes, if needed */ - for (y = 1 << j; j < i; j++, y <<= 1) - if ((y -= c[j]) < 0) - return Z_DATA_ERROR; - if ((y -= c[i]) < 0) - return Z_DATA_ERROR; - c[i] += y; - - - /* Generate starting offsets into the value table for each length */ - x[1] = j = 0; - p = c + 1; xp = x + 2; - while (--i) { /* note that i == g from above */ - *xp++ = (j += *p++); - } - - - /* Make a table of values in order of bit lengths */ - p = b; i = 0; - do { - if ((j = *p++) != 0) - v[x[j]++] = i; - } while (++i < n); - n = x[g]; /* set n to length of v */ - - - /* Generate the Huffman codes and for each, make the table entries */ - x[0] = i = 0; /* first Huffman code is zero */ - p = v; /* grab values in bit order */ - h = -1; /* no tables yet--level -1 */ - w = -l; /* bits decoded == (l * h) */ - u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ - q = (inflate_huft *)Z_NULL; /* ditto */ - z = 0; /* ditto */ - - /* go through the bit lengths (k already is bits in shortest code) */ - for (; k <= g; k++) - { - a = c[k]; - while (a--) - { - /* here i is the Huffman code of length k bits for value *p */ - /* make tables up to required level */ - while (k > w + l) - { - h++; - w += l; /* previous table always l bits */ - - /* compute minimum size table less than or equal to l bits */ - z = g - w; - z = z > (uInt)l ? l : z; /* table size upper limit */ - if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ - { /* too few codes for k-w bit table */ - f -= a + 1; /* deduct codes from patterns left */ - xp = c + k; - if (j < z) - while (++j < z) /* try smaller tables up to z bits */ - { - if ((f <<= 1) <= *++xp) - break; /* enough codes to use up j bits */ - f -= *xp; /* else deduct codes from patterns */ - } - } - z = 1 << j; /* table entries for j-bit table */ - - /* allocate new table */ - if (*hn + z > MANY) /* (note: doesn't matter for fixed) */ - return Z_MEM_ERROR; /* not enough memory */ - u[h] = q = hp + *hn; - *hn += z; - - /* connect to last table, if there is one */ - if (h) - { - x[h] = i; /* save pattern for backing up */ - r.bits = (Byte)l; /* bits to dump before this table */ - r.exop = (Byte)j; /* bits in this table */ - j = i >> (w - l); - r.base = (uInt)(q - u[h-1] - j); /* offset to this table */ - u[h-1][j] = r; /* connect to last table */ - } - else - *t = q; /* first table is returned result */ - } - - /* set up table entry in r */ - r.bits = (Byte)(k - w); - if (p >= v + n) - r.exop = 128 + 64; /* out of values--invalid code */ - else if (*p < s) - { - r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ - r.base = *p++; /* simple code is just the value */ - } - else - { - r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */ - r.base = d[*p++ - s]; - } - - /* fill code-like entries with r */ - f = 1 << (k - w); - for (j = i >> w; j < z; j += f) - q[j] = r; - - /* backwards increment the k-bit code i */ - for (j = 1 << (k - 1); i & j; j >>= 1) - i ^= j; - i ^= j; - - /* backup over finished tables */ - mask = (1 << w) - 1; /* needed on HP, cc -O bug */ - while ((i & mask) != x[h]) - { - h--; /* don't need to update q */ - w -= l; - mask = (1 << w) - 1; - } - } - } - - - /* Return Z_BUF_ERROR if we were given an incomplete table */ - return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; -} - - -int inflate_trees_bits(uInt *c, uInt *bb, inflate_huft * *tb, inflate_huft *hp, z_streamp z) -//uInt *c; /* 19 code lengths */ -//uInt *bb; /* bits tree desired/actual depth */ -//inflate_huft * *tb; /* bits tree result */ -//inflate_huft *hp; /* space for trees */ -//z_streamp z; /* for messages */ -{ - int r; - uInt hn = 0; /* hufts used in space */ - uInt *v; /* work area for huft_build */ - - if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) - return Z_MEM_ERROR; - r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, - tb, bb, hp, &hn, v); - if (r == Z_DATA_ERROR) - z->msg = (char*)"oversubscribed dynamic bit lengths tree"; - else if (r == Z_BUF_ERROR || *bb == 0) - { - z->msg = (char*)"incomplete dynamic bit lengths tree"; - r = Z_DATA_ERROR; - } - ZFREE(z, v); - return r; -} - - -int inflate_trees_dynamic(uInt nl, uInt nd, uInt *c, uInt *bl, uInt *bd, inflate_huft * *tl, inflate_huft * *td, inflate_huft *hp, z_streamp z) -//uInt nl; /* number of literal/length codes */ -//uInt nd; /* number of distance codes */ -//uInt *c; /* that many (total) code lengths */ -//uInt *bl; /* literal desired/actual bit depth */ -//uInt *bd; /* distance desired/actual bit depth */ -//inflate_huft * *tl; /* literal/length tree result */ -//inflate_huft * *td; /* distance tree result */ -//inflate_huft *hp; /* space for trees */ -//z_streamp z; /* for messages */ -{ - int r; - uInt hn = 0; /* hufts used in space */ - uInt *v; /* work area for huft_build */ - - /* allocate work area */ - if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) - return Z_MEM_ERROR; - - /* build literal/length tree */ - r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); - if (r != Z_OK || *bl == 0) - { - if (r == Z_DATA_ERROR) - z->msg = (char*)"oversubscribed literal/length tree"; - else if (r != Z_MEM_ERROR) - { - z->msg = (char*)"incomplete literal/length tree"; - r = Z_DATA_ERROR; - } - ZFREE(z, v); - return r; - } - - /* build distance tree */ - r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); - if (r != Z_OK || (*bd == 0 && nl > 257)) - { - if (r == Z_DATA_ERROR) - z->msg = (char*)"oversubscribed distance tree"; - else if (r == Z_BUF_ERROR) { -#ifdef PKZIP_BUG_WORKAROUND - r = Z_OK; - } -#else - z->msg = (char*)"incomplete distance tree"; - r = Z_DATA_ERROR; - } - else if (r != Z_MEM_ERROR) - { - z->msg = (char*)"empty distance tree with lengths"; - r = Z_DATA_ERROR; - } - ZFREE(z, v); - return r; -#endif - } - - /* done */ - ZFREE(z, v); - return Z_OK; -} - -/* inffixed.h -- table for decoding fixed codes - * Generated automatically by the maketree.c program - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -static uInt fixed_bl = 9; -static uInt fixed_bd = 5; -static inflate_huft fixed_tl[] = { - {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, - {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, - {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, - {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, - {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, - {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, - {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, - {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, - {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, - {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, - {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, - {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, - {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, - {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, - {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, - {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, - {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, - {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, - {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, - {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, - {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, - {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, - {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, - {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, - {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, - {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, - {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, - {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, - {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, - {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, - {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, - {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, - {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, - {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, - {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, - {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, - {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, - {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, - {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, - {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, - {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, - {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, - {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, - {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, - {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, - {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, - {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, - {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, - {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, - {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, - {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, - {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, - {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, - {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, - {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, - {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, - {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, - {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, - {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, - {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, - {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, - {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, - {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, - {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, - {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, - {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, - {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, - {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, - {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, - {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, - {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, - {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, - {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, - {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, - {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, - {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, - {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, - {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, - {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, - {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, - {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, - {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, - {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, - {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, - {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, - {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, - {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, - {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, - {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, - {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, - {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, - {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, - {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, - {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, - {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, - {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, - {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, - {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, - {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, - {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, - {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, - {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, - {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, - {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, - {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, - {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, - {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, - {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, - {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, - {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, - {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, - {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, - {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, - {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, - {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, - {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, - {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, - {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, - {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, - {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, - {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, - {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, - {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, - {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, - {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, - {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, - {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, - {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} - }; -static inflate_huft fixed_td[] = { - {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, - {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, - {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, - {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, - {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, - {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, - {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, - {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} - }; - -int inflate_trees_fixed(uInt *bl, uInt *bd, inflate_huft * *tl, inflate_huft * *td, z_streamp z) -//uInt *bl; /* literal desired/actual bit depth */ -//uInt *bd; /* distance desired/actual bit depth */ -//inflate_huft * *tl; /* literal/length tree result */ -//inflate_huft * *td; /* distance tree result */ -//z_streamp z; /* for memory allocation */ -{ - *bl = fixed_bl; - *bd = fixed_bd; - *tl = fixed_tl; - *td = fixed_td; - return Z_OK; -} - -/* simplify the use of the inflate_huft type with some defines */ -#define exop word.what.Exop -#define bits word.what.Bits - -/* macros for bit input with no checking and for returning unused bytes */ -#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} - -/* Called with number of bytes left to write in window at least 258 - (the maximum string length) and number of input bytes available - at least ten. The ten bytes are six bytes for the longest length/ - distance pair plus four bytes for overloading the bit buffer. */ - -static int inflate_fast(uInt bl, uInt bd, inflate_huft *tl, inflate_huft *td, inflate_blocks_statef *s, z_streamp z) -{ - inflate_huft *t; /* temporary pointer */ - uInt e; /* extra bits or operation */ - uLong b; /* bit buffer */ - uInt k; /* bits in bit buffer */ - Byte *p; /* input data pointer */ - uInt n; /* bytes available there */ - Byte *q; /* output window write pointer */ - uInt m; /* bytes to end of window or read pointer */ - uInt ml; /* mask for literal/length tree */ - uInt md; /* mask for distance tree */ - uInt c; /* bytes to copy */ - uInt d; /* distance back to copy from */ - Byte *r; /* copy source pointer */ - - /* load input, output, bit values */ - LOAD - - /* initialize masks */ - ml = inflate_mask[bl]; - md = inflate_mask[bd]; - - /* do until not enough input or output space for fast loop */ - do { /* assume called with m >= 258 && n >= 10 */ - /* get literal/length code */ - GRABBITS(20) /* max bits for literal/length code */ - if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) - { - DUMPBITS(t->bits) - Tracevv((t->base >= 0x20 && t->base < 0x7f ? - "inflate: * literal '%c'\n" : - "inflate: * literal 0x%02x\n", t->base)); - *q++ = (Byte)t->base; - m--; - continue; - } - do { - DUMPBITS(t->bits) - if (e & 16) - { - /* get extra bits for length */ - e &= 15; - c = t->base + ((uInt)b & inflate_mask[e]); - DUMPBITS(e) - Tracevv(("inflate: * length %u\n", c)); - - /* decode distance base of block to copy */ - GRABBITS(15); /* max bits for distance code */ - e = (t = td + ((uInt)b & md))->exop; - do { - DUMPBITS(t->bits) - if (e & 16) - { - /* get extra bits to add to distance base */ - e &= 15; - GRABBITS(e) /* get extra bits (up to 13) */ - d = t->base + ((uInt)b & inflate_mask[e]); - DUMPBITS(e) - Tracevv(("inflate: * distance %u\n", d)); - - /* do the copy */ - m -= c; - if ((uInt)(q - s->window) >= d) /* offset before dest */ - { /* just copy */ - r = q - d; - *q++ = *r++; c--; /* minimum count is three, */ - *q++ = *r++; c--; /* so unroll loop a little */ - } - else /* else offset after destination */ - { - e = d - (uInt)(q - s->window); /* bytes from offset to end */ - r = s->end - e; /* pointer to offset */ - if (c > e) /* if source crosses, */ - { - c -= e; /* copy to end of window */ - do { - *q++ = *r++; - } while (--e); - r = s->window; /* copy rest from start of window */ - } - } - do { /* copy all or what's left */ - *q++ = *r++; - } while (--c); - break; - } - else if ((e & 64) == 0) - { - t += t->base; - e = (t += ((uInt)b & inflate_mask[e]))->exop; - } - else - { - z->msg = (char*)"invalid distance code"; - UNGRAB - UPDATE - return Z_DATA_ERROR; - } - } while (1); - break; - } - if ((e & 64) == 0) - { - t += t->base; - if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) - { - DUMPBITS(t->bits) - Tracevv((t->base >= 0x20 && t->base < 0x7f ? - "inflate: * literal '%c'\n" : - "inflate: * literal 0x%02x\n", t->base)); - *q++ = (Byte)t->base; - m--; - break; - } - } - else if (e & 32) - { - Tracevv(("inflate: * end of block\n")); - UNGRAB - UPDATE - return Z_STREAM_END; - } - else - { - z->msg = (char*)"invalid literal/length code"; - UNGRAB - UPDATE - return Z_DATA_ERROR; - } - } while (1); - } while (m >= 258 && n >= 10); - - /* not enough input or output--restore pointers and return */ - UNGRAB - UPDATE - return Z_OK; -} - -/* infcodes.c -- process literals and length/distance pairs - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* simplify the use of the inflate_huft type with some defines */ -#define exop word.what.Exop -#define bits word.what.Bits - -typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ - START, /* x: set up for LEN */ - LEN, /* i: get length/literal/eob next */ - LENEXT, /* i: getting length extra (have base) */ - DIST, /* i: get distance next */ - DISTEXT, /* i: getting distance extra */ - COPY, /* o: copying bytes in window, waiting for space */ - LIT, /* o: got literal, waiting for output space */ - WASH, /* o: got eob, possibly still output waiting */ - END, /* x: got eob and all data flushed */ - BADCODE} /* x: got error */ -inflate_codes_mode; - -/* inflate codes private state */ -struct inflate_codes_state { - - /* mode */ - inflate_codes_mode mode; /* current inflate_codes mode */ - - /* mode dependent information */ - uInt len; - union { - struct { - inflate_huft *tree; /* pointer into tree */ - uInt need; /* bits needed */ - } code; /* if LEN or DIST, where in tree */ - uInt lit; /* if LIT, literal */ - struct { - uInt get; /* bits to get for extra */ - uInt dist; /* distance back to copy from */ - } copy; /* if EXT or COPY, where and how much */ - } sub; /* submode */ - - /* mode independent information */ - Byte lbits; /* ltree bits decoded per branch */ - Byte dbits; /* dtree bits decoder per branch */ - inflate_huft *ltree; /* literal/length/eob tree */ - inflate_huft *dtree; /* distance tree */ - -}; - - -inflate_codes_statef *inflate_codes_new(uInt bl, uInt bd, inflate_huft *tl, inflate_huft *td, z_streamp z) -{ - inflate_codes_statef *c; - - if ((c = (inflate_codes_statef *) - ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) - { - c->mode = START; - c->lbits = (Byte)bl; - c->dbits = (Byte)bd; - c->ltree = tl; - c->dtree = td; - Tracev(("inflate: codes new\n")); - } - return c; -} - - -int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) -{ - uInt j; /* temporary storage */ - inflate_huft *t; /* temporary pointer */ - uInt e; /* extra bits or operation */ - uLong b; /* bit buffer */ - uInt k; /* bits in bit buffer */ - Byte *p; /* input data pointer */ - uInt n; /* bytes available there */ - Byte *q; /* output window write pointer */ - uInt m; /* bytes to end of window or read pointer */ - Byte *f; /* pointer to copy strings from */ - inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ - - /* copy input/output information to locals (UPDATE macro restores) */ - LOAD - - /* process input and output based on current state */ - while (1) switch (c->mode) - { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ - case START: /* x: set up for LEN */ -#ifndef SLOW - if (m >= 258 && n >= 10) - { - UPDATE - r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); - LOAD - if (r != Z_OK) - { - c->mode = r == Z_STREAM_END ? WASH : BADCODE; - break; - } - } -#endif /* !SLOW */ - c->sub.code.need = c->lbits; - c->sub.code.tree = c->ltree; - c->mode = LEN; - case LEN: /* i: get length/literal/eob next */ - j = c->sub.code.need; - NEEDBITS(j) - t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); - DUMPBITS(t->bits) - e = (uInt)(t->exop); - if (e == 0) /* literal */ - { - c->sub.lit = t->base; - Tracevv((t->base >= 0x20 && t->base < 0x7f ? - "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", t->base)); - c->mode = LIT; - break; - } - if (e & 16) /* length */ - { - c->sub.copy.get = e & 15; - c->len = t->base; - c->mode = LENEXT; - break; - } - if ((e & 64) == 0) /* next table */ - { - c->sub.code.need = e; - c->sub.code.tree = t + t->base; - break; - } - if (e & 32) /* end of block */ - { - Tracevv(("inflate: end of block\n")); - c->mode = WASH; - break; - } - c->mode = BADCODE; /* invalid code */ - z->msg = (char*)"invalid literal/length code"; - r = Z_DATA_ERROR; - LEAVE - case LENEXT: /* i: getting length extra (have base) */ - j = c->sub.copy.get; - NEEDBITS(j) - c->len += (uInt)b & inflate_mask[j]; - DUMPBITS(j) - c->sub.code.need = c->dbits; - c->sub.code.tree = c->dtree; - Tracevv(("inflate: length %u\n", c->len)); - c->mode = DIST; - case DIST: /* i: get distance next */ - j = c->sub.code.need; - NEEDBITS(j) - t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); - DUMPBITS(t->bits) - e = (uInt)(t->exop); - if (e & 16) /* distance */ - { - c->sub.copy.get = e & 15; - c->sub.copy.dist = t->base; - c->mode = DISTEXT; - break; - } - if ((e & 64) == 0) /* next table */ - { - c->sub.code.need = e; - c->sub.code.tree = t + t->base; - break; - } - c->mode = BADCODE; /* invalid code */ - z->msg = (char*)"invalid distance code"; - r = Z_DATA_ERROR; - LEAVE - case DISTEXT: /* i: getting distance extra */ - j = c->sub.copy.get; - NEEDBITS(j) - c->sub.copy.dist += (uInt)b & inflate_mask[j]; - DUMPBITS(j) - Tracevv(("inflate: distance %u\n", c->sub.copy.dist)); - c->mode = COPY; - case COPY: /* o: copying bytes in window, waiting for space */ -#ifndef __TURBOC__ /* Turbo C bug for following expression */ - f = (uInt)(q - s->window) < c->sub.copy.dist ? - s->end - (c->sub.copy.dist - (q - s->window)) : - q - c->sub.copy.dist; -#else - f = q - c->sub.copy.dist; - if ((uInt)(q - s->window) < c->sub.copy.dist) - f = s->end - (c->sub.copy.dist - (uInt)(q - s->window)); -#endif - while (c->len) - { - NEEDOUT - OUTBYTE(*f++) - if (f == s->end) - f = s->window; - c->len--; - } - c->mode = START; - break; - case LIT: /* o: got literal, waiting for output space */ - NEEDOUT - OUTBYTE(c->sub.lit) - c->mode = START; - break; - case WASH: /* o: got eob, possibly more output */ - if (k > 7) /* return unused byte, if any */ - { - Assert(k < 16, "inflate_codes grabbed too many bytes") - k -= 8; - n++; - p--; /* can always return one */ - } - FLUSH - if (s->read != s->write) - LEAVE - c->mode = END; - case END: - r = Z_STREAM_END; - LEAVE - case BADCODE: /* x: got error */ - r = Z_DATA_ERROR; - LEAVE - default: - r = Z_STREAM_ERROR; - LEAVE - } -#ifdef NEED_DUMMY_RETURN - return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ -#endif -} - - -void inflate_codes_free(inflate_codes_statef *c, z_streamp z) -{ - ZFREE(z, c); - Tracev(("inflate: codes free\n")); -} - -/* adler32.c -- compute the Adler-32 checksum of a data stream - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#define BASE 65521L /* largest prime smaller than 65536 */ -#define NMAX 5552 -/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ - -#undef DO1 -#undef DO2 -#undef DO4 -#undef DO8 - -#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} -#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); -#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); -#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); -#define DO16(buf) DO8(buf,0); DO8(buf,8); - -/* ========================================================================= */ -static uLong adler32(uLong adler, const Byte *buf, uInt len) -{ - unsigned long s1 = adler & 0xffff; - unsigned long s2 = (adler >> 16) & 0xffff; - int k; - - if (buf == Z_NULL) return 1L; - - while (len > 0) { - k = len < NMAX ? len : NMAX; - len -= k; - while (k >= 16) { - DO16(buf); - buf += 16; - k -= 16; - } - if (k != 0) do { - s1 += *buf++; - s2 += s1; - } while (--k); - s1 %= BASE; - s2 %= BASE; - } - return (s2 << 16) | s1; -} - - -/* infblock.h -- header to use infblock.c - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -static inflate_blocks_statef * inflate_blocks_new OF(( - z_streamp z, - check_func c, /* check function */ - uInt w)); /* window size */ - -static int inflate_blocks OF(( - inflate_blocks_statef *, - z_streamp , - int)); /* initial return code */ - -static void inflate_blocks_reset OF(( - inflate_blocks_statef *, - z_streamp , - uLong *)); /* check value on output */ - -static int inflate_blocks_free OF(( - inflate_blocks_statef *, - z_streamp)); - -#if 0 -static void inflate_set_dictionary OF(( - inflate_blocks_statef *s, - const Byte *d, /* dictionary */ - uInt n)); /* dictionary length */ - -static int inflate_blocks_sync_point OF(( - inflate_blocks_statef *s)); -#endif - -typedef enum { - imMETHOD, /* waiting for method byte */ - imFLAG, /* waiting for flag byte */ - imDICT4, /* four dictionary check bytes to go */ - imDICT3, /* three dictionary check bytes to go */ - imDICT2, /* two dictionary check bytes to go */ - imDICT1, /* one dictionary check byte to go */ - imDICT0, /* waiting for inflateSetDictionary */ - imBLOCKS, /* decompressing blocks */ - imCHECK4, /* four check bytes to go */ - imCHECK3, /* three check bytes to go */ - imCHECK2, /* two check bytes to go */ - imCHECK1, /* one check byte to go */ - imDONE, /* finished check, done */ - imBAD} /* got an error--stay here */ -inflate_mode; - -/* inflate private state */ -struct internal_state { - - /* mode */ - inflate_mode mode; /* current inflate mode */ - - /* mode dependent information */ - union { - uInt method; /* if FLAGS, method byte */ - struct { - uLong was; /* computed check value */ - uLong need; /* stream check value */ - } check; /* if CHECK, check values to compare */ - uInt marker; /* if BAD, inflateSync's marker bytes count */ - } sub; /* submode */ - - /* mode independent information */ - int nowrap; /* flag for no wrapper */ - uInt wbits; /* log2(window size) (8..15, defaults to 15) */ - inflate_blocks_statef - *blocks; /* current inflate_blocks state */ - -}; - - -int inflateReset(z_streamp z) -{ - if (z == Z_NULL || z->state == Z_NULL) - return Z_STREAM_ERROR; - z->total_in = z->total_out = 0; - z->msg = Z_NULL; - z->state->mode = z->state->nowrap ? imBLOCKS : imMETHOD; - inflate_blocks_reset(z->state->blocks, z, Z_NULL); - Tracev(("inflate: reset\n")); - return Z_OK; -} - - -int inflateEnd(z_streamp z) -{ - if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) - return Z_STREAM_ERROR; - if (z->state->blocks != Z_NULL) - inflate_blocks_free(z->state->blocks, z); - ZFREE(z, z->state); - z->state = Z_NULL; - Tracev(("inflate: end\n")); - return Z_OK; -} - - - -int inflateInit2_(z_streamp z, int w, const char *version, int stream_size) -{ - if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || - stream_size != sizeof(z_stream)) - return Z_VERSION_ERROR; - - /* initialize state */ - if (z == Z_NULL) - return Z_STREAM_ERROR; - z->msg = Z_NULL; - if (z->zalloc == Z_NULL) - { - z->zalloc = (void *(*)(void *, unsigned, unsigned))zcalloc; - z->opaque = (voidp)0; - } - if (z->zfree == Z_NULL) z->zfree = (void (*)(void *, void *))zcfree; - if ((z->state = (struct internal_state *) - ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) - return Z_MEM_ERROR; - z->state->blocks = Z_NULL; - - /* handle undocumented nowrap option (no zlib header or check) */ - z->state->nowrap = 0; - if (w < 0) - { - w = - w; - z->state->nowrap = 1; - } - - /* set window size */ - if (w < 8 || w > 15) - { - inflateEnd(z); - return Z_STREAM_ERROR; - } - z->state->wbits = (uInt)w; - - /* create inflate_blocks state */ - if ((z->state->blocks = - inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) - == Z_NULL) - { - inflateEnd(z); - return Z_MEM_ERROR; - } - Tracev(("inflate: allocated\n")); - - /* reset state */ - inflateReset(z); - return Z_OK; -} - -#if 0 -int inflateInit_(z_streamp z, const char *version, int stream_size) -{ - return inflateInit2_(z, DEF_WBITS, version, stream_size); -} -#endif - -#define iNEEDBYTE {if(z->avail_in==0)return r;r=f;} -#define iNEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) - -int inflate(z_streamp z, int f) -{ - int r; - uInt b; - - if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) - return Z_STREAM_ERROR; - f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; - r = Z_BUF_ERROR; - while (1) switch (z->state->mode) - { - case imMETHOD: - iNEEDBYTE - if (((z->state->sub.method = iNEXTBYTE) & 0xf) != Z_DEFLATED) - { - z->state->mode = imBAD; - z->msg = (char*)"unknown compression method"; - z->state->sub.marker = 5; /* can't try inflateSync */ - break; - } - if ((z->state->sub.method >> 4) + 8 > z->state->wbits) - { - z->state->mode = imBAD; - z->msg = (char*)"invalid window size"; - z->state->sub.marker = 5; /* can't try inflateSync */ - break; - } - z->state->mode = imFLAG; - case imFLAG: - iNEEDBYTE - b = iNEXTBYTE; - if (((z->state->sub.method << 8) + b) % 31) - { - z->state->mode = imBAD; - z->msg = (char*)"incorrect header check"; - z->state->sub.marker = 5; /* can't try inflateSync */ - break; - } - Tracev(("inflate: zlib header ok\n")); - if (!(b & PRESET_DICT)) - { - z->state->mode = imBLOCKS; - break; - } - z->state->mode = imDICT4; - case imDICT4: - iNEEDBYTE - z->state->sub.check.need = (uLong)iNEXTBYTE << 24; - z->state->mode = imDICT3; - case imDICT3: - iNEEDBYTE - z->state->sub.check.need += (uLong)iNEXTBYTE << 16; - z->state->mode = imDICT2; - case imDICT2: - iNEEDBYTE - z->state->sub.check.need += (uLong)iNEXTBYTE << 8; - z->state->mode = imDICT1; - case imDICT1: - iNEEDBYTE - z->state->sub.check.need += (uLong)iNEXTBYTE; - z->adler = z->state->sub.check.need; - z->state->mode = imDICT0; - return Z_NEED_DICT; - case imDICT0: - z->state->mode = imBAD; - z->msg = (char*)"need dictionary"; - z->state->sub.marker = 0; /* can try inflateSync */ - return Z_STREAM_ERROR; - case imBLOCKS: - r = inflate_blocks(z->state->blocks, z, r); - if (r == Z_DATA_ERROR) - { - z->state->mode = imBAD; - z->state->sub.marker = 0; /* can try inflateSync */ - break; - } - if (r == Z_OK) - r = f; - if (r != Z_STREAM_END) - return r; - r = f; - inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); - if (z->state->nowrap) - { - z->state->mode = imDONE; - break; - } - z->state->mode = imCHECK4; - case imCHECK4: - iNEEDBYTE - z->state->sub.check.need = (uLong)iNEXTBYTE << 24; - z->state->mode = imCHECK3; - case imCHECK3: - iNEEDBYTE - z->state->sub.check.need += (uLong)iNEXTBYTE << 16; - z->state->mode = imCHECK2; - case imCHECK2: - iNEEDBYTE - z->state->sub.check.need += (uLong)iNEXTBYTE << 8; - z->state->mode = imCHECK1; - case imCHECK1: - iNEEDBYTE - z->state->sub.check.need += (uLong)iNEXTBYTE; - - if (z->state->sub.check.was != z->state->sub.check.need) - { - z->state->mode = imBAD; - z->msg = (char*)"incorrect data check"; - z->state->sub.marker = 5; /* can't try inflateSync */ - break; - } - Tracev(("inflate: zlib check ok\n")); - z->state->mode = imDONE; - case imDONE: - return Z_STREAM_END; - case imBAD: - return Z_DATA_ERROR; - default: - return Z_STREAM_ERROR; - } -#ifdef NEED_DUMMY_RETURN - return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ -#endif -} - -// defined but not used -#if 0 -int inflateSetDictionary(z_streamp z, const Byte *dictionary, uInt dictLength) -{ - uInt length = dictLength; - - if (z == Z_NULL || z->state == Z_NULL || z->state->mode != imDICT0) - return Z_STREAM_ERROR; - - if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR; - z->adler = 1L; - - if (length >= ((uInt)1<state->wbits)) - { - length = (1<state->wbits)-1; - dictionary += dictLength - length; - } - inflate_set_dictionary(z->state->blocks, dictionary, length); - z->state->mode = imBLOCKS; - return Z_OK; -} - -int inflateSync(z_streamp z) -{ - uInt n; /* number of bytes to look at */ - Byte *p; /* pointer to bytes */ - uInt m; /* number of marker bytes found in a row */ - uLong r, w; /* temporaries to save total_in and total_out */ - - /* set up */ - if (z == Z_NULL || z->state == Z_NULL) - return Z_STREAM_ERROR; - if (z->state->mode != imBAD) - { - z->state->mode = imBAD; - z->state->sub.marker = 0; - } - if ((n = z->avail_in) == 0) - return Z_BUF_ERROR; - p = z->next_in; - m = z->state->sub.marker; - - /* search */ - while (n && m < 4) - { - static const Byte mark[4] = {0, 0, 0xff, 0xff}; - if (*p == mark[m]) - m++; - else if (*p) - m = 0; - else - m = 4 - m; - p++, n--; - } - - /* restore */ - z->total_in += p - z->next_in; - z->next_in = p; - z->avail_in = n; - z->state->sub.marker = m; - - /* return no joy or set up to restart on a new block */ - if (m != 4) - return Z_DATA_ERROR; - r = z->total_in; w = z->total_out; - inflateReset(z); - z->total_in = r; z->total_out = w; - z->state->mode = imBLOCKS; - return Z_OK; -} - -/* Returns true if inflate is currently at the end of a block generated - * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP - * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH - * but removes the length bytes of the resulting empty stored block. When - * decompressing, PPP checks that at the end of input packet, inflate is - * waiting for these length bytes. - */ -int inflateSyncPoint(z_streamp z) -{ - if (z == Z_NULL || z->state == Z_NULL || z->state->blocks == Z_NULL) - return Z_STREAM_ERROR; - return inflate_blocks_sync_point(z->state->blocks); -} -#endif - -voidp zcalloc (voidp opaque, unsigned items, unsigned size) -{ - if (opaque) items += size - size; /* make compiler happy */ - return (voidp)Z_Malloc(items*size); -} - -void zcfree (voidp opaque, voidp ptr) -{ - Z_Free(ptr); - if (opaque) return; /* make compiler happy */ -} - - diff --git a/tools/quake3/bspc/deps/qcommon/unzip.h b/tools/quake3/bspc/deps/qcommon/unzip.h deleted file mode 100644 index 2c9584b9..00000000 --- a/tools/quake3/bspc/deps/qcommon/unzip.h +++ /dev/null @@ -1,338 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include - -#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) -/* like the STRICT of WIN32, we define a pointer that cannot be converted - from (void*) without cast */ -typedef struct TagunzFile__ { int unused; } unzFile__; -typedef unzFile__ *unzFile; -#else -typedef void* unzFile; -#endif - -/* tm_unz contain date/time info */ -typedef struct tm_unz_s -{ - unsigned int tm_sec; /* seconds after the minute - [0,59] */ - unsigned int tm_min; /* minutes after the hour - [0,59] */ - unsigned int tm_hour; /* hours since midnight - [0,23] */ - unsigned int tm_mday; /* day of the month - [1,31] */ - unsigned int tm_mon; /* months since January - [0,11] */ - unsigned int tm_year; /* years - [1980..2044] */ -} tm_unz; - -/* unz_global_info structure contain global data about the ZIPfile - These data comes from the end of central dir */ -typedef struct unz_global_info_s -{ - unsigned long number_entry; /* total number of entries in the central dir on this disk */ - unsigned long size_comment; /* size of the global comment of the zipfile */ -} unz_global_info; - - -/* unz_file_info contain information about a file in the zipfile */ -typedef struct unz_file_info_s -{ - unsigned long version; /* version made by 2 unsigned chars */ - unsigned long version_needed; /* version needed to extract 2 unsigned chars */ - unsigned long flag; /* general purpose bit flag 2 unsigned chars */ - unsigned long compression_method; /* compression method 2 unsigned chars */ - unsigned long dosDate; /* last mod file date in Dos fmt 4 unsigned chars */ - unsigned long crc; /* crc-32 4 unsigned chars */ - unsigned long compressed_size; /* compressed size 4 unsigned chars */ - unsigned long uncompressed_size; /* uncompressed size 4 unsigned chars */ - unsigned long size_filename; /* filename length 2 unsigned chars */ - unsigned long size_file_extra; /* extra field length 2 unsigned chars */ - unsigned long size_file_comment; /* file comment length 2 unsigned chars */ - - unsigned long disk_num_start; /* disk number start 2 unsigned chars */ - unsigned long internal_fa; /* internal file attributes 2 unsigned chars */ - unsigned long external_fa; /* external file attributes 4 unsigned chars */ - - tm_unz tmu_date; -} unz_file_info; - -/* unz_file_info_interntal contain internal info about a file in zipfile*/ -typedef struct unz_file_info_internal_s -{ - unsigned long offset_curfile;/* relative offset of static header 4 unsigned chars */ -} unz_file_info_internal; - -typedef void* (*alloc_func) (void* opaque, unsigned int items, unsigned int size); -typedef void (*free_func) (void* opaque, void* address); - -struct internal_state; - -typedef struct z_stream_s { - unsigned char *next_in; /* next input unsigned char */ - unsigned int avail_in; /* number of unsigned chars available at next_in */ - unsigned long total_in; /* total nb of input unsigned chars read so */ - - unsigned char *next_out; /* next output unsigned char should be put there */ - unsigned int avail_out; /* remaining free space at next_out */ - unsigned long total_out; /* total nb of unsigned chars output so */ - - char *msg; /* last error message, NULL if no error */ - struct internal_state *state; /* not visible by applications */ - - alloc_func zalloc; /* used to allocate the internal state */ - free_func zfree; /* used to free the internal state */ - unsigned char* opaque; /* private data object passed to zalloc and zfree */ - - int data_type; /* best guess about the data type: ascii or binary */ - unsigned long adler; /* adler32 value of the uncompressed data */ - unsigned long reserved; /* reserved for future use */ -} z_stream; - -typedef z_stream *z_streamp; - - -/* file_in_zip_read_info_s contain internal information about a file in zipfile, - when reading and decompress it */ -typedef struct -{ - char *read_buffer; /* internal buffer for compressed data */ - z_stream stream; /* zLib stream structure for inflate */ - - unsigned long pos_in_zipfile; /* position in unsigned char on the zipfile, for fseek*/ - unsigned long stream_initialised; /* flag set if stream structure is initialised*/ - - unsigned long offset_local_extrafield;/* offset of the static extra field */ - unsigned int size_local_extrafield;/* size of the static extra field */ - unsigned long pos_local_extrafield; /* position in the static extra field in read*/ - - unsigned long crc32; /* crc32 of all data uncompressed */ - unsigned long crc32_wait; /* crc32 we must obtain after decompress all */ - unsigned long rest_read_compressed; /* number of unsigned char to be decompressed */ - unsigned long rest_read_uncompressed;/*number of unsigned char to be obtained after decomp*/ - FILE* file; /* io structore of the zipfile */ - unsigned long compression_method; /* compression method (0==store) */ - unsigned long byte_before_the_zipfile;/* unsigned char before the zipfile, (>0 for sfx)*/ -} file_in_zip_read_info_s; - - -/* unz_s contain internal information about the zipfile -*/ -typedef struct -{ - FILE* file; /* io structore of the zipfile */ - unz_global_info gi; /* public global information */ - unsigned long byte_before_the_zipfile;/* unsigned char before the zipfile, (>0 for sfx)*/ - unsigned long num_file; /* number of the current file in the zipfile*/ - unsigned long pos_in_central_dir; /* pos of the current file in the central dir*/ - unsigned long current_file_ok; /* flag about the usability of the current file*/ - unsigned long central_pos; /* position of the beginning of the central dir*/ - - unsigned long size_central_dir; /* size of the central directory */ - unsigned long offset_central_dir; /* offset of start of central directory with - respect to the starting disk number */ - - unz_file_info cur_file_info; /* public info about the current file in zip*/ - unz_file_info_internal cur_file_info_internal; /* private info about it*/ - file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current - file if we are decompressing it */ - unsigned char* tmpFile; - int tmpPos,tmpSize; -} unz_s; - -#define UNZ_OK (0) -#define UNZ_END_OF_LIST_OF_FILE (-100) -#define UNZ_ERRNO (Z_ERRNO) -#define UNZ_EOF (0) -#define UNZ_PARAMERROR (-102) -#define UNZ_BADZIPFILE (-103) -#define UNZ_INTERNALERROR (-104) -#define UNZ_CRCERROR (-105) - -#define UNZ_CASESENSITIVE 1 -#define UNZ_NOTCASESENSITIVE 2 -#define UNZ_OSDEFAULTCASE 0 - -extern int unzStringFileNameCompare (const char* fileName1, const char* fileName2, int iCaseSensitivity); - -/* - Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi - or strcasecmp) - If iCaseSenisivity = 0, case sensitivity is defaut of your operating system - (like 1 on Unix, 2 on Windows) -*/ - -extern unzFile unzOpen (const char *path); -extern unzFile unzReOpen (const char* path, unzFile file); - -/* - Open a Zip file. path contain the full pathname (by example, - on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer - "zlib/zlib111.zip". - If the zipfile cannot be opened (file don't exist or in not valid), the - return value is NULL. - Else, the return value is a unzFile Handle, usable with other function - of this unzip package. -*/ - -extern int unzClose (unzFile file); - -/* - Close a ZipFile opened with unzipOpen. - If there is files inside the .Zip opened with unzOpenCurrentFile (see later), - these files MUST be closed with unzipCloseCurrentFile before call unzipClose. - return UNZ_OK if there is no problem. */ - -extern int unzGetGlobalInfo (unzFile file, unz_global_info *pglobal_info); - -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. */ - - -extern int unzGetGlobalComment (unzFile file, char *szComment, unsigned long uSizeBuf); - -/* - Get the global comment string of the ZipFile, in the szComment buffer. - uSizeBuf is the size of the szComment buffer. - return the number of unsigned char copied or an error code <0 -*/ - - -/***************************************************************************/ -/* Unzip package allow you browse the directory of the zipfile */ - -extern int unzGoToFirstFile (unzFile file); - -/* - Set the current file of the zipfile to the first file. - return UNZ_OK if there is no problem -*/ - -extern int unzGoToNextFile (unzFile file); - -/* - Set the current file of the zipfile to the next file. - return UNZ_OK if there is no problem - return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. -*/ - -extern int unzGetCurrentFileInfoPosition (unzFile file, unsigned long *pos ); - -/* - Get the position of the info of the current file in the zip. - return UNZ_OK if there is no problem -*/ - -extern int unzSetCurrentFileInfoPosition (unzFile file, unsigned long pos ); - -/* - Set the position of the info of the current file in the zip. - return UNZ_OK if there is no problem -*/ - -extern int unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity); - -/* - Try locate the file szFileName in the zipfile. - For the iCaseSensitivity signification, see unzStringFileNameCompare - - return value : - UNZ_OK if the file is found. It becomes the current file. - UNZ_END_OF_LIST_OF_FILE if the file is not found -*/ - - -extern int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, char *szFileName, unsigned long fileNameBufferSize, void *extraField, unsigned long extraFieldBufferSize, char *szComment, unsigned long commentBufferSize); - -/* - Get Info about the current file - if pfile_info!=NULL, the *pfile_info structure will contain somes info about - the current file - if szFileName!=NULL, the filemane string will be copied in szFileName - (fileNameBufferSize is the size of the buffer) - if extraField!=NULL, the extra field information will be copied in extraField - (extraFieldBufferSize is the size of the buffer). - This is the Central-header version of the extra field - if szComment!=NULL, the comment string of the file will be copied in szComment - (commentBufferSize is the size of the buffer) -*/ - -/***************************************************************************/ -/* for reading the content of the current zipfile, you can open it, read data - from it, and close it (you can close it before reading all the file) - */ - -extern int unzOpenCurrentFile (unzFile file); - -/* - Open for reading data the current file in the zipfile. - If there is no error, the return value is UNZ_OK. -*/ - -extern int unzCloseCurrentFile (unzFile file); - -/* - Close the file in zip opened with unzOpenCurrentFile - Return UNZ_CRCERROR if all the file was read but the CRC is not good -*/ - - -extern int unzReadCurrentFile (unzFile file, void* buf, unsigned len); - -/* - Read unsigned chars from the current file (opened by unzOpenCurrentFile) - buf contain buffer where data must be copied - len the size of buf. - - return the number of unsigned char copied if somes unsigned chars are copied - return 0 if the end of file was reached - return <0 with error code if there is an error - (UNZ_ERRNO for IO error, or zLib error for uncompress error) -*/ - -extern long unztell(unzFile file); - -/* - Give the current position in uncompressed data -*/ - -extern int unzeof (unzFile file); - -/* - return 1 if the end of file was reached, 0 elsewhere -*/ - -extern int unzGetLocalExtrafield (unzFile file, void* buf, unsigned len); - -/* - Read extra field from the current file (opened by unzOpenCurrentFile) - This is the local-header version of the extra field (sometimes, there is - more info in the local-header version than in the central-header) - - if buf==NULL, it return the size of the local extra field - - if buf!=NULL, len is the size of the buffer, the extra header is copied in - buf. - the return value is the number of unsigned chars copied in buf, or (if <0) - the error code -*/ diff --git a/tools/quake3/bspc/deps/qcommon/vm.c b/tools/quake3/bspc/deps/qcommon/vm.c deleted file mode 100644 index a6b1e3c6..00000000 --- a/tools/quake3/bspc/deps/qcommon/vm.c +++ /dev/null @@ -1,835 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// vm.c -- virtual machine - -/* - - -intermix code and data -symbol table - -a dll has one imported function: VM_SystemCall -and one exported function: Perform - - -*/ - -#include "vm_local.h" - - -vm_t *currentVM = NULL; // bk001212 -vm_t *lastVM = NULL; // bk001212 -int vm_debugLevel; - -#define MAX_VM 3 -vm_t vmTable[MAX_VM]; - - -void VM_VmInfo_f( void ); -void VM_VmProfile_f( void ); - - -// converts a VM pointer to a C pointer and -// checks to make sure that the range is acceptable -void *VM_VM2C( vmptr_t p, int length ) { - return (void *)p; -} - -void VM_Debug( int level ) { - vm_debugLevel = level; -} - -/* -============== -VM_Init -============== -*/ -void VM_Init( void ) { - Cvar_Get( "vm_cgame", "2", CVAR_ARCHIVE ); // !@# SHIP WITH SET TO 2 - Cvar_Get( "vm_game", "2", CVAR_ARCHIVE ); // !@# SHIP WITH SET TO 2 - Cvar_Get( "vm_ui", "2", CVAR_ARCHIVE ); // !@# SHIP WITH SET TO 2 - - Cmd_AddCommand ("vmprofile", VM_VmProfile_f ); - Cmd_AddCommand ("vminfo", VM_VmInfo_f ); - - Com_Memset( vmTable, 0, sizeof( vmTable ) ); -} - - -/* -=============== -VM_ValueToSymbol - -Assumes a program counter value -=============== -*/ -const char *VM_ValueToSymbol( vm_t *vm, int value ) { - vmSymbol_t *sym; - static char text[MAX_TOKEN_CHARS]; - - sym = vm->symbols; - if ( !sym ) { - return "NO SYMBOLS"; - } - - // find the symbol - while ( sym->next && sym->next->symValue <= value ) { - sym = sym->next; - } - - if ( value == sym->symValue ) { - return sym->symName; - } - - Com_sprintf( text, sizeof( text ), "%s+%i", sym->symName, value - sym->symValue ); - - return text; -} - -/* -=============== -VM_ValueToFunctionSymbol - -For profiling, find the symbol behind this value -=============== -*/ -vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ) { - vmSymbol_t *sym; - static vmSymbol_t nullSym; - - sym = vm->symbols; - if ( !sym ) { - return &nullSym; - } - - while ( sym->next && sym->next->symValue <= value ) { - sym = sym->next; - } - - return sym; -} - - -/* -=============== -VM_SymbolToValue -=============== -*/ -int VM_SymbolToValue( vm_t *vm, const char *symbol ) { - vmSymbol_t *sym; - - for ( sym = vm->symbols ; sym ; sym = sym->next ) { - if ( !strcmp( symbol, sym->symName ) ) { - return sym->symValue; - } - } - return 0; -} - - -/* -===================== -VM_SymbolForCompiledPointer -===================== -*/ -const char *VM_SymbolForCompiledPointer( vm_t *vm, void *code ) { - int i; - - if ( code < (void *)vm->codeBase ) { - return "Before code block"; - } - if ( code >= (void *)(vm->codeBase + vm->codeLength) ) { - return "After code block"; - } - - // find which original instruction it is after - for ( i = 0 ; i < vm->codeLength ; i++ ) { - if ( (void *)vm->instructionPointers[i] > code ) { - break; - } - } - i--; - - // now look up the bytecode instruction pointer - return VM_ValueToSymbol( vm, i ); -} - - - -/* -=============== -ParseHex -=============== -*/ -int ParseHex( const char *text ) { - int value; - int c; - - value = 0; - while ( ( c = *text++ ) != 0 ) { - if ( c >= '0' && c <= '9' ) { - value = value * 16 + c - '0'; - continue; - } - if ( c >= 'a' && c <= 'f' ) { - value = value * 16 + 10 + c - 'a'; - continue; - } - if ( c >= 'A' && c <= 'F' ) { - value = value * 16 + 10 + c - 'A'; - continue; - } - } - - return value; -} - -/* -=============== -VM_LoadSymbols -=============== -*/ -void VM_LoadSymbols( vm_t *vm ) { - int len; - char *mapfile, *text_p, *token; - char name[MAX_QPATH]; - char symbols[MAX_QPATH]; - vmSymbol_t **prev, *sym; - int count; - int value; - int chars; - int segment; - int numInstructions; - - // don't load symbols if not developer - if ( !com_developer->integer ) { - return; - } - - COM_StripExtension( vm->name, name ); - Com_sprintf( symbols, sizeof( symbols ), "vm/%s.map", name ); - len = FS_ReadFile( symbols, (void **)&mapfile ); - if ( !mapfile ) { - Com_Printf( "Couldn't load symbol file: %s\n", symbols ); - return; - } - - numInstructions = vm->instructionPointersLength >> 2; - - // parse the symbols - text_p = mapfile; - prev = &vm->symbols; - count = 0; - - while ( 1 ) { - token = COM_Parse( &text_p ); - if ( !token[0] ) { - break; - } - segment = ParseHex( token ); - if ( segment ) { - COM_Parse( &text_p ); - COM_Parse( &text_p ); - continue; // only load code segment values - } - - token = COM_Parse( &text_p ); - if ( !token[0] ) { - Com_Printf( "WARNING: incomplete line at end of file\n" ); - break; - } - value = ParseHex( token ); - - token = COM_Parse( &text_p ); - if ( !token[0] ) { - Com_Printf( "WARNING: incomplete line at end of file\n" ); - break; - } - chars = strlen( token ); - sym = Hunk_Alloc( sizeof( *sym ) + chars, h_high ); - *prev = sym; - prev = &sym->next; - sym->next = NULL; - - // convert value from an instruction number to a code offset - if ( value >= 0 && value < numInstructions ) { - value = vm->instructionPointers[value]; - } - - sym->symValue = value; - Q_strncpyz( sym->symName, token, chars + 1 ); - - count++; - } - - vm->numSymbols = count; - Com_Printf( "%i symbols parsed from %s\n", count, symbols ); - FS_FreeFile( mapfile ); -} - -/* -============ -VM_DllSyscall - -Dlls will call this directly - - rcg010206 The horror; the horror. - - The syscall mechanism relies on stack manipulation to get it's args. - This is likely due to C's inability to pass "..." parameters to - a function in one clean chunk. On PowerPC Linux, these parameters - are not necessarily passed on the stack, so while (&arg[0] == arg) - is true, (&arg[1] == 2nd function parameter) is not necessarily - accurate, as arg's value might have been stored to the stack or - other piece of scratch memory to give it a valid address, but the - next parameter might still be sitting in a register. - - Quake's syscall system also assumes that the stack grows downward, - and that any needed types can be squeezed, safely, into a signed int. - - This hack below copies all needed values for an argument to a - array in memory, so that Quake can get the correct values. This can - also be used on systems where the stack grows upwards, as the - presumably standard and safe stdargs.h macros are used. - - As for having enough space in a signed int for your datatypes, well, - it might be better to wait for DOOM 3 before you start porting. :) - - The original code, while probably still inherently dangerous, seems - to work well enough for the platforms it already works on. Rather - than add the performance hit for those platforms, the original code - is still in use there. - - For speed, we just grab 15 arguments, and don't worry about exactly - how many the syscall actually needs; the extra is thrown away. - -============ -*/ -int QDECL VM_DllSyscall( int arg, ... ) { -#if ((defined __linux__) && (defined __powerpc__)) - // rcg010206 - see commentary above - int args[16]; - int i; - va_list ap; - - args[0] = arg; - - va_start(ap, arg); - for (i = 1; i < sizeof (args) / sizeof (args[i]); i++) - args[i] = va_arg(ap, int); - va_end(ap); - - return currentVM->systemCall( args ); -#else // original id code - return currentVM->systemCall( &arg ); -#endif -} - -/* -================= -VM_Restart - -Reload the data, but leave everything else in place -This allows a server to do a map_restart without changing memory allocation -================= -*/ -vm_t *VM_Restart( vm_t *vm ) { - vmHeader_t *header; - int length; - int dataLength; - int i; - char filename[MAX_QPATH]; - - // DLL's can't be restarted in place - if ( vm->dllHandle ) { - char name[MAX_QPATH]; - int (*systemCall)( int *parms ); - - systemCall = vm->systemCall; - Q_strncpyz( name, vm->name, sizeof( name ) ); - - VM_Free( vm ); - - vm = VM_Create( name, systemCall, VMI_NATIVE ); - return vm; - } - - // load the image - Com_Printf( "VM_Restart()\n", filename ); - Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name ); - Com_Printf( "Loading vm file %s.\n", filename ); - length = FS_ReadFile( filename, (void **)&header ); - if ( !header ) { - Com_Error( ERR_DROP, "VM_Restart failed.\n" ); - } - - // byte swap the header - for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) { - ((int *)header)[i] = LittleLong( ((int *)header)[i] ); - } - - // validate - if ( header->vmMagic != VM_MAGIC - || header->bssLength < 0 - || header->dataLength < 0 - || header->litLength < 0 - || header->codeLength <= 0 ) { - VM_Free( vm ); - Com_Error( ERR_FATAL, "%s has bad header", filename ); - } - - // round up to next power of 2 so all data operations can - // be mask protected - dataLength = header->dataLength + header->litLength + header->bssLength; - for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) { - } - dataLength = 1 << i; - - // clear the data - Com_Memset( vm->dataBase, 0, dataLength ); - - // copy the intialized data - Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength ); - - // byte swap the longs - for ( i = 0 ; i < header->dataLength ; i += 4 ) { - *(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) ); - } - - // free the original file - FS_FreeFile( header ); - - return vm; -} - -/* -================ -VM_Create - -If image ends in .qvm it will be interpreted, otherwise -it will attempt to load as a system dll -================ -*/ - -#define STACK_SIZE 0x20000 - -vm_t *VM_Create( const char *module, int (*systemCalls)(int *), - vmInterpret_t interpret ) { - vm_t *vm; - vmHeader_t *header; - int length; - int dataLength; - int i, remaining; - char filename[MAX_QPATH]; - - if ( !module || !module[0] || !systemCalls ) { - Com_Error( ERR_FATAL, "VM_Create: bad parms" ); - } - - remaining = Hunk_MemoryRemaining(); - - // see if we already have the VM - for ( i = 0 ; i < MAX_VM ; i++ ) { - if (!Q_stricmp(vmTable[i].name, module)) { - vm = &vmTable[i]; - return vm; - } - } - - // find a free vm - for ( i = 0 ; i < MAX_VM ; i++ ) { - if ( !vmTable[i].name[0] ) { - break; - } - } - - if ( i == MAX_VM ) { - Com_Error( ERR_FATAL, "VM_Create: no free vm_t" ); - } - - vm = &vmTable[i]; - - Q_strncpyz( vm->name, module, sizeof( vm->name ) ); - vm->systemCall = systemCalls; - - // never allow dll loading with a demo - if ( interpret == VMI_NATIVE ) { - if ( Cvar_VariableValue( "fs_restrict" ) ) { - interpret = VMI_COMPILED; - } - } - - if ( interpret == VMI_NATIVE ) { - // try to load as a system dll - Com_Printf( "Loading dll file %s.\n", vm->name ); - vm->dllHandle = Sys_LoadDll( module, vm->fqpath , &vm->entryPoint, VM_DllSyscall ); - if ( vm->dllHandle ) { - return vm; - } - - Com_Printf( "Failed to load dll, looking for qvm.\n" ); - interpret = VMI_COMPILED; - } - - // load the image - Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name ); - Com_Printf( "Loading vm file %s.\n", filename ); - length = FS_ReadFile( filename, (void **)&header ); - if ( !header ) { - Com_Printf( "Failed.\n" ); - VM_Free( vm ); - return NULL; - } - - // byte swap the header - for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) { - ((int *)header)[i] = LittleLong( ((int *)header)[i] ); - } - - // validate - if ( header->vmMagic != VM_MAGIC - || header->bssLength < 0 - || header->dataLength < 0 - || header->litLength < 0 - || header->codeLength <= 0 ) { - VM_Free( vm ); - Com_Error( ERR_FATAL, "%s has bad header", filename ); - } - - // round up to next power of 2 so all data operations can - // be mask protected - dataLength = header->dataLength + header->litLength + header->bssLength; - for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) { - } - dataLength = 1 << i; - - // allocate zero filled space for initialized and uninitialized data - vm->dataBase = Hunk_Alloc( dataLength, h_high ); - vm->dataMask = dataLength - 1; - - // copy the intialized data - Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength ); - - // byte swap the longs - for ( i = 0 ; i < header->dataLength ; i += 4 ) { - *(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) ); - } - - // allocate space for the jump targets, which will be filled in by the compile/prep functions - vm->instructionPointersLength = header->instructionCount * 4; - vm->instructionPointers = Hunk_Alloc( vm->instructionPointersLength, h_high ); - - // copy or compile the instructions - vm->codeLength = header->codeLength; - - if ( interpret >= VMI_COMPILED ) { - vm->compiled = qtrue; - VM_Compile( vm, header ); - } else { - vm->compiled = qfalse; - VM_PrepareInterpreter( vm, header ); - } - - // free the original file - FS_FreeFile( header ); - - // load the map file - VM_LoadSymbols( vm ); - - // the stack is implicitly at the end of the image - vm->programStack = vm->dataMask + 1; - vm->stackBottom = vm->programStack - STACK_SIZE; - - Com_Printf("%s loaded in %d bytes on the hunk\n", module, remaining - Hunk_MemoryRemaining()); - - return vm; -} - -/* -============== -VM_Free -============== -*/ -void VM_Free( vm_t *vm ) { - - if ( vm->dllHandle ) { - Sys_UnloadDll( vm->dllHandle ); - Com_Memset( vm, 0, sizeof( *vm ) ); - } -#if 0 // now automatically freed by hunk - if ( vm->codeBase ) { - Z_Free( vm->codeBase ); - } - if ( vm->dataBase ) { - Z_Free( vm->dataBase ); - } - if ( vm->instructionPointers ) { - Z_Free( vm->instructionPointers ); - } -#endif - Com_Memset( vm, 0, sizeof( *vm ) ); - - currentVM = NULL; - lastVM = NULL; -} - -void VM_Clear(void) { - int i; - for (i=0;ientryPoint ) { - return (void *)(currentVM->dataBase + intValue); - } - else { - return (void *)(currentVM->dataBase + (intValue & currentVM->dataMask)); - } -} - -void *VM_ExplicitArgPtr( vm_t *vm, int intValue ) { - if ( !intValue ) { - return NULL; - } - - // bk010124 - currentVM is missing on reconnect here as well? - if ( currentVM==NULL ) - return NULL; - - // - if ( vm->entryPoint ) { - return (void *)(vm->dataBase + intValue); - } - else { - return (void *)(vm->dataBase + (intValue & vm->dataMask)); - } -} - - -/* -============== -VM_Call - - -Upon a system call, the stack will look like: - -sp+32 parm1 -sp+28 parm0 -sp+24 return value -sp+20 return address -sp+16 local1 -sp+14 local0 -sp+12 arg1 -sp+8 arg0 -sp+4 return stack -sp return address - -An interpreted function will immediately execute -an OP_ENTER instruction, which will subtract space for -locals from sp -============== -*/ -#define MAX_STACK 256 -#define STACK_MASK (MAX_STACK-1) - -int QDECL VM_Call( vm_t *vm, int callnum, ... ) { - vm_t *oldVM; - int r; - int i; - int args[16]; - va_list ap; - - - if ( !vm ) { - Com_Error( ERR_FATAL, "VM_Call with NULL vm" ); - } - - oldVM = currentVM; - currentVM = vm; - lastVM = vm; - - if ( vm_debugLevel ) { - Com_Printf( "VM_Call( %i )\n", callnum ); - } - - // if we have a dll loaded, call it directly - if ( vm->entryPoint ) { - //rcg010207 - see dissertation at top of VM_DllSyscall() in this file. - va_start(ap, callnum); - for (i = 0; i < sizeof (args) / sizeof (args[i]); i++) { - args[i] = va_arg(ap, int); - } - va_end(ap); - - r = vm->entryPoint( callnum, args[0], args[1], args[2], args[3], - args[4], args[5], args[6], args[7], - args[8], args[9], args[10], args[11], - args[12], args[13], args[14], args[15]); - } else if ( vm->compiled ) { - r = VM_CallCompiled( vm, &callnum ); - } else { - r = VM_CallInterpreted( vm, &callnum ); - } - - if ( oldVM != NULL ) // bk001220 - assert(currentVM!=NULL) for oldVM==NULL - currentVM = oldVM; - return r; -} - -//================================================================= - -static int QDECL VM_ProfileSort( const void *a, const void *b ) { - vmSymbol_t *sa, *sb; - - sa = *(vmSymbol_t **)a; - sb = *(vmSymbol_t **)b; - - if ( sa->profileCount < sb->profileCount ) { - return -1; - } - if ( sa->profileCount > sb->profileCount ) { - return 1; - } - return 0; -} - -/* -============== -VM_VmProfile_f - -============== -*/ -void VM_VmProfile_f( void ) { - vm_t *vm; - vmSymbol_t **sorted, *sym; - int i; - double total; - - if ( !lastVM ) { - return; - } - - vm = lastVM; - - if ( !vm->numSymbols ) { - return; - } - - sorted = Z_Malloc( vm->numSymbols * sizeof( *sorted ) ); - sorted[0] = vm->symbols; - total = sorted[0]->profileCount; - for ( i = 1 ; i < vm->numSymbols ; i++ ) { - sorted[i] = sorted[i-1]->next; - total += sorted[i]->profileCount; - } - - qsort( sorted, vm->numSymbols, sizeof( *sorted ), VM_ProfileSort ); - - for ( i = 0 ; i < vm->numSymbols ; i++ ) { - int perc; - - sym = sorted[i]; - - perc = 100 * (float) sym->profileCount / total; - Com_Printf( "%2i%% %9i %s\n", perc, sym->profileCount, sym->symName ); - sym->profileCount = 0; - } - - Com_Printf(" %9.0f total\n", total ); - - Z_Free( sorted ); -} - -/* -============== -VM_VmInfo_f - -============== -*/ -void VM_VmInfo_f( void ) { - vm_t *vm; - int i; - - Com_Printf( "Registered virtual machines:\n" ); - for ( i = 0 ; i < MAX_VM ; i++ ) { - vm = &vmTable[i]; - if ( !vm->name[0] ) { - break; - } - Com_Printf( "%s : ", vm->name ); - if ( vm->dllHandle ) { - Com_Printf( "native\n" ); - continue; - } - if ( vm->compiled ) { - Com_Printf( "compiled on load\n" ); - } else { - Com_Printf( "interpreted\n" ); - } - Com_Printf( " code length : %7i\n", vm->codeLength ); - Com_Printf( " table length: %7i\n", vm->instructionPointersLength ); - Com_Printf( " data length : %7i\n", vm->dataMask + 1 ); - } -} - -/* -=============== -VM_LogSyscalls - -Insert calls to this while debugging the vm compiler -=============== -*/ -void VM_LogSyscalls( int *args ) { - static int callnum; - static FILE *f; - - if ( !f ) { - f = fopen("syscalls.log", "w" ); - } - callnum++; - fprintf(f, "%i: %i (%i) = %i %i %i %i\n", callnum, args - (int *)currentVM->dataBase, - args[0], args[1], args[2], args[3], args[4] ); -} - - - -#ifdef oDLL_ONLY // bk010215 - for DLL_ONLY dedicated servers/builds w/o VM -int VM_CallCompiled( vm_t *vm, int *args ) { - return(0); -} - -void VM_Compile( vm_t *vm, vmHeader_t *header ) {} -#endif // DLL_ONLY diff --git a/tools/quake3/bspc/deps/qcommon/vm_interpreted.c b/tools/quake3/bspc/deps/qcommon/vm_interpreted.c deleted file mode 100644 index 2c0087f0..00000000 --- a/tools/quake3/bspc/deps/qcommon/vm_interpreted.c +++ /dev/null @@ -1,889 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "vm_local.h" - -#ifdef DEBUG_VM // bk001204 -static char *opnames[256] = { - "OP_UNDEF", - - "OP_IGNORE", - - "OP_BREAK", - - "OP_ENTER", - "OP_LEAVE", - "OP_CALL", - "OP_PUSH", - "OP_POP", - - "OP_CONST", - - "OP_LOCAL", - - "OP_JUMP", - - //------------------- - - "OP_EQ", - "OP_NE", - - "OP_LTI", - "OP_LEI", - "OP_GTI", - "OP_GEI", - - "OP_LTU", - "OP_LEU", - "OP_GTU", - "OP_GEU", - - "OP_EQF", - "OP_NEF", - - "OP_LTF", - "OP_LEF", - "OP_GTF", - "OP_GEF", - - //------------------- - - "OP_LOAD1", - "OP_LOAD2", - "OP_LOAD4", - "OP_STORE1", - "OP_STORE2", - "OP_STORE4", - "OP_ARG", - - "OP_BLOCK_COPY", - - //------------------- - - "OP_SEX8", - "OP_SEX16", - - "OP_NEGI", - "OP_ADD", - "OP_SUB", - "OP_DIVI", - "OP_DIVU", - "OP_MODI", - "OP_MODU", - "OP_MULI", - "OP_MULU", - - "OP_BAND", - "OP_BOR", - "OP_BXOR", - "OP_BCOM", - - "OP_LSH", - "OP_RSHI", - "OP_RSHU", - - "OP_NEGF", - "OP_ADDF", - "OP_SUBF", - "OP_DIVF", - "OP_MULF", - - "OP_CVIF", - "OP_CVFI" -}; -#endif - -#if idppc - #if defined(__GNUC__) - static inline unsigned int loadWord(void *addr) { - unsigned int word; - - asm("lwbrx %0,0,%1" : "=r" (word) : "r" (addr)); - return word; - } - #else - #define loadWord(addr) __lwbrx(addr,0) - #endif -#else - #define loadWord(addr) *((int *)addr) -#endif - -char *VM_Indent( vm_t *vm ) { - static char *string = " "; - if ( vm->callLevel > 20 ) { - return string; - } - return string + 2 * ( 20 - vm->callLevel ); -} - -void VM_StackTrace( vm_t *vm, int programCounter, int programStack ) { - int count; - - count = 0; - do { - Com_Printf( "%s\n", VM_ValueToSymbol( vm, programCounter ) ); - programStack = *(int *)&vm->dataBase[programStack+4]; - programCounter = *(int *)&vm->dataBase[programStack]; - } while ( programCounter != -1 && ++count < 32 ); - -} - - -/* -==================== -VM_PrepareInterpreter -==================== -*/ -void VM_PrepareInterpreter( vm_t *vm, vmHeader_t *header ) { - int op; - int pc; - byte *code; - int instruction; - int *codeBase; - - vm->codeBase = Hunk_Alloc( vm->codeLength*4, h_high ); // we're now int aligned -// memcpy( vm->codeBase, (byte *)header + header->codeOffset, vm->codeLength ); - - // we don't need to translate the instructions, but we still need - // to find each instructions starting point for jumps - pc = 0; - instruction = 0; - code = (byte *)header + header->codeOffset; - codeBase = (int *)vm->codeBase; - - while ( instruction < header->instructionCount ) { - vm->instructionPointers[ instruction ] = pc; - instruction++; - - op = code[ pc ]; - codeBase[pc] = op; - if ( pc > header->codeLength ) { - Com_Error( ERR_FATAL, "VM_PrepareInterpreter: pc > header->codeLength" ); - } - - pc++; - - // these are the only opcodes that aren't a single byte - switch ( op ) { - case OP_ENTER: - case OP_CONST: - case OP_LOCAL: - case OP_LEAVE: - case OP_EQ: - case OP_NE: - case OP_LTI: - case OP_LEI: - case OP_GTI: - case OP_GEI: - case OP_LTU: - case OP_LEU: - case OP_GTU: - case OP_GEU: - case OP_EQF: - case OP_NEF: - case OP_LTF: - case OP_LEF: - case OP_GTF: - case OP_GEF: - case OP_BLOCK_COPY: - codeBase[pc+0] = loadWord(&code[pc]); - pc += 4; - break; - case OP_ARG: - codeBase[pc+0] = code[pc]; - pc += 1; - break; - default: - break; - } - - } - pc = 0; - instruction = 0; - code = (byte *)header + header->codeOffset; - codeBase = (int *)vm->codeBase; - - while ( instruction < header->instructionCount ) { - op = code[ pc ]; - instruction++; - pc++; - switch ( op ) { - case OP_ENTER: - case OP_CONST: - case OP_LOCAL: - case OP_LEAVE: - case OP_EQ: - case OP_NE: - case OP_LTI: - case OP_LEI: - case OP_GTI: - case OP_GEI: - case OP_LTU: - case OP_LEU: - case OP_GTU: - case OP_GEU: - case OP_EQF: - case OP_NEF: - case OP_LTF: - case OP_LEF: - case OP_GTF: - case OP_GEF: - case OP_BLOCK_COPY: - switch(op) { - case OP_EQ: - case OP_NE: - case OP_LTI: - case OP_LEI: - case OP_GTI: - case OP_GEI: - case OP_LTU: - case OP_LEU: - case OP_GTU: - case OP_GEU: - case OP_EQF: - case OP_NEF: - case OP_LTF: - case OP_LEF: - case OP_GTF: - case OP_GEF: - codeBase[pc] = vm->instructionPointers[codeBase[pc]]; - break; - default: - break; - } - pc += 4; - break; - case OP_ARG: - pc += 1; - break; - default: - break; - } - - } -} - -/* -============== -VM_Call - - -Upon a system call, the stack will look like: - -sp+32 parm1 -sp+28 parm0 -sp+24 return stack -sp+20 return address -sp+16 local1 -sp+14 local0 -sp+12 arg1 -sp+8 arg0 -sp+4 return stack -sp return address - -An interpreted function will immediately execute -an OP_ENTER instruction, which will subtract space for -locals from sp -============== -*/ -#define MAX_STACK 256 -#define STACK_MASK (MAX_STACK-1) -//#define DEBUG_VM - -#define DEBUGSTR va("%s%i", VM_Indent(vm), opStack-stack ) - -int VM_CallInterpreted( vm_t *vm, int *args ) { - int stack[MAX_STACK]; - int *opStack; - int programCounter; - int programStack; - int stackOnEntry; - byte *image; - int *codeImage; - int v1; - int dataMask; -#ifdef DEBUG_VM - vmSymbol_t *profileSymbol; -#endif - - // interpret the code - vm->currentlyInterpreting = qtrue; - - // we might be called recursively, so this might not be the very top - programStack = stackOnEntry = vm->programStack; - -#ifdef DEBUG_VM - profileSymbol = VM_ValueToFunctionSymbol( vm, 0 ); - // uncomment this for debugging breakpoints - vm->breakFunction = 0; -#endif - // set up the stack frame - - image = vm->dataBase; - codeImage = (int *)vm->codeBase; - dataMask = vm->dataMask; - - // leave a free spot at start of stack so - // that as long as opStack is valid, opStack-1 will - // not corrupt anything - opStack = stack; - programCounter = 0; - - programStack -= 48; - - *(int *)&image[ programStack + 44] = args[9]; - *(int *)&image[ programStack + 40] = args[8]; - *(int *)&image[ programStack + 36] = args[7]; - *(int *)&image[ programStack + 32] = args[6]; - *(int *)&image[ programStack + 28] = args[5]; - *(int *)&image[ programStack + 24] = args[4]; - *(int *)&image[ programStack + 20] = args[3]; - *(int *)&image[ programStack + 16] = args[2]; - *(int *)&image[ programStack + 12] = args[1]; - *(int *)&image[ programStack + 8 ] = args[0]; - *(int *)&image[ programStack + 4 ] = 0; // return stack - *(int *)&image[ programStack ] = -1; // will terminate the loop on return - - vm->callLevel = 0; - - VM_Debug(0); - -// vm_debugLevel=2; - // main interpreter loop, will exit when a LEAVE instruction - // grabs the -1 program counter - -#define r2 codeImage[programCounter] - - while ( 1 ) { - int opcode, r0, r1; -// unsigned int r2; - -nextInstruction: - r0 = ((int *)opStack)[0]; - r1 = ((int *)opStack)[-1]; -nextInstruction2: - opcode = codeImage[ programCounter++ ]; -#ifdef DEBUG_VM - if ( (unsigned)programCounter > vm->codeLength ) { - Com_Error( ERR_DROP, "VM pc out of range" ); - } - - if ( opStack < stack ) { - Com_Error( ERR_DROP, "VM opStack underflow" ); - } - if ( opStack >= stack+MAX_STACK ) { - Com_Error( ERR_DROP, "VM opStack overflow" ); - } - - if ( programStack <= vm->stackBottom ) { - Com_Error( ERR_DROP, "VM stack overflow" ); - } - - if ( programStack & 3 ) { - Com_Error( ERR_DROP, "VM program stack misaligned" ); - } - - if ( vm_debugLevel > 1 ) { - Com_Printf( "%s %s\n", DEBUGSTR, opnames[opcode] ); - } - profileSymbol->profileCount++; -#endif - - switch ( opcode ) { -#ifdef DEBUG_VM - default: - Com_Error( ERR_DROP, "Bad VM instruction" ); // this should be scanned on load! -#endif - case OP_BREAK: - vm->breakCount++; - goto nextInstruction2; - case OP_CONST: - opStack++; - r1 = r0; - r0 = *opStack = r2; - - programCounter += 4; - goto nextInstruction2; - case OP_LOCAL: - opStack++; - r1 = r0; - r0 = *opStack = r2+programStack; - - programCounter += 4; - goto nextInstruction2; - - case OP_LOAD4: -#ifdef DEBUG_VM - if ( *opStack & 3 ) { - Com_Error( ERR_DROP, "OP_LOAD4 misaligned" ); - } -#endif - r0 = *opStack = *(int *)&image[ r0&dataMask ]; - goto nextInstruction2; - case OP_LOAD2: - r0 = *opStack = *(unsigned short *)&image[ r0&dataMask ]; - goto nextInstruction2; - case OP_LOAD1: - r0 = *opStack = image[ r0&dataMask ]; - goto nextInstruction2; - - case OP_STORE4: - *(int *)&image[ r1&(dataMask & ~3) ] = r0; - opStack -= 2; - goto nextInstruction; - case OP_STORE2: - *(short *)&image[ r1&(dataMask & ~1) ] = r0; - opStack -= 2; - goto nextInstruction; - case OP_STORE1: - image[ r1&dataMask ] = r0; - opStack -= 2; - goto nextInstruction; - - case OP_ARG: - // single byte offset from programStack - *(int *)&image[ codeImage[programCounter] + programStack ] = r0; - opStack--; - programCounter += 1; - goto nextInstruction; - - case OP_BLOCK_COPY: - { - int *src, *dest; - int i, count, srci, desti; - - count = r2; - // MrE: copy range check - srci = r0 & dataMask; - desti = r1 & dataMask; - count = ((srci + count) & dataMask) - srci; - count = ((desti + count) & dataMask) - desti; - - src = (int *)&image[ r0&dataMask ]; - dest = (int *)&image[ r1&dataMask ]; - if ( ( (int)src | (int)dest | count ) & 3 ) { - Com_Error( ERR_DROP, "OP_BLOCK_COPY not dword aligned" ); - } - count >>= 2; - for ( i = count-1 ; i>= 0 ; i-- ) { - dest[i] = src[i]; - } - programCounter += 4; - opStack -= 2; - } - goto nextInstruction; - - case OP_CALL: - // save current program counter - *(int *)&image[ programStack ] = programCounter; - - // jump to the location on the stack - programCounter = r0; - opStack--; - if ( programCounter < 0 ) { - // system call - int r; - int temp; -#ifdef DEBUG_VM - int stomped; - - if ( vm_debugLevel ) { - Com_Printf( "%s---> systemcall(%i)\n", DEBUGSTR, -1 - programCounter ); - } -#endif - // save the stack to allow recursive VM entry - temp = vm->callLevel; - vm->programStack = programStack - 4; -#ifdef DEBUG_VM - stomped = *(int *)&image[ programStack + 4 ]; -#endif - *(int *)&image[ programStack + 4 ] = -1 - programCounter; - -//VM_LogSyscalls( (int *)&image[ programStack + 4 ] ); - r = vm->systemCall( (int *)&image[ programStack + 4 ] ); - -#ifdef DEBUG_VM - // this is just our stack frame pointer, only needed - // for debugging - *(int *)&image[ programStack + 4 ] = stomped; -#endif - - // save return value - opStack++; - *opStack = r; - programCounter = *(int *)&image[ programStack ]; - vm->callLevel = temp; -#ifdef DEBUG_VM - if ( vm_debugLevel ) { - Com_Printf( "%s<--- %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter ) ); - } -#endif - } else { - programCounter = vm->instructionPointers[ programCounter ]; - } - goto nextInstruction; - - // push and pop are only needed for discarded or bad function return values - case OP_PUSH: - opStack++; - goto nextInstruction; - case OP_POP: - opStack--; - goto nextInstruction; - - case OP_ENTER: -#ifdef DEBUG_VM - profileSymbol = VM_ValueToFunctionSymbol( vm, programCounter ); -#endif - // get size of stack frame - v1 = r2; - - programCounter += 4; - programStack -= v1; -#ifdef DEBUG_VM - // save old stack frame for debugging traces - *(int *)&image[programStack+4] = programStack + v1; - if ( vm_debugLevel ) { - Com_Printf( "%s---> %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter - 5 ) ); - if ( vm->breakFunction && programCounter - 5 == vm->breakFunction ) { - // this is to allow setting breakpoints here in the debugger - vm->breakCount++; -// vm_debugLevel = 2; -// VM_StackTrace( vm, programCounter, programStack ); - } - vm->callLevel++; - } -#endif - goto nextInstruction; - case OP_LEAVE: - // remove our stack frame - v1 = r2; - - programStack += v1; - - // grab the saved program counter - programCounter = *(int *)&image[ programStack ]; -#ifdef DEBUG_VM - profileSymbol = VM_ValueToFunctionSymbol( vm, programCounter ); - if ( vm_debugLevel ) { - vm->callLevel--; - Com_Printf( "%s<--- %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter ) ); - } -#endif - // check for leaving the VM - if ( programCounter == -1 ) { - goto done; - } - goto nextInstruction; - - /* - =================================================================== - BRANCHES - =================================================================== - */ - - case OP_JUMP: - programCounter = r0; - programCounter = vm->instructionPointers[ programCounter ]; - opStack--; - goto nextInstruction; - - case OP_EQ: - opStack -= 2; - if ( r1 == r0 ) { - programCounter = r2; //vm->instructionPointers[r2]; - goto nextInstruction; - } else { - programCounter += 4; - goto nextInstruction; - } - - case OP_NE: - opStack -= 2; - if ( r1 != r0 ) { - programCounter = r2; //vm->instructionPointers[r2]; - goto nextInstruction; - } else { - programCounter += 4; - goto nextInstruction; - } - - case OP_LTI: - opStack -= 2; - if ( r1 < r0 ) { - programCounter = r2; //vm->instructionPointers[r2]; - goto nextInstruction; - } else { - programCounter += 4; - goto nextInstruction; - } - - case OP_LEI: - opStack -= 2; - if ( r1 <= r0 ) { - programCounter = r2; //vm->instructionPointers[r2]; - goto nextInstruction; - } else { - programCounter += 4; - goto nextInstruction; - } - - case OP_GTI: - opStack -= 2; - if ( r1 > r0 ) { - programCounter = r2; //vm->instructionPointers[r2]; - goto nextInstruction; - } else { - programCounter += 4; - goto nextInstruction; - } - - case OP_GEI: - opStack -= 2; - if ( r1 >= r0 ) { - programCounter = r2; //vm->instructionPointers[r2]; - goto nextInstruction; - } else { - programCounter += 4; - goto nextInstruction; - } - - case OP_LTU: - opStack -= 2; - if ( ((unsigned)r1) < ((unsigned)r0) ) { - programCounter = r2; //vm->instructionPointers[r2]; - goto nextInstruction; - } else { - programCounter += 4; - goto nextInstruction; - } - - case OP_LEU: - opStack -= 2; - if ( ((unsigned)r1) <= ((unsigned)r0) ) { - programCounter = r2; //vm->instructionPointers[r2]; - goto nextInstruction; - } else { - programCounter += 4; - goto nextInstruction; - } - - case OP_GTU: - opStack -= 2; - if ( ((unsigned)r1) > ((unsigned)r0) ) { - programCounter = r2; //vm->instructionPointers[r2]; - goto nextInstruction; - } else { - programCounter += 4; - goto nextInstruction; - } - - case OP_GEU: - opStack -= 2; - if ( ((unsigned)r1) >= ((unsigned)r0) ) { - programCounter = r2; //vm->instructionPointers[r2]; - goto nextInstruction; - } else { - programCounter += 4; - goto nextInstruction; - } - - case OP_EQF: - if ( ((float *)opStack)[-1] == *(float *)opStack ) { - programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; - goto nextInstruction; - } else { - programCounter += 4; - opStack -= 2; - goto nextInstruction; - } - - case OP_NEF: - if ( ((float *)opStack)[-1] != *(float *)opStack ) { - programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; - goto nextInstruction; - } else { - programCounter += 4; - opStack -= 2; - goto nextInstruction; - } - - case OP_LTF: - if ( ((float *)opStack)[-1] < *(float *)opStack ) { - programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; - goto nextInstruction; - } else { - programCounter += 4; - opStack -= 2; - goto nextInstruction; - } - - case OP_LEF: - if ( ((float *)opStack)[-1] <= *(float *)opStack ) { - programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; - goto nextInstruction; - } else { - programCounter += 4; - opStack -= 2; - goto nextInstruction; - } - - case OP_GTF: - if ( ((float *)opStack)[-1] > *(float *)opStack ) { - programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; - goto nextInstruction; - } else { - programCounter += 4; - opStack -= 2; - goto nextInstruction; - } - - case OP_GEF: - if ( ((float *)opStack)[-1] >= *(float *)opStack ) { - programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; - goto nextInstruction; - } else { - programCounter += 4; - opStack -= 2; - goto nextInstruction; - } - - - //=================================================================== - - case OP_NEGI: - *opStack = -r0; - goto nextInstruction; - case OP_ADD: - opStack[-1] = r1 + r0; - opStack--; - goto nextInstruction; - case OP_SUB: - opStack[-1] = r1 - r0; - opStack--; - goto nextInstruction; - case OP_DIVI: - opStack[-1] = r1 / r0; - opStack--; - goto nextInstruction; - case OP_DIVU: - opStack[-1] = ((unsigned)r1) / ((unsigned)r0); - opStack--; - goto nextInstruction; - case OP_MODI: - opStack[-1] = r1 % r0; - opStack--; - goto nextInstruction; - case OP_MODU: - opStack[-1] = ((unsigned)r1) % (unsigned)r0; - opStack--; - goto nextInstruction; - case OP_MULI: - opStack[-1] = r1 * r0; - opStack--; - goto nextInstruction; - case OP_MULU: - opStack[-1] = ((unsigned)r1) * ((unsigned)r0); - opStack--; - goto nextInstruction; - - case OP_BAND: - opStack[-1] = ((unsigned)r1) & ((unsigned)r0); - opStack--; - goto nextInstruction; - case OP_BOR: - opStack[-1] = ((unsigned)r1) | ((unsigned)r0); - opStack--; - goto nextInstruction; - case OP_BXOR: - opStack[-1] = ((unsigned)r1) ^ ((unsigned)r0); - opStack--; - goto nextInstruction; - case OP_BCOM: - opStack[-1] = ~ ((unsigned)r0); - goto nextInstruction; - - case OP_LSH: - opStack[-1] = r1 << r0; - opStack--; - goto nextInstruction; - case OP_RSHI: - opStack[-1] = r1 >> r0; - opStack--; - goto nextInstruction; - case OP_RSHU: - opStack[-1] = ((unsigned)r1) >> r0; - opStack--; - goto nextInstruction; - - case OP_NEGF: - *(float *)opStack = -*(float *)opStack; - goto nextInstruction; - case OP_ADDF: - *(float *)(opStack-1) = *(float *)(opStack-1) + *(float *)opStack; - opStack--; - goto nextInstruction; - case OP_SUBF: - *(float *)(opStack-1) = *(float *)(opStack-1) - *(float *)opStack; - opStack--; - goto nextInstruction; - case OP_DIVF: - *(float *)(opStack-1) = *(float *)(opStack-1) / *(float *)opStack; - opStack--; - goto nextInstruction; - case OP_MULF: - *(float *)(opStack-1) = *(float *)(opStack-1) * *(float *)opStack; - opStack--; - goto nextInstruction; - - case OP_CVIF: - *(float *)opStack = (float)*opStack; - goto nextInstruction; - case OP_CVFI: - *opStack = (int) *(float *)opStack; - goto nextInstruction; - case OP_SEX8: - *opStack = (signed char)*opStack; - goto nextInstruction; - case OP_SEX16: - *opStack = (short)*opStack; - goto nextInstruction; - } - } - -done: - vm->currentlyInterpreting = qfalse; - - if ( opStack != &stack[1] ) { - Com_Error( ERR_DROP, "Interpreter error: opStack = %i", opStack - stack ); - } - - vm->programStack = stackOnEntry; - - // return the result - return *opStack; -} diff --git a/tools/quake3/bspc/deps/qcommon/vm_local.h b/tools/quake3/bspc/deps/qcommon/vm_local.h deleted file mode 100644 index 89127710..00000000 --- a/tools/quake3/bspc/deps/qcommon/vm_local.h +++ /dev/null @@ -1,190 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "q_shared.h" -#include "qcommon.h" - -#define OPSTACK_SIZE 256 -#define OPSTACK_MASK (OPSTACK_SIZE-1) - -// don't change -// Hardcoded in q3asm an reserved at end of bss -#define PROGRAM_STACK_SIZE 0x10000 -#define PROGRAM_STACK_MASK (PROGRAM_STACK_SIZE-1) - -typedef enum { - OP_UNDEF, - - OP_IGNORE, - - OP_BREAK, - - OP_ENTER, - OP_LEAVE, - OP_CALL, - OP_PUSH, - OP_POP, - - OP_CONST, - OP_LOCAL, - - OP_JUMP, - - //------------------- - - OP_EQ, - OP_NE, - - OP_LTI, - OP_LEI, - OP_GTI, - OP_GEI, - - OP_LTU, - OP_LEU, - OP_GTU, - OP_GEU, - - OP_EQF, - OP_NEF, - - OP_LTF, - OP_LEF, - OP_GTF, - OP_GEF, - - //------------------- - - OP_LOAD1, - OP_LOAD2, - OP_LOAD4, - OP_STORE1, - OP_STORE2, - OP_STORE4, // *(stack[top-1]) = stack[top] - OP_ARG, - - OP_BLOCK_COPY, - - //------------------- - - OP_SEX8, - OP_SEX16, - - OP_NEGI, - OP_ADD, - OP_SUB, - OP_DIVI, - OP_DIVU, - OP_MODI, - OP_MODU, - OP_MULI, - OP_MULU, - - OP_BAND, - OP_BOR, - OP_BXOR, - OP_BCOM, - - OP_LSH, - OP_RSHI, - OP_RSHU, - - OP_NEGF, - OP_ADDF, - OP_SUBF, - OP_DIVF, - OP_MULF, - - OP_CVIF, - OP_CVFI -} opcode_t; - - - -typedef int vmptr_t; - -typedef struct vmSymbol_s { - struct vmSymbol_s *next; - int symValue; - int profileCount; - char symName[1]; // variable sized -} vmSymbol_t; - -#define VM_OFFSET_PROGRAM_STACK 0 -#define VM_OFFSET_SYSTEM_CALL 4 - -struct vm_s { - // DO NOT MOVE OR CHANGE THESE WITHOUT CHANGING THE VM_OFFSET_* DEFINES - // USED BY THE ASM CODE - int programStack; // the vm may be recursively entered - intptr_t (*systemCall)( intptr_t *parms ); - - //------------------------------------ - - char name[MAX_QPATH]; - - // for dynamic linked modules - void *dllHandle; - intptr_t (QDECL *entryPoint)( int callNum, ... ); - void (*destroy)(vm_t* self); - - // for interpreted modules - qboolean currentlyInterpreting; - - qboolean compiled; - byte *codeBase; - int codeLength; - - int *instructionPointers; - int instructionCount; - - byte *dataBase; - int dataMask; - - int stackBottom; // if programStack < stackBottom, error - - int numSymbols; - struct vmSymbol_s *symbols; - - int callLevel; // counts recursive VM_Call - int breakFunction; // increment breakCount on function entry to this - int breakCount; - - char fqpath[MAX_QPATH+1] ; - - byte *jumpTableTargets; - int numJumpTableTargets; -}; - - -extern vm_t *currentVM; -extern int vm_debugLevel; - -void VM_Compile( vm_t *vm, vmHeader_t *header ); -int VM_CallCompiled( vm_t *vm, int *args ); - -void VM_PrepareInterpreter( vm_t *vm, vmHeader_t *header ); -int VM_CallInterpreted( vm_t *vm, int *args ); - -vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ); -int VM_SymbolToValue( vm_t *vm, const char *symbol ); -const char *VM_ValueToSymbol( vm_t *vm, int value ); -void VM_LogSyscalls( int *args ); diff --git a/tools/quake3/bspc/deps/qcommon/vm_powerpc_asm.h b/tools/quake3/bspc/deps/qcommon/vm_powerpc_asm.h deleted file mode 100644 index 9bccf18a..00000000 --- a/tools/quake3/bspc/deps/qcommon/vm_powerpc_asm.h +++ /dev/null @@ -1,156 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2008 Przemyslaw Iskra - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#ifndef VM_POWERPC_ASM_H -#define VM_POWERPC_ASM_H - -/* - * Register information according to: - * http://refspecs.freestandards.org/elf/elfspec_ppc.pdf - */ - -#define r0 0 // volatile -#define r1 1 // caller safe ( stack pointer ) -#define r2 2 // reserved -#define r3 3 // callee safe -#define r4 4 // callee safe -#define r5 5 // callee safe -#define r6 6 // callee safe -#define r7 7 // callee safe -#define r8 8 // callee safe -#define r9 9 // callee safe -#define r10 10 // callee safe -#define r11 11 // volatile -#define r12 12 // volatile -#define r13 13 // reserved ( small data area ) -#define r14 14 // caller safe -#define r15 15 // caller safe -#define r16 16 // caller safe -#define r17 17 // caller safe -#define r18 18 // caller safe -#define r19 19 // caller safe -#define r20 20 // caller safe -#define r21 21 // caller safe -#define r22 22 // caller safe -#define r23 23 // caller safe -#define r24 24 // caller safe -#define r25 25 // caller safe -#define r26 26 // caller safe -#define r27 27 // caller safe -#define r28 28 // caller safe -#define r29 29 // caller safe -#define r30 30 // caller safe -#define r31 31 // caller safe ( environment pointers ) - -#define f0 0 // callee safe -#define f1 1 // callee safe -#define f2 2 // callee safe -#define f3 3 // callee safe -#define f4 4 // callee safe -#define f5 5 // callee safe -#define f6 6 // callee safe -#define f7 7 // callee safe -#define f8 8 // callee safe -#define f9 9 // callee safe -#define f10 10 // callee safe -#define f11 11 // callee safe -#define f12 12 // callee safe -#define f13 13 // callee safe -#define f14 14 // caller safe -#define f15 15 // caller safe -#define f16 16 // caller safe -#define f17 17 // caller safe -#define f18 18 // caller safe -#define f19 19 // caller safe -#define f20 20 // caller safe -#define f21 21 // caller safe -#define f22 22 // caller safe -#define f23 23 // caller safe -#define f24 24 // caller safe -#define f25 25 // caller safe -#define f26 26 // caller safe -#define f27 27 // caller safe -#define f28 28 // caller safe -#define f29 29 // caller safe -#define f30 30 // caller safe -#define f31 31 // caller safe - -#define cr0 0 // volatile -#define cr1 1 // volatile -#define cr2 2 // caller safe -#define cr3 3 // caller safe -#define cr4 4 // caller safe -#define cr5 5 // volatile -#define cr6 6 // volatile -#define cr7 7 // volatile - -#define lt 0 -#define gt 1 -#define eq 2 -#define so 3 - -// branch bo field values -#define branchLikely 1 -#define branchFalse 4 -#define branchTrue 12 -#define branchAlways 20 - -// branch extensions (change branch type) -#define branchExtLink 0x0001 - - -/* - * This list must match exactly the powerpc_opcodes list from vm_powerpc_asm.c - * If you're changing the original list remember to regenerate this one. You - * may do so using this perl script: - perl -p -e 'BEGIN{%t=("-"=>m=>"+"=>p=>"."=>d=>);$l=""}$o=0 if/^}/; - if($o && s/^{ "(.*?)([\.+-])?".+/i\U$1\E$t{$2}/s){$_.="_" while$l{$_}; - $l{$_}=1;if(length $l.$_ > 70){$s=$_;$_="\t$l\n";$l="$s,"}else - {$l.=" $_,";$_=undef}}else{$o=1 if/powerpc_opcodes.*=/;$_=undef}; - END{print "\t$l\n"}' < vm_powerpc_asm.c - */ - -typedef enum powerpc_iname { - iCMPLWI, iCMPWI, iCMPW, iCMPLW, iFCMPU, iLI, iLIS, iADDI, iADDIS, - iBLTm, iBC, iBCL, iB, iBL, iBLR, iBCTR, iBCTRL, iRLWINM, iNOP, iORI, - iXORIS, iLDX, iLWZX, iSLW, iAND, iSUB, iLBZX, iNEG, iNOT, iSTWX, iSTBX, - iMULLW, iADD, iLHZX, iXOR, iMFLR, iSTHX, iMR, iOR, iDIVWU, iMTLR, - iMTCTR, iDIVW, iLFSX, iSRW, iSTFSX, iSRAW, iEXTSH, iEXTSB, iLWZ, iLBZ, - iSTW, iSTWU, iSTB, iLHZ, iSTH, iLFS, iLFD, iSTFS, iSTFD, iLD, iFDIVS, - iFSUBS, iFADDS, iFMULS, iSTD, iSTDU, iFRSP, iFCTIWZ, iFSUB, iFNEG, -} powerpc_iname_t; - -#include - -typedef uint32_t ppc_instruction_t; - -extern ppc_instruction_t -asm_instruction( powerpc_iname_t, const int, const long int * ); - -#define IN( inst, args... ) \ -({\ - const long int argv[] = { args };\ - const int argc = sizeof( argv ) / sizeof( argv[0] ); \ - asm_instruction( inst, argc, argv );\ -}) - -#endif diff --git a/tools/quake3/bspc/deps/qcommon/vm_ppc.c b/tools/quake3/bspc/deps/qcommon/vm_ppc.c deleted file mode 100644 index a891ef6f..00000000 --- a/tools/quake3/bspc/deps/qcommon/vm_ppc.c +++ /dev/null @@ -1,1479 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// vm_ppc.c -// ppc dynamic compiler - -#include "vm_local.h" - -#pragma opt_pointer_analysis off - - -typedef enum { - R_REAL_STACK = 1, - // registers 3-11 are the parameter passing registers - - // state - R_STACK = 3, // local - R_OPSTACK, // global - - // constants - R_MEMBASE, // global - R_MEMMASK, - R_ASMCALL, // global - R_INSTRUCTIONS, // global - R_NUM_INSTRUCTIONS, // global - R_CVM, // currentVM - - // temps - R_TOP = 11, - R_SECOND = 12, - R_EA = 2 // effective address calculation - -} regNums_t; - -#define RG_REAL_STACK r1 -#define RG_STACK r3 -#define RG_OPSTACK r4 -#define RG_MEMBASE r5 -#define RG_MEMMASK r6 -#define RG_ASMCALL r7 -#define RG_INSTRUCTIONS r8 -#define RG_NUM_INSTRUCTIONS r9 -#define RG_CVM r10 -#define RG_TOP r12 -#define RG_SECOND r13 -#define RG_EA r14 - -// this doesn't have the low order bits set for instructions i'm not using... -typedef enum { - PPC_TDI = 0x08000000, - PPC_TWI = 0x0c000000, - PPC_MULLI = 0x1c000000, - PPC_SUBFIC = 0x20000000, - PPC_CMPI = 0x28000000, - PPC_CMPLI = 0x2c000000, - PPC_ADDIC = 0x30000000, - PPC_ADDIC_ = 0x34000000, - PPC_ADDI = 0x38000000, - PPC_ADDIS = 0x3c000000, - PPC_BC = 0x40000000, - PPC_SC = 0x44000000, - PPC_B = 0x48000000, - - PPC_MCRF = 0x4c000000, - PPC_BCLR = 0x4c000020, - PPC_RFID = 0x4c000000, - PPC_CRNOR = 0x4c000000, - PPC_RFI = 0x4c000000, - PPC_CRANDC = 0x4c000000, - PPC_ISYNC = 0x4c000000, - PPC_CRXOR = 0x4c000000, - PPC_CRNAND = 0x4c000000, - PPC_CREQV = 0x4c000000, - PPC_CRORC = 0x4c000000, - PPC_CROR = 0x4c000000, -//------------ - PPC_BCCTR = 0x4c000420, - PPC_RLWIMI = 0x50000000, - PPC_RLWINM = 0x54000000, - PPC_RLWNM = 0x5c000000, - PPC_ORI = 0x60000000, - PPC_ORIS = 0x64000000, - PPC_XORI = 0x68000000, - PPC_XORIS = 0x6c000000, - PPC_ANDI_ = 0x70000000, - PPC_ANDIS_ = 0x74000000, - PPC_RLDICL = 0x78000000, - PPC_RLDICR = 0x78000000, - PPC_RLDIC = 0x78000000, - PPC_RLDIMI = 0x78000000, - PPC_RLDCL = 0x78000000, - PPC_RLDCR = 0x78000000, - PPC_CMP = 0x7c000000, - PPC_TW = 0x7c000000, - PPC_SUBFC = 0x7c000010, - PPC_MULHDU = 0x7c000000, - PPC_ADDC = 0x7c000014, - PPC_MULHWU = 0x7c000000, - PPC_MFCR = 0x7c000000, - PPC_LWAR = 0x7c000000, - PPC_LDX = 0x7c000000, - PPC_LWZX = 0x7c00002e, - PPC_SLW = 0x7c000030, - PPC_CNTLZW = 0x7c000000, - PPC_SLD = 0x7c000000, - PPC_AND = 0x7c000038, - PPC_CMPL = 0x7c000040, - PPC_SUBF = 0x7c000050, - PPC_LDUX = 0x7c000000, -//------------ - PPC_DCBST = 0x7c000000, - PPC_LWZUX = 0x7c00006c, - PPC_CNTLZD = 0x7c000000, - PPC_ANDC = 0x7c000000, - PPC_TD = 0x7c000000, - PPC_MULHD = 0x7c000000, - PPC_MULHW = 0x7c000000, - PPC_MTSRD = 0x7c000000, - PPC_MFMSR = 0x7c000000, - PPC_LDARX = 0x7c000000, - PPC_DCBF = 0x7c000000, - PPC_LBZX = 0x7c0000ae, - PPC_NEG = 0x7c000000, - PPC_MTSRDIN = 0x7c000000, - PPC_LBZUX = 0x7c000000, - PPC_NOR = 0x7c0000f8, - PPC_SUBFE = 0x7c000000, - PPC_ADDE = 0x7c000000, - PPC_MTCRF = 0x7c000000, - PPC_MTMSR = 0x7c000000, - PPC_STDX = 0x7c000000, - PPC_STWCX_ = 0x7c000000, - PPC_STWX = 0x7c00012e, - PPC_MTMSRD = 0x7c000000, - PPC_STDUX = 0x7c000000, - PPC_STWUX = 0x7c00016e, - PPC_SUBFZE = 0x7c000000, - PPC_ADDZE = 0x7c000000, - PPC_MTSR = 0x7c000000, - PPC_STDCX_ = 0x7c000000, - PPC_STBX = 0x7c0001ae, - PPC_SUBFME = 0x7c000000, - PPC_MULLD = 0x7c000000, -//------------ - PPC_ADDME = 0x7c000000, - PPC_MULLW = 0x7c0001d6, - PPC_MTSRIN = 0x7c000000, - PPC_DCBTST = 0x7c000000, - PPC_STBUX = 0x7c000000, - PPC_ADD = 0x7c000214, - PPC_DCBT = 0x7c000000, - PPC_LHZX = 0x7c00022e, - PPC_EQV = 0x7c000000, - PPC_TLBIE = 0x7c000000, - PPC_ECIWX = 0x7c000000, - PPC_LHZUX = 0x7c000000, - PPC_XOR = 0x7c000278, - PPC_MFSPR = 0x7c0002a6, - PPC_LWAX = 0x7c000000, - PPC_LHAX = 0x7c000000, - PPC_TLBIA = 0x7c000000, - PPC_MFTB = 0x7c000000, - PPC_LWAUX = 0x7c000000, - PPC_LHAUX = 0x7c000000, - PPC_STHX = 0x7c00032e, - PPC_ORC = 0x7c000338, - PPC_SRADI = 0x7c000000, - PPC_SLBIE = 0x7c000000, - PPC_ECOWX = 0x7c000000, - PPC_STHUX = 0x7c000000, - PPC_OR = 0x7c000378, - PPC_DIVDU = 0x7c000000, - PPC_DIVWU = 0x7c000396, - PPC_MTSPR = 0x7c0003a6, - PPC_DCBI = 0x7c000000, - PPC_NAND = 0x7c000000, - PPC_DIVD = 0x7c000000, -//------------ - PPC_DIVW = 0x7c0003d6, - PPC_SLBIA = 0x7c000000, - PPC_MCRXR = 0x7c000000, - PPC_LSWX = 0x7c000000, - PPC_LWBRX = 0x7c000000, - PPC_LFSX = 0x7c000000, - PPC_SRW = 0x7c000430, - PPC_SRD = 0x7c000000, - PPC_TLBSYNC = 0x7c000000, - PPC_LFSUX = 0x7c000000, - PPC_MFSR = 0x7c000000, - PPC_LSWI = 0x7c000000, - PPC_SYNC = 0x7c000000, - PPC_LFDX = 0x7c000000, - PPC_LFDUX = 0x7c000000, - PPC_MFSRIN = 0x7c000000, - PPC_STSWX = 0x7c000000, - PPC_STWBRX = 0x7c000000, - PPC_STFSX = 0x7c000000, - PPC_STFSUX = 0x7c000000, - PPC_STSWI = 0x7c000000, - PPC_STFDX = 0x7c000000, - PPC_DCBA = 0x7c000000, - PPC_STFDUX = 0x7c000000, - PPC_LHBRX = 0x7c000000, - PPC_SRAW = 0x7c000630, - PPC_SRAD = 0x7c000000, - PPC_SRAWI = 0x7c000000, - PPC_EIEIO = 0x7c000000, - PPC_STHBRX = 0x7c000000, - PPC_EXTSH = 0x7c000734, - PPC_EXTSB = 0x7c000774, - PPC_ICBI = 0x7c000000, -//------------ - PPC_STFIWX = 0x7c0007ae, - PPC_EXTSW = 0x7c000000, - PPC_DCBZ = 0x7c000000, - PPC_LWZ = 0x80000000, - PPC_LWZU = 0x84000000, - PPC_LBZ = 0x88000000, - PPC_LBZU = 0x8c000000, - PPC_STW = 0x90000000, - PPC_STWU = 0x94000000, - PPC_STB = 0x98000000, - PPC_STBU = 0x9c000000, - PPC_LHZ = 0xa0000000, - PPC_LHZU = 0xa4000000, - PPC_LHA = 0xa8000000, - PPC_LHAU = 0xac000000, - PPC_STH = 0xb0000000, - PPC_STHU = 0xb4000000, - PPC_LMW = 0xb8000000, - PPC_STMW = 0xbc000000, - PPC_LFS = 0xc0000000, - PPC_LFSU = 0xc4000000, - PPC_LFD = 0xc8000000, - PPC_LFDU = 0xcc000000, - PPC_STFS = 0xd0000000, - PPC_STFSU = 0xd4000000, - PPC_STFD = 0xd8000000, - PPC_STFDU = 0xdc000000, - PPC_LD = 0xe8000000, - PPC_LDU = 0xe8000001, - PPC_LWA = 0xe8000002, - PPC_FDIVS = 0xec000024, - PPC_FSUBS = 0xec000028, - PPC_FADDS = 0xec00002a, -//------------ - PPC_FSQRTS = 0xec000000, - PPC_FRES = 0xec000000, - PPC_FMULS = 0xec000032, - PPC_FMSUBS = 0xec000000, - PPC_FMADDS = 0xec000000, - PPC_FNMSUBS = 0xec000000, - PPC_FNMADDS = 0xec000000, - PPC_STD = 0xf8000000, - PPC_STDU = 0xf8000001, - PPC_FCMPU = 0xfc000000, - PPC_FRSP = 0xfc000018, - PPC_FCTIW = 0xfc000000, - PPC_FCTIWZ = 0xfc00001e, - PPC_FDIV = 0xfc000000, - PPC_FSUB = 0xfc000028, - PPC_FADD = 0xfc000000, - PPC_FSQRT = 0xfc000000, - PPC_FSEL = 0xfc000000, - PPC_FMUL = 0xfc000000, - PPC_FRSQRTE = 0xfc000000, - PPC_FMSUB = 0xfc000000, - PPC_FMADD = 0xfc000000, - PPC_FNMSUB = 0xfc000000, - PPC_FNMADD = 0xfc000000, - PPC_FCMPO = 0xfc000000, - PPC_MTFSB1 = 0xfc000000, - PPC_FNEG = 0xfc000050, - PPC_MCRFS = 0xfc000000, - PPC_MTFSB0 = 0xfc000000, - PPC_FMR = 0xfc000000, - PPC_MTFSFI = 0xfc000000, - PPC_FNABS = 0xfc000000, - PPC_FABS = 0xfc000000, -//------------ - PPC_MFFS = 0xfc000000, - PPC_MTFSF = 0xfc000000, - PPC_FCTID = 0xfc000000, - PPC_FCTIDZ = 0xfc000000, - PPC_FCFID = 0xfc000000 - -} ppcOpcodes_t; - - -// the newly generated code -static unsigned *buf; -static int compiledOfs; // in dwords - -// fromt the original bytecode -static byte *code; -static int pc; - -void AsmCall( void ); - -double itofConvert[2]; - -static int Constant4( void ) { - int v; - - v = code[pc] | (code[pc+1]<<8) | (code[pc+2]<<16) | (code[pc+3]<<24); - pc += 4; - return v; -} - -static int Constant1( void ) { - int v; - - v = code[pc]; - pc += 1; - return v; -} - -static void Emit4( int i ) { - buf[ compiledOfs ] = i; - compiledOfs++; -} - -static void Inst( int opcode, int destReg, int aReg, int bReg ) { - unsigned r; - - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) ; - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static void Inst4( int opcode, int destReg, int aReg, int bReg, int cReg ) { - unsigned r; - - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) | ( cReg << 6 ); - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static void InstImm( int opcode, int destReg, int aReg, int immediate ) { - unsigned r; - - if ( immediate > 32767 || immediate < -32768 ) { - Com_Error( ERR_FATAL, "VM_Compile: immediate value %i out of range, opcode %x,%d,%d", immediate, opcode, destReg, aReg ); - } - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( immediate & 0xffff ); - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static void InstImmU( int opcode, int destReg, int aReg, int immediate ) { - unsigned r; - - if ( immediate > 0xffff || immediate < 0 ) { - Com_Error( ERR_FATAL, "VM_Compile: immediate value %i out of range", immediate ); - } - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( immediate & 0xffff ); - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static qboolean rtopped; -static int pop0, pop1, oc0, oc1; -static vm_t *tvm; -static int instruction; -static byte *jused; -static int pass; - -static void ltop() { - if (rtopped == qfalse) { - InstImm( PPC_LWZ, R_TOP, R_OPSTACK, 0 ); // get value from opstack - } -} - -static void ltopandsecond() { - if (pass>=0 && buf[compiledOfs-1] == (PPC_STWU | R_TOP<<21 | R_OPSTACK<<16 | 4 ) && jused[instruction]==0 ) { - compiledOfs--; - if (!pass) { - tvm->instructionPointers[instruction] = compiledOfs * 4; - } - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - } else if (pass>=0 && buf[compiledOfs-1] == (PPC_STW | R_TOP<<21 | R_OPSTACK<<16 | 0 ) && jused[instruction]==0 ) { - compiledOfs--; - if (!pass) { - tvm->instructionPointers[instruction] = compiledOfs * 4; - } - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); - } else { - ltop(); // get value from opstack - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); - } - rtopped = qfalse; -} - -// TJW: Unused -#if 0 -static void fltop() { - if (rtopped == qfalse) { - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - } -} -#endif - -static void fltopandsecond() { - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_LFS, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); - rtopped = qfalse; - return; -} - -/* -================= -VM_Compile -================= -*/ -void VM_Compile( vm_t *vm, vmHeader_t *header ) { - int op; - int maxLength; - int v; - int i; - - // set up the into-to-float variables - ((int *)itofConvert)[0] = 0x43300000; - ((int *)itofConvert)[1] = 0x80000000; - ((int *)itofConvert)[2] = 0x43300000; - - // allocate a very large temp buffer, we will shrink it later - maxLength = header->codeLength * 8; - buf = Z_Malloc( maxLength ); - jused = Z_Malloc(header->instructionCount + 2); - Com_Memset(jused, 0, header->instructionCount+2); - - // compile everything twice, so the second pass will have valid instruction - // pointers for branches - for ( pass = -1 ; pass < 2 ; pass++ ) { - - rtopped = qfalse; - // translate all instructions - pc = 0; - - pop0 = 343545; - pop1 = 2443545; - oc0 = -2343535; - oc1 = 24353454; - tvm = vm; - code = (byte *)header + header->codeOffset; - compiledOfs = 0; -#ifndef __GNUC__ - // metrowerks seems to require this header in front of functions - Emit4( (int)(buf+2) ); - Emit4( 0 ); -#endif - - for ( instruction = 0 ; instruction < header->instructionCount ; instruction++ ) { - if ( compiledOfs*4 > maxLength - 16 ) { - Com_Error( ERR_DROP, "VM_Compile: maxLength exceeded" ); - } - - op = code[ pc ]; - if ( !pass ) { - vm->instructionPointers[ instruction ] = compiledOfs * 4; - } - pc++; - switch ( op ) { - case 0: - break; - case OP_BREAK: - InstImmU( PPC_ADDI, R_TOP, 0, 0 ); - InstImm( PPC_LWZ, R_TOP, R_TOP, 0 ); // *(int *)0 to crash to debugger - rtopped = qfalse; - break; - case OP_ENTER: - InstImm( PPC_ADDI, R_STACK, R_STACK, -Constant4() ); // sub R_STACK, R_STACK, imm - rtopped = qfalse; - break; - case OP_CONST: - v = Constant4(); - if (code[pc] == OP_LOAD4 || code[pc] == OP_LOAD2 || code[pc] == OP_LOAD1) { - v &= vm->dataMask; - } - if ( v < 32768 && v >= -32768 ) { - InstImmU( PPC_ADDI, R_TOP, 0, v & 0xffff ); - } else { - InstImmU( PPC_ADDIS, R_TOP, 0, (v >> 16)&0xffff ); - if ( v & 0xffff ) { - InstImmU( PPC_ORI, R_TOP, R_TOP, v & 0xffff ); - } - } - if (code[pc] == OP_LOAD4) { - Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } else if (code[pc] == OP_LOAD2) { - Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } else if (code[pc] == OP_LOAD1) { - Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } - if (code[pc] == OP_STORE4) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } else if (code[pc] == OP_STORE2) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } else if (code[pc] == OP_STORE1) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } - if (code[pc] == OP_JUMP) { - jused[v] = 1; - } - InstImm( PPC_STWU, R_TOP, R_OPSTACK, 4 ); - rtopped = qtrue; - break; - case OP_LOCAL: - oc0 = oc1; - oc1 = Constant4(); - if (code[pc] == OP_LOAD4 || code[pc] == OP_LOAD2 || code[pc] == OP_LOAD1) { - oc1 &= vm->dataMask; - } - InstImm( PPC_ADDI, R_TOP, R_STACK, oc1 ); - if (code[pc] == OP_LOAD4) { - Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } else if (code[pc] == OP_LOAD2) { - Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } else if (code[pc] == OP_LOAD1) { - Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } - if (code[pc] == OP_STORE4) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } else if (code[pc] == OP_STORE2) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } else if (code[pc] == OP_STORE1) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } - InstImm( PPC_STWU, R_TOP, R_OPSTACK, 4 ); - rtopped = qtrue; - break; - case OP_ARG: - ltop(); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - InstImm( PPC_ADDI, R_EA, R_STACK, Constant1() ); // location to put it - Inst( PPC_STWX, R_TOP, R_EA, R_MEMBASE ); - rtopped = qfalse; - break; - case OP_CALL: - Inst( PPC_MFSPR, R_SECOND, 8, 0 ); // move from link register - InstImm( PPC_STWU, R_SECOND, R_REAL_STACK, -16 ); // save off the old return address - - Inst( PPC_MTSPR, R_ASMCALL, 9, 0 ); // move to count register - Inst( PPC_BCCTR | 1, 20, 0, 0 ); // jump and link to the count register - - InstImm( PPC_LWZ, R_SECOND, R_REAL_STACK, 0 ); // fetch the old return address - InstImm( PPC_ADDI, R_REAL_STACK, R_REAL_STACK, 16 ); - Inst( PPC_MTSPR, R_SECOND, 8, 0 ); // move to link register - rtopped = qfalse; - break; - case OP_PUSH: - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, 4 ); - rtopped = qfalse; - break; - case OP_POP: - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - rtopped = qfalse; - break; - case OP_LEAVE: - InstImm( PPC_ADDI, R_STACK, R_STACK, Constant4() ); // add R_STACK, R_STACK, imm - Inst( PPC_BCLR, 20, 0, 0 ); // branch unconditionally to link register - rtopped = qfalse; - break; - case OP_LOAD4: - ltop(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP ); // mask it - Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); - rtopped = qtrue; - break; - case OP_LOAD2: - ltop(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP ); // mask it - Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); - rtopped = qtrue; - break; - case OP_LOAD1: - ltop(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP ); // mask it - Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); - rtopped = qtrue; - break; - case OP_STORE4: - ltopandsecond(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - rtopped = qfalse; - break; - case OP_STORE2: - ltopandsecond(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - rtopped = qfalse; - break; - case OP_STORE1: - ltopandsecond(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - rtopped = qfalse; - break; - - case OP_EQ: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 4, 2, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (v&0x3ffffff) ); - rtopped = qfalse; - break; - case OP_NE: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 12, 2, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 4, 2, v ); - - rtopped = qfalse; - break; - case OP_LTI: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 4, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 12, 0, v ); - rtopped = qfalse; - break; - case OP_LEI: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 12, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 4, 1, v ); - rtopped = qfalse; - break; - case OP_GTI: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 4, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 12, 1, v ); - rtopped = qfalse; - break; - case OP_GEI: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 12, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 4, 0, v ); - rtopped = qfalse; - break; - case OP_LTU: - ltopandsecond(); // get value from opstack - Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 4, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 12, 0, v ); - rtopped = qfalse; - break; - case OP_LEU: - ltopandsecond(); // get value from opstack - Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 12, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 4, 1, v ); - rtopped = qfalse; - break; - case OP_GTU: - ltopandsecond(); // get value from opstack - Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 4, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 12, 1, v ); - rtopped = qfalse; - break; - case OP_GEU: - ltopandsecond(); // get value from opstack - Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 12, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 4, 0, v ); - rtopped = qfalse; - break; - - case OP_EQF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_TOP, R_SECOND ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 4, 2, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 12, 2, v ); - rtopped = qfalse; - break; - case OP_NEF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_TOP, R_SECOND ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 12, 2, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 4, 2, v ); - rtopped = qfalse; - break; - case OP_LTF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 4, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 12, 0, v ); - rtopped = qfalse; - break; - case OP_LEF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 12, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 4, 1, v ); - rtopped = qfalse; - break; - case OP_GTF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 4, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 12, 1, v ); - rtopped = qfalse; - break; - case OP_GEF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 12, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 4, 0, v ); - rtopped = qfalse; - break; - - case OP_NEGI: - ltop(); // get value from opstack - InstImm( PPC_SUBFIC, R_TOP, R_TOP, 0 ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_ADD: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_ADD, R_TOP, R_TOP, R_SECOND ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_SUB: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_SUBF, R_TOP, R_TOP, R_SECOND ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_DIVI: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_DIVW, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_DIVU: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_DIVWU, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_MODI: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_DIVW, R_EA, R_SECOND, R_TOP ); - Inst( PPC_MULLW, R_EA, R_TOP, R_EA ); - Inst( PPC_SUBF, R_TOP, R_EA, R_SECOND ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_MODU: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_DIVWU, R_EA, R_SECOND, R_TOP ); - Inst( PPC_MULLW, R_EA, R_TOP, R_EA ); - Inst( PPC_SUBF, R_TOP, R_EA, R_SECOND ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_MULI: - case OP_MULU: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_MULLW, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_BAND: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_AND, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_BOR: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_OR, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_BXOR: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_XOR, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_BCOM: - ltop(); // get value from opstack - Inst( PPC_NOR, R_TOP, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_LSH: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_SLW, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_RSHI: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_SRAW, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_RSHU: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_SRW, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - - case OP_NEGF: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - Inst( PPC_FNEG, R_TOP, 0, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - case OP_ADDF: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_FADDS, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - case OP_SUBF: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_FSUBS, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - case OP_DIVF: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_FDIVS, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - case OP_MULF: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst4( PPC_FMULS, R_TOP, R_SECOND, 0, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - - case OP_CVIF: - v = (int)&itofConvert; - InstImmU( PPC_ADDIS, R_EA, 0, (v >> 16)&0xffff ); - InstImmU( PPC_ORI, R_EA, R_EA, v & 0xffff ); - InstImm( PPC_LWZ, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImmU( PPC_XORIS, R_TOP, R_TOP, 0x8000 ); - InstImm( PPC_STW, R_TOP, R_EA, 12 ); - InstImm( PPC_LFD, R_TOP, R_EA, 0 ); - InstImm( PPC_LFD, R_SECOND, R_EA, 8 ); - Inst( PPC_FSUB, R_TOP, R_SECOND, R_TOP ); - // Inst( PPC_FRSP, R_TOP, 0, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - case OP_CVFI: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - Inst( PPC_FCTIWZ, R_TOP, 0, R_TOP ); - Inst( PPC_STFIWX, R_TOP, 0, R_OPSTACK ); // save value to opstack - rtopped = qfalse; - break; - case OP_SEX8: - ltop(); // get value from opstack - Inst( PPC_EXTSB, R_TOP, R_TOP, 0 ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); - rtopped = qtrue; - break; - case OP_SEX16: - ltop(); // get value from opstack - Inst( PPC_EXTSH, R_TOP, R_TOP, 0 ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); - rtopped = qtrue; - break; - - case OP_BLOCK_COPY: - v = Constant4() >> 2; - ltop(); // source - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 ); // dest - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); - InstImmU( PPC_ADDI, R_EA, 0, v ); // count - // FIXME: range check - Inst( PPC_MTSPR, R_EA, 9, 0 ); // move to count register - - Inst( PPC_ADD, R_TOP, R_TOP, R_MEMBASE ); - InstImm( PPC_ADDI, R_TOP, R_TOP, -4 ); - Inst( PPC_ADD, R_SECOND, R_SECOND, R_MEMBASE ); - InstImm( PPC_ADDI, R_SECOND, R_SECOND, -4 ); - - InstImm( PPC_LWZU, R_EA, R_TOP, 4 ); // source - InstImm( PPC_STWU, R_EA, R_SECOND, 4 ); // dest - Inst( PPC_BC | 0xfff8 , 16, 0, 0 ); // loop - rtopped = qfalse; - break; - - case OP_JUMP: - ltop(); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - Inst( PPC_RLWINM | ( 29 << 1 ), R_TOP, R_TOP, 2 ); - // FIXME: range check - Inst( PPC_LWZX, R_TOP, R_TOP, R_INSTRUCTIONS ); - Inst( PPC_MTSPR, R_TOP, 9, 0 ); // move to count register - Inst( PPC_BCCTR, 20, 0, 0 ); // jump to the count register - rtopped = qfalse; - break; - default: - Com_Error( ERR_DROP, "VM_CompilePPC: bad opcode %i at instruction %i, offset %i", op, instruction, pc ); - } - pop0 = pop1; - pop1 = op; - } - - Com_Printf( "VM file %s pass %d compiled to %i bytes of code\n", vm->name, (pass+1), compiledOfs*4 ); - - if ( pass == 0 ) { - // copy to an exact size buffer on the hunk - vm->codeLength = compiledOfs * 4; - vm->codeBase = Hunk_Alloc( vm->codeLength, h_low ); - Com_Memcpy( vm->codeBase, buf, vm->codeLength ); - Z_Free( buf ); - - // offset all the instruction pointers for the new location - for ( i = 0 ; i < header->instructionCount ; i++ ) { - vm->instructionPointers[i] += (int)vm->codeBase; - } - - // go back over it in place now to fixup reletive jump targets - buf = (unsigned *)vm->codeBase; - } - } - Z_Free( jused ); -} - -/* -============== -VM_CallCompiled - -This function is called directly by the generated code -============== -*/ -int VM_CallCompiled( vm_t *vm, int *args ) { - int stack[1024]; - int programStack; - int stackOnEntry; - byte *image; - - currentVM = vm; - - // interpret the code - vm->currentlyInterpreting = qtrue; - - // we might be called recursively, so this might not be the very top - programStack = vm->programStack; - stackOnEntry = programStack; - image = vm->dataBase; - - // set up the stack frame - programStack -= 48; - - *(int *)&image[ programStack + 44] = args[9]; - *(int *)&image[ programStack + 40] = args[8]; - *(int *)&image[ programStack + 36] = args[7]; - *(int *)&image[ programStack + 32] = args[6]; - *(int *)&image[ programStack + 28] = args[5]; - *(int *)&image[ programStack + 24] = args[4]; - *(int *)&image[ programStack + 20] = args[3]; - *(int *)&image[ programStack + 16] = args[2]; - *(int *)&image[ programStack + 12] = args[1]; - *(int *)&image[ programStack + 8 ] = args[0]; - *(int *)&image[ programStack + 4 ] = 0; // return stack - *(int *)&image[ programStack ] = -1; // will terminate the loop on return - - // off we go into generated code... - // the PPC calling standard says the parms will all go into R3 - R11, so - // no special asm code is needed here -#ifdef __GNUC__ - ((void(*)(int, int, int, int, int, int, int, int))(vm->codeBase))( - programStack, (int)&stack, - (int)image, vm->dataMask, (int)&AsmCall, - (int)vm->instructionPointers, vm->instructionPointersLength, - (int)vm ); -#else - ((void(*)(int, int, int, int, int, int, int, int))(vm->codeBase))( - programStack, (int)&stack, - (int)image, vm->dataMask, *(int *)&AsmCall /* skip function pointer header */, - (int)vm->instructionPointers, vm->instructionPointersLength, - (int)vm ); -#endif - vm->programStack = stackOnEntry; - - vm->currentlyInterpreting = qfalse; - - return stack[1]; -} - - -/* -================== -AsmCall - -Put this at end of file because gcc messes up debug line numbers -================== -*/ -#ifdef __GNUC__ - -void AsmCall( void ) { -asm ( - // pop off the destination instruction -" lwz r12,0(r4) \n" // RG_TOP, 0(RG_OPSTACK) -" addi r4,r4,-4 \n" // RG_OPSTACK, RG_OPSTACK, -4 \n" - - // see if it is a system trap -" cmpwi r12,0 \n" // RG_TOP, 0 \n" -" bc 12,0, systemTrap \n" - - // calling another VM function, so lookup in instructionPointers -" slwi r12,r12,2 \n" // RG_TOP,RG_TOP,2 - // FIXME: range check -" lwzx r12, r8, r12 \n" // RG_TOP, RG_INSTRUCTIONS(RG_TOP) -" mtctr r12 \n" // RG_TOP -); - -#if defined(MACOS_X) && defined(__OPTIMIZE__) - // On Mac OS X, gcc doesn't push a frame when we are optimized, so trying to tear it down results in grave disorder. -#warning Mac OS X optimization on, not popping GCC AsmCall frame -#else - // Mac OS X Server and unoptimized compiles include a GCC AsmCall frame - asm ( -" lwz r1,0(r1) \n" // pop off the GCC AsmCall frame -" lmw r30,-8(r1) \n" -); -#endif - -asm ( -" bcctr 20,0 \n" // when it hits a leave, it will branch to the current link register - - // calling a system trap -"systemTrap: \n" - // convert to positive system call number -" subfic r12,r12,-1 \n" - - // save all our registers, including the current link register -" mflr r13 \n" // RG_SECOND // copy off our link register -" addi r1,r1,-92 \n" // required 24 byets of linkage, 32 bytes of parameter, plus our saves -" stw r3,56(r1) \n" // RG_STACK, -36(REAL_STACK) -" stw r4,60(r1) \n" // RG_OPSTACK, 4(RG_REAL_STACK) -" stw r5,64(r1) \n" // RG_MEMBASE, 8(RG_REAL_STACK) -" stw r6,68(r1) \n" // RG_MEMMASK, 12(RG_REAL_STACK) -" stw r7,72(r1) \n" // RG_ASMCALL, 16(RG_REAL_STACK) -" stw r8,76(r1) \n" // RG_INSTRUCTIONS, 20(RG_REAL_STACK) -" stw r9,80(r1) \n" // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK) -" stw r10,84(r1) \n" // RG_VM, 28(RG_REAL_STACK) -" stw r13,88(r1) \n" // RG_SECOND, 32(RG_REAL_STACK) // link register - - // save the vm stack position to allow recursive VM entry -" addi r13,r3,-4 \n" // RG_TOP, RG_STACK, -4 -" stw r13,0(r10) \n" //RG_TOP, VM_OFFSET_PROGRAM_STACK(RG_VM) - - // save the system call number as the 0th parameter -" add r3,r3,r5 \n" // r3, RG_STACK, RG_MEMBASE // r3 is the first parameter to vm->systemCalls -" stwu r12,4(r3) \n" // RG_TOP, 4(r3) - - // make the system call with the address of all the VM parms as a parameter - // vm->systemCalls( &parms ) -" lwz r12,4(r10) \n" // RG_TOP, VM_OFFSET_SYSTEM_CALL(RG_VM) -" mtctr r12 \n" // RG_TOP -" bcctrl 20,0 \n" -" mr r12,r3 \n" // RG_TOP, r3 - - // pop our saved registers -" lwz r3,56(r1) \n" // RG_STACK, 0(RG_REAL_STACK) -" lwz r4,60(r1) \n" // RG_OPSTACK, 4(RG_REAL_STACK) -" lwz r5,64(r1) \n" // RG_MEMBASE, 8(RG_REAL_STACK) -" lwz r6,68(r1) \n" // RG_MEMMASK, 12(RG_REAL_STACK) -" lwz r7,72(r1) \n" // RG_ASMCALL, 16(RG_REAL_STACK) -" lwz r8,76(r1) \n" // RG_INSTRUCTIONS, 20(RG_REAL_STACK) -" lwz r9,80(r1) \n" // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK) -" lwz r10,84(r1) \n" // RG_VM, 28(RG_REAL_STACK) -" lwz r13,88(r1) \n" // RG_SECOND, 32(RG_REAL_STACK) -" addi r1,r1,92 \n" // RG_REAL_STACK, RG_REAL_STACK, 36 - - // restore the old link register -" mtlr r13 \n" // RG_SECOND - - // save off the return value -" stwu r12,4(r4) \n" // RG_TOP, 0(RG_OPSTACK) - - // GCC adds its own prolog / epilog code - ); -} -#else - -// codewarrior version - -void asm AsmCall( void ) { - - // pop off the destination instruction - - lwz r12,0(r4) // RG_TOP, 0(RG_OPSTACK) - - addi r4,r4,-4 // RG_OPSTACK, RG_OPSTACK, -4 - - - - // see if it is a system trap - - cmpwi r12,0 // RG_TOP, 0 - - bc 12,0, systemTrap - - - - // calling another VM function, so lookup in instructionPointers - - slwi r12,r12,2 // RG_TOP,RG_TOP,2 - - // FIXME: range check - - lwzx r12, r8, r12 // RG_TOP, RG_INSTRUCTIONS(RG_TOP) - - mtctr r12 // RG_TOP - - - - bcctr 20,0 // when it hits a leave, it will branch to the current link register - - - - // calling a system trap - -systemTrap: - - // convert to positive system call number - - subfic r12,r12,-1 - - - - // save all our registers, including the current link register - - mflr r13 // RG_SECOND // copy off our link register - - addi r1,r1,-92 // required 24 byets of linkage, 32 bytes of parameter, plus our saves - - stw r3,56(r1) // RG_STACK, -36(REAL_STACK) - - stw r4,60(r1) // RG_OPSTACK, 4(RG_REAL_STACK) - - stw r5,64(r1) // RG_MEMBASE, 8(RG_REAL_STACK) - - stw r6,68(r1) // RG_MEMMASK, 12(RG_REAL_STACK) - - stw r7,72(r1) // RG_ASMCALL, 16(RG_REAL_STACK) - - stw r8,76(r1) // RG_INSTRUCTIONS, 20(RG_REAL_STACK) - - stw r9,80(r1) // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK) - - stw r10,84(r1) // RG_VM, 28(RG_REAL_STACK) - - stw r13,88(r1) // RG_SECOND, 32(RG_REAL_STACK) // link register - - - - // save the vm stack position to allow recursive VM entry - - addi r13,r3,-4 // RG_TOP, RG_STACK, -4 - - stw r13,0(r10) //RG_TOP, VM_OFFSET_PROGRAM_STACK(RG_VM) - - - - // save the system call number as the 0th parameter - - add r3,r3,r5 // r3, RG_STACK, RG_MEMBASE // r3 is the first parameter to vm->systemCalls - - stwu r12,4(r3) // RG_TOP, 4(r3) - - - - // make the system call with the address of all the VM parms as a parameter - - // vm->systemCalls( &parms ) - - lwz r12,4(r10) // RG_TOP, VM_OFFSET_SYSTEM_CALL(RG_VM) - - - - // perform macos cross fragment fixup crap - - lwz r9,0(r12) - - stw r2,52(r1) // save old TOC - - lwz r2,4(r12) - - - - mtctr r9 // RG_TOP - - bcctrl 20,0 - - - - lwz r2,52(r1) // restore TOC - - - - mr r12,r3 // RG_TOP, r3 - - - - // pop our saved registers - - lwz r3,56(r1) // RG_STACK, 0(RG_REAL_STACK) - - lwz r4,60(r1) // RG_OPSTACK, 4(RG_REAL_STACK) - - lwz r5,64(r1) // RG_MEMBASE, 8(RG_REAL_STACK) - - lwz r6,68(r1) // RG_MEMMASK, 12(RG_REAL_STACK) - - lwz r7,72(r1) // RG_ASMCALL, 16(RG_REAL_STACK) - - lwz r8,76(r1) // RG_INSTRUCTIONS, 20(RG_REAL_STACK) - - lwz r9,80(r1) // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK) - - lwz r10,84(r1) // RG_VM, 28(RG_REAL_STACK) - - lwz r13,88(r1) // RG_SECOND, 32(RG_REAL_STACK) - - addi r1,r1,92 // RG_REAL_STACK, RG_REAL_STACK, 36 - - - - // restore the old link register - - mtlr r13 // RG_SECOND - - - - // save off the return value - - stwu r12,4(r4) // RG_TOP, 0(RG_OPSTACK) - - - - blr - -} - - - - -#endif diff --git a/tools/quake3/bspc/deps/qcommon/vm_ppc_new.c b/tools/quake3/bspc/deps/qcommon/vm_ppc_new.c deleted file mode 100644 index 83c77db1..00000000 --- a/tools/quake3/bspc/deps/qcommon/vm_ppc_new.c +++ /dev/null @@ -1,2119 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// vm_ppc.c -// ppc dynamic compiler - -#include "vm_local.h" - -#pragma opt_pointer_analysis off - -#define DEBUG_VM 0 - -#if DEBUG_VM -static char *opnames[256] = { - "OP_UNDEF", - - "OP_IGNORE", - - "OP_BREAK", - - "OP_ENTER", - "OP_LEAVE", - "OP_CALL", - "OP_PUSH", - "OP_POP", - - "OP_CONST", - - "OP_LOCAL", - - "OP_JUMP", - - //------------------- - - "OP_EQ", - "OP_NE", - - "OP_LTI", - "OP_LEI", - "OP_GTI", - "OP_GEI", - - "OP_LTU", - "OP_LEU", - "OP_GTU", - "OP_GEU", - - "OP_EQF", - "OP_NEF", - - "OP_LTF", - "OP_LEF", - "OP_GTF", - "OP_GEF", - - //------------------- - - "OP_LOAD1", - "OP_LOAD2", - "OP_LOAD4", - "OP_STORE1", - "OP_STORE2", - "OP_STORE4", - "OP_ARG", - - "OP_BLOCK_COPY", - - //------------------- - - "OP_SEX8", - "OP_SEX16", - - "OP_NEGI", - "OP_ADD", - "OP_SUB", - "OP_DIVI", - "OP_DIVU", - "OP_MODI", - "OP_MODU", - "OP_MULI", - "OP_MULU", - - "OP_BAND", - "OP_BOR", - "OP_BXOR", - "OP_BCOM", - - "OP_LSH", - "OP_RSHI", - "OP_RSHU", - - "OP_NEGF", - "OP_ADDF", - "OP_SUBF", - "OP_DIVF", - "OP_MULF", - - "OP_CVIF", - "OP_CVFI" -}; -#endif - -typedef enum { - R_REAL_STACK = 1, - // registers 3-11 are the parameter passing registers - - // state - R_STACK = 3, // local - R_OPSTACK, // global - - // constants - R_MEMBASE, // global - R_MEMMASK, - R_ASMCALL, // global - R_INSTRUCTIONS, // global - R_NUM_INSTRUCTIONS, // global - R_CVM, // currentVM - - // temps - R_TOP = 11, - R_SECOND = 12, - R_EA = 2 // effective address calculation - -} regNums_t; - -#define RG_REAL_STACK r1 -#define RG_STACK r3 -#define RG_OPSTACK r4 -#define RG_MEMBASE r5 -#define RG_MEMMASK r6 -#define RG_ASMCALL r7 -#define RG_INSTRUCTIONS r8 -#define RG_NUM_INSTRUCTIONS r9 -#define RG_CVM r10 -#define RG_TOP r12 -#define RG_SECOND r13 -#define RG_EA r14 - -// The deepest value I saw in the Quake3 games was 9. -#define OP_STACK_MAX_DEPTH 12 - -// These are all volatile and thus must be saved -// upon entry to the VM code. -static int opStackIntRegisters[OP_STACK_MAX_DEPTH] = -{ - 16, 17, 18, 19, - 20, 21, 22, 23, - 24, 25, 26, 27 -}; - -static unsigned int *opStackLoadInstructionAddr[OP_STACK_MAX_DEPTH]; - -// We use different registers for the floating point -// operand stack (these are volatile in the PPC ABI) -static int opStackFloatRegisters[OP_STACK_MAX_DEPTH] = -{ - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11 -}; - -static int opStackRegType[OP_STACK_MAX_DEPTH] = -{ - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0 -}; - -// this doesn't have the low order bits set for instructions i'm not using... -typedef enum { - PPC_TDI = 0x08000000, - PPC_TWI = 0x0c000000, - PPC_MULLI = 0x1c000000, - PPC_SUBFIC = 0x20000000, - PPC_CMPI = 0x28000000, - PPC_CMPLI = 0x2c000000, - PPC_ADDIC = 0x30000000, - PPC_ADDIC_ = 0x34000000, - PPC_ADDI = 0x38000000, - PPC_ADDIS = 0x3c000000, - PPC_BC = 0x40000000, - PPC_SC = 0x44000000, - PPC_B = 0x48000000, - - PPC_MCRF = 0x4c000000, - PPC_BCLR = 0x4c000020, - PPC_RFID = 0x4c000000, - PPC_CRNOR = 0x4c000000, - PPC_RFI = 0x4c000000, - PPC_CRANDC = 0x4c000000, - PPC_ISYNC = 0x4c000000, - PPC_CRXOR = 0x4c000000, - PPC_CRNAND = 0x4c000000, - PPC_CREQV = 0x4c000000, - PPC_CRORC = 0x4c000000, - PPC_CROR = 0x4c000000, -//------------ - PPC_BCCTR = 0x4c000420, - PPC_RLWIMI = 0x50000000, - PPC_RLWINM = 0x54000000, - PPC_RLWNM = 0x5c000000, - PPC_ORI = 0x60000000, - PPC_ORIS = 0x64000000, - PPC_XORI = 0x68000000, - PPC_XORIS = 0x6c000000, - PPC_ANDI_ = 0x70000000, - PPC_ANDIS_ = 0x74000000, - PPC_RLDICL = 0x78000000, - PPC_RLDICR = 0x78000000, - PPC_RLDIC = 0x78000000, - PPC_RLDIMI = 0x78000000, - PPC_RLDCL = 0x78000000, - PPC_RLDCR = 0x78000000, - PPC_CMP = 0x7c000000, - PPC_TW = 0x7c000000, - PPC_SUBFC = 0x7c000010, - PPC_MULHDU = 0x7c000000, - PPC_ADDC = 0x7c000014, - PPC_MULHWU = 0x7c000000, - PPC_MFCR = 0x7c000000, - PPC_LWAR = 0x7c000000, - PPC_LDX = 0x7c000000, - PPC_LWZX = 0x7c00002e, - PPC_SLW = 0x7c000030, - PPC_CNTLZW = 0x7c000000, - PPC_SLD = 0x7c000000, - PPC_AND = 0x7c000038, - PPC_CMPL = 0x7c000040, - PPC_SUBF = 0x7c000050, - PPC_LDUX = 0x7c000000, -//------------ - PPC_DCBST = 0x7c000000, - PPC_LWZUX = 0x7c00006c, - PPC_CNTLZD = 0x7c000000, - PPC_ANDC = 0x7c000000, - PPC_TD = 0x7c000000, - PPC_MULHD = 0x7c000000, - PPC_MULHW = 0x7c000000, - PPC_MTSRD = 0x7c000000, - PPC_MFMSR = 0x7c000000, - PPC_LDARX = 0x7c000000, - PPC_DCBF = 0x7c000000, - PPC_LBZX = 0x7c0000ae, - PPC_NEG = 0x7c000000, - PPC_MTSRDIN = 0x7c000000, - PPC_LBZUX = 0x7c000000, - PPC_NOR = 0x7c0000f8, - PPC_SUBFE = 0x7c000000, - PPC_ADDE = 0x7c000000, - PPC_MTCRF = 0x7c000000, - PPC_MTMSR = 0x7c000000, - PPC_STDX = 0x7c000000, - PPC_STWCX_ = 0x7c000000, - PPC_STWX = 0x7c00012e, - PPC_MTMSRD = 0x7c000000, - PPC_STDUX = 0x7c000000, - PPC_STWUX = 0x7c00016e, - PPC_SUBFZE = 0x7c000000, - PPC_ADDZE = 0x7c000000, - PPC_MTSR = 0x7c000000, - PPC_STDCX_ = 0x7c000000, - PPC_STBX = 0x7c0001ae, - PPC_SUBFME = 0x7c000000, - PPC_MULLD = 0x7c000000, -//------------ - PPC_ADDME = 0x7c000000, - PPC_MULLW = 0x7c0001d6, - PPC_MTSRIN = 0x7c000000, - PPC_DCBTST = 0x7c000000, - PPC_STBUX = 0x7c000000, - PPC_ADD = 0x7c000214, - PPC_DCBT = 0x7c000000, - PPC_LHZX = 0x7c00022e, - PPC_EQV = 0x7c000000, - PPC_TLBIE = 0x7c000000, - PPC_ECIWX = 0x7c000000, - PPC_LHZUX = 0x7c000000, - PPC_XOR = 0x7c000278, - PPC_MFSPR = 0x7c0002a6, - PPC_LWAX = 0x7c000000, - PPC_LHAX = 0x7c000000, - PPC_TLBIA = 0x7c000000, - PPC_MFTB = 0x7c000000, - PPC_LWAUX = 0x7c000000, - PPC_LHAUX = 0x7c000000, - PPC_STHX = 0x7c00032e, - PPC_ORC = 0x7c000338, - PPC_SRADI = 0x7c000000, - PPC_SLBIE = 0x7c000000, - PPC_ECOWX = 0x7c000000, - PPC_STHUX = 0x7c000000, - PPC_OR = 0x7c000378, - PPC_DIVDU = 0x7c000000, - PPC_DIVWU = 0x7c000396, - PPC_MTSPR = 0x7c0003a6, - PPC_DCBI = 0x7c000000, - PPC_NAND = 0x7c000000, - PPC_DIVD = 0x7c000000, -//------------ - PPC_DIVW = 0x7c0003d6, - PPC_SLBIA = 0x7c000000, - PPC_MCRXR = 0x7c000000, - PPC_LSWX = 0x7c000000, - PPC_LWBRX = 0x7c000000, - PPC_LFSX = 0x7c00042e, - PPC_SRW = 0x7c000430, - PPC_SRD = 0x7c000000, - PPC_TLBSYNC = 0x7c000000, - PPC_LFSUX = 0x7c000000, - PPC_MFSR = 0x7c000000, - PPC_LSWI = 0x7c000000, - PPC_SYNC = 0x7c000000, - PPC_LFDX = 0x7c000000, - PPC_LFDUX = 0x7c000000, - PPC_MFSRIN = 0x7c000000, - PPC_STSWX = 0x7c000000, - PPC_STWBRX = 0x7c000000, - PPC_STFSX = 0x7c00052e, - PPC_STFSUX = 0x7c000000, - PPC_STSWI = 0x7c000000, - PPC_STFDX = 0x7c000000, - PPC_DCBA = 0x7c000000, - PPC_STFDUX = 0x7c000000, - PPC_LHBRX = 0x7c000000, - PPC_SRAW = 0x7c000630, - PPC_SRAD = 0x7c000000, - PPC_SRAWI = 0x7c000000, - PPC_EIEIO = 0x7c000000, - PPC_STHBRX = 0x7c000000, - PPC_EXTSH = 0x7c000734, - PPC_EXTSB = 0x7c000774, - PPC_ICBI = 0x7c000000, -//------------ - PPC_STFIWX = 0x7c0007ae, - PPC_EXTSW = 0x7c000000, - PPC_DCBZ = 0x7c000000, - PPC_LWZ = 0x80000000, - PPC_LWZU = 0x84000000, - PPC_LBZ = 0x88000000, - PPC_LBZU = 0x8c000000, - PPC_STW = 0x90000000, - PPC_STWU = 0x94000000, - PPC_STB = 0x98000000, - PPC_STBU = 0x9c000000, - PPC_LHZ = 0xa0000000, - PPC_LHZU = 0xa4000000, - PPC_LHA = 0xa8000000, - PPC_LHAU = 0xac000000, - PPC_STH = 0xb0000000, - PPC_STHU = 0xb4000000, - PPC_LMW = 0xb8000000, - PPC_STMW = 0xbc000000, - PPC_LFS = 0xc0000000, - PPC_LFSU = 0xc4000000, - PPC_LFD = 0xc8000000, - PPC_LFDU = 0xcc000000, - PPC_STFS = 0xd0000000, - PPC_STFSU = 0xd4000000, - PPC_STFD = 0xd8000000, - PPC_STFDU = 0xdc000000, - PPC_LD = 0xe8000000, - PPC_LDU = 0xe8000001, - PPC_LWA = 0xe8000002, - PPC_FDIVS = 0xec000024, - PPC_FSUBS = 0xec000028, - PPC_FADDS = 0xec00002a, -//------------ - PPC_FSQRTS = 0xec000000, - PPC_FRES = 0xec000000, - PPC_FMULS = 0xec000032, - PPC_FMSUBS = 0xec000000, - PPC_FMADDS = 0xec000000, - PPC_FNMSUBS = 0xec000000, - PPC_FNMADDS = 0xec000000, - PPC_STD = 0xf8000000, - PPC_STDU = 0xf8000001, - PPC_FCMPU = 0xfc000000, - PPC_FRSP = 0xfc000018, - PPC_FCTIW = 0xfc000000, - PPC_FCTIWZ = 0xfc00001e, - PPC_FDIV = 0xfc000000, - PPC_FSUB = 0xfc000028, - PPC_FADD = 0xfc000000, - PPC_FSQRT = 0xfc000000, - PPC_FSEL = 0xfc000000, - PPC_FMUL = 0xfc000000, - PPC_FRSQRTE = 0xfc000000, - PPC_FMSUB = 0xfc000000, - PPC_FMADD = 0xfc000000, - PPC_FNMSUB = 0xfc000000, - PPC_FNMADD = 0xfc000000, - PPC_FCMPO = 0xfc000000, - PPC_MTFSB1 = 0xfc000000, - PPC_FNEG = 0xfc000050, - PPC_MCRFS = 0xfc000000, - PPC_MTFSB0 = 0xfc000000, - PPC_FMR = 0xfc000000, - PPC_MTFSFI = 0xfc000000, - PPC_FNABS = 0xfc000000, - PPC_FABS = 0xfc000000, -//------------ - PPC_MFFS = 0xfc000000, - PPC_MTFSF = 0xfc000000, - PPC_FCTID = 0xfc000000, - PPC_FCTIDZ = 0xfc000000, - PPC_FCFID = 0xfc000000 - -} ppcOpcodes_t; - - -// the newly generated code -static unsigned *buf; -static int compiledOfs; // in dwords -static int pass; - -// fromt the original bytecode -static byte *code; -static int pc; - -void AsmCall( void ); - -double itofConvert[2]; - -static int Constant4( void ) { - int v; - - v = code[pc] | (code[pc+1]<<8) | (code[pc+2]<<16) | (code[pc+3]<<24); - pc += 4; - return v; -} - -static int Constant1( void ) { - int v; - - v = code[pc]; - pc += 1; - return v; -} - -static void Emit4( char *opname, int i ) { - #if DEBUG_VM - if(pass == 1) - printf("\t\t\t%p %s\t%08lx\n",&buf[compiledOfs],opname,i&0x3ffffff); - #endif - buf[ compiledOfs ] = i; - compiledOfs++; -} - -static void Inst( char *opname, int opcode, int destReg, int aReg, int bReg ) { - unsigned r; - - #if DEBUG_VM - if(pass == 1) - printf("\t\t\t%p %s\tr%d,r%d,r%d\n",&buf[compiledOfs],opname,destReg,aReg,bReg); - #endif - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) ; - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static void Inst4( char *opname, int opcode, int destReg, int aReg, int bReg, int cReg ) { - unsigned r; - - #if DEBUG_VM - if(pass == 1) - printf("\t\t\t%p %s\tr%d,r%d,r%d,r%d\n",&buf[compiledOfs],opname,destReg,aReg,bReg,cReg); - #endif - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) | ( cReg << 6 ); - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static void InstImm( char *opname, int opcode, int destReg, int aReg, int immediate ) { - unsigned r; - - if ( immediate > 32767 || immediate < -32768 ) { - Com_Error( ERR_FATAL, "VM_Compile: immediate value %i out of range, opcode %x,%d,%d", immediate, opcode, destReg, aReg ); - } - #if DEBUG_VM - if(pass == 1) - printf("\t\t\t%p %s\tr%d,r%d,0x%x\n",&buf[compiledOfs],opname,destReg,aReg,immediate); - #endif - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( immediate & 0xffff ); - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static void InstImmU( char *opname, int opcode, int destReg, int aReg, int immediate ) { - unsigned r; - - if ( immediate > 0xffff || immediate < 0 ) { - Com_Error( ERR_FATAL, "VM_Compile: immediate value %i out of range", immediate ); - } - #if DEBUG_VM - if(pass == 1) - printf("\t\t\t%p %s\tr%d,r%d,0x%x\n",&buf[compiledOfs],opname,destReg,aReg,immediate); - #endif - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( immediate & 0xffff ); - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static int pop0, pop1, oc0, oc1; -static vm_t *tvm; -static int instruction; -static byte *jused; - -static void ltop() { -// if (rtopped == qfalse) { -// InstImm( PPC_LWZ, R_TOP, R_OPSTACK, 0 ); // get value from opstack -// } -} - -static void ltopandsecond() { -#if 0 - if (pass>=0 && buf[compiledOfs-1] == (PPC_STWU | R_TOP<<21 | R_OPSTACK<<16 | 4 ) && jused[instruction]==0 ) { - compiledOfs--; - if (!pass) { - tvm->instructionPointers[instruction] = compiledOfs * 4; - } - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - } else if (pass>=0 && buf[compiledOfs-1] == (PPC_STW | R_TOP<<21 | R_OPSTACK<<16 | 0 ) && jused[instruction]==0 ) { - compiledOfs--; - if (!pass) { - tvm->instructionPointers[instruction] = compiledOfs * 4; - } - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); - } else { - ltop(); // get value from opstack - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); - } - rtopped = qfalse; -#endif -} - -static void spillOpStack(int depth) -{ - // Store out each register on the operand stack to it's correct location. - int i; - - for(i = 0; i < depth; i++) - { - assert(opStackRegType[i]); - assert(opStackRegType[i] == 1); - switch(opStackRegType[i]) - { - case 1: // Integer register - InstImm( "stw", PPC_STW, opStackIntRegisters[i], R_OPSTACK, i*4+4); - break; - case 2: // Float register - InstImm( "stfs", PPC_STFS, opStackFloatRegisters[i], R_OPSTACK, i*4+4); - break; - } - opStackRegType[i] = 0; - } -} - -static void loadOpStack(int depth) -{ - // Back off operand stack pointer and reload all operands. -// InstImm( "addi", PPC_ADDI, R_OPSTACK, R_OPSTACK, -(depth)*4 ); - - int i; - - for(i = 0; i < depth; i++) - { - assert(opStackRegType[i] == 0); - // For now we're stuck reloading everything as an integer. - opStackLoadInstructionAddr[i] = &buf[compiledOfs]; - InstImm( "lwz", PPC_LWZ, opStackIntRegisters[i], R_OPSTACK, i*4+4); - opStackRegType[i] = 1; - } -} - -static void makeInteger(int depth) -{ - // This should really never be necessary... - assert(opStackRegType[depth] == 1); - //assert(opStackRegType[depth] == 2); - if(opStackRegType[depth] == 2) - { - unsigned instruction; - assert(opStackLoadInstructionAddr[depth]); - - printf("patching float load at %p to int load\n",opStackLoadInstructionAddr[depth]); - // Repatch load instruction to use LFS instead of LWZ - instruction = *opStackLoadInstructionAddr[depth]; - instruction &= ~PPC_LFSX; - instruction |= PPC_LWZX; - *opStackLoadInstructionAddr[depth] = instruction; - opStackLoadInstructionAddr[depth] = 0; - opStackRegType[depth] = 1; - #if 0 - InstImm( "stfs", PPC_STFS, opStackFloatRegisters[depth], R_OPSTACK, depth*4+4); - // For XXX make sure we force enough NOPs to get the load into - // another dispatch group to avoid pipeline flush. - Inst( "ori", PPC_ORI, 0, 0, 0 ); - Inst( "ori", PPC_ORI, 0, 0, 0 ); - Inst( "ori", PPC_ORI, 0, 0, 0 ); - Inst( "ori", PPC_ORI, 0, 0, 0 ); - InstImm( "lwz", PPC_LWZ, opStackIntRegisters[depth], R_OPSTACK, depth*4+4); - opStackRegType[depth] = 1; - #endif - } -} - -static void makeFloat(int depth) -{ - //assert(opStackRegType[depth] == 1); - if(opStackRegType[depth] == 1) - { - unsigned instruction; - unsigned destReg, aReg, bReg, imm; - - if(opStackLoadInstructionAddr[depth]) - { - // Repatch load instruction to use LFS instead of LWZ - instruction = *opStackLoadInstructionAddr[depth]; - // Figure out if it's LWZ or LWZX - if((instruction & 0xfc000000) == PPC_LWZ) - { - //printf("patching LWZ at %p to LFS at depth %ld\n",opStackLoadInstructionAddr[depth],depth); - //printf("old instruction: %08lx\n",instruction); - // Extract registers - destReg = (instruction >> 21) & 31; - aReg = (instruction >> 16) & 31; - imm = instruction & 0xffff; - - // Calculate correct FP register to use. - // THIS ASSUMES REGISTER USAGE FOR THE STACK IS n, n+1, n+2, etc! - //printf("old dest: %ld\n",destReg); - destReg = (destReg - opStackIntRegisters[0]) + opStackFloatRegisters[0]; - instruction = PPC_LFS | ( destReg << 21 ) | ( aReg << 16 ) | imm ; - //printf("new dest: %ld\n",destReg); - //printf("new instruction: %08lx\n",instruction); - } - else - { - //printf("patching LWZX at %p to LFSX at depth %ld\n",opStackLoadInstructionAddr[depth],depth); - //printf("old instruction: %08lx\n",instruction); - // Extract registers - destReg = (instruction >> 21) & 31; - aReg = (instruction >> 16) & 31; - bReg = (instruction >> 11) & 31; - // Calculate correct FP register to use. - // THIS ASSUMES REGISTER USAGE FOR THE STACK IS n, n+1, n+2, etc! - //printf("old dest: %ld\n",destReg); - destReg = (destReg - opStackIntRegisters[0]) + opStackFloatRegisters[0]; - instruction = PPC_LFSX | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) ; - //printf("new dest: %ld\n",destReg); - //printf("new instruction: %08lx\n",instruction); - } - *opStackLoadInstructionAddr[depth] = instruction; - opStackLoadInstructionAddr[depth] = 0; - } - else - { - //printf("doing float constant load at %p for depth %ld\n",&buf[compiledOfs],depth); - // It was likely loaded as a constant so we have to save/load it. A more - // interesting implementation might be to generate code to do a "PC relative" - // load from the VM code region. - InstImm( "stw", PPC_STW, opStackIntRegisters[depth], R_OPSTACK, depth*4+4); - // For XXX make sure we force enough NOPs to get the load into - // another dispatch group to avoid pipeline flush. - Inst( "ori", PPC_ORI, 0, 0, 0 ); - Inst( "ori", PPC_ORI, 0, 0, 0 ); - Inst( "ori", PPC_ORI, 0, 0, 0 ); - Inst( "ori", PPC_ORI, 0, 0, 0 ); - InstImm( "lfs", PPC_LFS, opStackFloatRegisters[depth], R_OPSTACK, depth*4+4); - } - opStackRegType[depth] = 2; - } -} - -// TJW: Unused -#if 0 -static void fltop() { - if (rtopped == qfalse) { - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - } -} -#endif - -#if 0 -static void fltopandsecond() { - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_LFS, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); - rtopped = qfalse; - return; -} -#endif - -#define assertInteger(depth) assert(opStackRegType[depth] == 1) - -/* -================= -VM_Compile -================= -*/ -void VM_Compile( vm_t *vm, vmHeader_t *header ) { - int op; - int maxLength; - int v; - int i; - int opStackDepth; - - int mainFunction; - - // set up the into-to-float variables - ((int *)itofConvert)[0] = 0x43300000; - ((int *)itofConvert)[1] = 0x80000000; - ((int *)itofConvert)[2] = 0x43300000; - - // allocate a very large temp buffer, we will shrink it later - maxLength = header->codeLength * 8; - buf = Z_Malloc( maxLength ); - jused = Z_Malloc(header->instructionCount + 2); - Com_Memset(jused, 0, header->instructionCount+2); - - // compile everything twice, so the second pass will have valid instruction - // pointers for branches - for ( pass = -1 ; pass < 2 ; pass++ ) { - - // translate all instructions - pc = 0; - mainFunction = 0; - opStackDepth = 0; - - pop0 = 343545; - pop1 = 2443545; - oc0 = -2343535; - oc1 = 24353454; - tvm = vm; - code = (byte *)header + header->codeOffset; - compiledOfs = 0; -#ifndef __GNUC__ - // metrowerks seems to require this header in front of functions - Emit4( (int)(buf+2) ); - Emit4( 0 ); -#endif - - for ( instruction = 0 ; instruction < header->instructionCount ; instruction++ ) { - if ( compiledOfs*4 > maxLength - 16 ) { - Com_Error( ERR_DROP, "VM_Compile: maxLength exceeded" ); - } - - op = code[ pc ]; - if ( !pass ) { - vm->instructionPointers[ instruction ] = compiledOfs * 4; - } - pc++; - switch ( op ) { - case 0: - break; - case OP_BREAK: - #if DEBUG_VM - if(pass == 1) - printf("%08lx BREAK\n",instruction); - #endif - InstImmU( "addi", PPC_ADDI, R_TOP, 0, 0 ); - InstImm( "lwz", PPC_LWZ, R_TOP, R_TOP, 0 ); // *(int *)0 to crash to debugger - break; - case OP_ENTER: - opStackDepth = 0; - v = Constant4(); - #if DEBUG_VM - if(pass == 1) - printf("%08x ENTER\t%04x\n",instruction,v); - #endif - opStackRegType[opStackDepth] = 0; - mainFunction++; - if(mainFunction == 1) - { - // Main VM entry point is the first thing we compile, so save off operand stack - // registers here. This avoids issues with trying to trick the native compiler - // into doing it, and properly matches the PowerPC ABI - InstImm( "addi", PPC_ADDI, R_REAL_STACK, R_REAL_STACK, -OP_STACK_MAX_DEPTH*4 ); // sub R_STACK, R_STACK, imm - for(i = 0; i < OP_STACK_MAX_DEPTH; i++) - InstImm( "stw", PPC_STW, opStackIntRegisters[i], R_REAL_STACK, i*4); - } - InstImm( "addi", PPC_ADDI, R_STACK, R_STACK, -v ); // sub R_STACK, R_STACK, imm - break; - case OP_CONST: - v = Constant4(); - #if DEBUG_VM - if(pass == 1) - printf("%08x CONST\t%08x\n",instruction,v); - #endif - opStackLoadInstructionAddr[opStackDepth] = 0; - if ( v < 32768 && v >= -32768 ) { - InstImmU( "addi", PPC_ADDI, opStackIntRegisters[opStackDepth], 0, v & 0xffff ); - } else { - InstImmU( "addis", PPC_ADDIS, opStackIntRegisters[opStackDepth], 0, (v >> 16)&0xffff ); - if ( v & 0xffff ) { - InstImmU( "ori", PPC_ORI, opStackIntRegisters[opStackDepth], opStackIntRegisters[opStackDepth], v & 0xffff ); - } - } - opStackRegType[opStackDepth] = 1; - opStackDepth += 1; - if (code[pc] == OP_JUMP) { - jused[v] = 1; - } - break; - case OP_LOCAL: - oc1 = Constant4(); - #if DEBUG_VM - if(pass == 1) - printf("%08x LOCAL\t%08x\n",instruction,oc1); - #endif - if (code[pc] == OP_LOAD4 || code[pc] == OP_LOAD2 || code[pc] == OP_LOAD1) { - oc1 &= vm->dataMask; - } - InstImm( "addi", PPC_ADDI, opStackIntRegisters[opStackDepth], R_STACK, oc1 ); - opStackRegType[opStackDepth] = 1; - opStackLoadInstructionAddr[opStackDepth] = 0; - opStackDepth += 1; - break; - case OP_ARG: - v = Constant1(); - #if DEBUG_VM - if(pass == 1) - printf("%08x ARG \t%08x\n",instruction,v); - #endif - InstImm( "addi", PPC_ADDI, R_EA, R_STACK, v ); // location to put it - if(opStackRegType[opStackDepth-1] == 1) - Inst( "stwx", PPC_STWX, opStackIntRegisters[opStackDepth-1], R_EA, R_MEMBASE ); - else - Inst( "stfsx", PPC_STFSX, opStackFloatRegisters[opStackDepth-1], R_EA, R_MEMBASE ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - - break; - case OP_CALL: - #if DEBUG_VM - if(pass == 1) - printf("%08x CALL\n",instruction); - #endif - assertInteger(opStackDepth-1); - assert(opStackDepth > 0); - Inst( "mflr", PPC_MFSPR, R_SECOND, 8, 0 ); // move from link register - InstImm( "stwu", PPC_STWU, R_SECOND, R_REAL_STACK, -16 ); // save off the old return address - - // Spill operand stack registers. - spillOpStack(opStackDepth); - - // We need to leave R_OPSTACK pointing to the top entry on the stack, which is the call address. - // It will be consumed (and R4 decremented) by the AsmCall code. - InstImm( "addi", PPC_ADDI, R_OPSTACK, R_OPSTACK, opStackDepth*4); - - Inst( "mtctr", PPC_MTSPR, R_ASMCALL, 9, 0 ); // move to count register - Inst( "bctrl", PPC_BCCTR | 1, 20, 0, 0 ); // jump and link to the count register - - // R4 now points to the top of the operand stack, which has the return value in it. We want to - // back off the pointer to point to the base of our local operand stack and then reload the stack. - - InstImm("addi", PPC_ADDI, R_OPSTACK, R_OPSTACK, -opStackDepth*4); - - // Reload operand stack. - loadOpStack(opStackDepth); - - InstImm( "lwz", PPC_LWZ, R_SECOND, R_REAL_STACK, 0 ); // fetch the old return address - InstImm( "addi", PPC_ADDI, R_REAL_STACK, R_REAL_STACK, 16 ); - Inst( "mtlr", PPC_MTSPR, R_SECOND, 8, 0 ); // move to link register - break; - case OP_PUSH: - #if DEBUG_VM - if(pass == 1) - printf("%08x PUSH\n",instruction); - #endif - opStackRegType[opStackDepth] = 1; // Garbage int value. - opStackDepth += 1; - break; - case OP_POP: - #if DEBUG_VM - if(pass == 1) - printf("%08x POP\n",instruction); - #endif - opStackDepth -= 1; - opStackRegType[opStackDepth] = 0; // ?? - opStackLoadInstructionAddr[opStackDepth-1] = 0; - break; - case OP_LEAVE: - #if DEBUG_VM - if(pass == 1) - printf("%08x LEAVE\n",instruction); - #endif - assert(opStackDepth == 1); - assert(opStackRegType[0] != 0); - // Save return value onto top of op stack. We also have to increment R_OPSTACK - switch(opStackRegType[0]) - { - case 1: // Integer register - InstImm( "stw", PPC_STWU, opStackIntRegisters[0], R_OPSTACK, 4); - break; - case 2: // Float register - InstImm( "stfs", PPC_STFSU, opStackFloatRegisters[0], R_OPSTACK, 4); - break; - } - InstImm( "addi", PPC_ADDI, R_STACK, R_STACK, Constant4() ); // add R_STACK, R_STACK, imm - if(mainFunction == 1) - { - for(i = 0; i < OP_STACK_MAX_DEPTH; i++) - InstImm( "lwz", PPC_LWZ, opStackIntRegisters[i], R_REAL_STACK, i*4); - InstImm( "addi", PPC_ADDI, R_REAL_STACK, R_REAL_STACK, OP_STACK_MAX_DEPTH*4 ); - } - opStackDepth--; - opStackRegType[opStackDepth] = 0; - opStackLoadInstructionAddr[opStackDepth] = 0; - Inst( "blr", PPC_BCLR, 20, 0, 0 ); // branch unconditionally to link register - break; - case OP_LOAD4: - #if DEBUG_VM - if(pass == 1) - printf("%08x LOAD4\n",instruction); - #endif - // We should try to figure out whether to use LWZX or LFSX based - // on some kind of code analysis after subsequent passes. I think what - // we could do is store the compiled load instruction address along with - // the register type. When we hit the first mismatched operator, we go back - // and patch the load. Since LCC's operand stack should be at 0 depth by the - // time we hit a branch, this should work fairly well. FIXME FIXME FIXME. - assertInteger(opStackDepth-1); - opStackLoadInstructionAddr[opStackDepth-1] = &buf[ compiledOfs ]; - Inst( "lwzx", PPC_LWZX, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], R_MEMBASE );// load from memory base - opStackRegType[opStackDepth-1] = 1; - break; - case OP_LOAD2: - #if DEBUG_VM - if(pass == 1) - printf("%08x LOAD2\n",instruction); - #endif - assertInteger(opStackDepth-1); - opStackLoadInstructionAddr[opStackDepth-1] = 0; - Inst( "lhzx", PPC_LHZX, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], R_MEMBASE );// load from memory base - opStackRegType[opStackDepth-1] = 1; - break; - case OP_LOAD1: - #if DEBUG_VM - if(pass == 1) - printf("%08x LOAD1\n",instruction); - #endif - assertInteger(opStackDepth-1); - opStackLoadInstructionAddr[opStackDepth-1] = 0; - Inst( "lbzx", PPC_LBZX, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], R_MEMBASE );// load from memory base - opStackRegType[opStackDepth-1] = 1; - break; - case OP_STORE4: - #if DEBUG_VM - if(pass == 1) - printf("%08x STORE4\n",instruction); - #endif - assertInteger(opStackDepth-2); - if(opStackRegType[opStackDepth-1] == 1) - Inst( "stwx", PPC_STWX, opStackIntRegisters[opStackDepth-1], - opStackIntRegisters[opStackDepth-2], R_MEMBASE ); // store from memory base - else - Inst( "stfsx", PPC_STFSX, opStackFloatRegisters[opStackDepth-1], - opStackIntRegisters[opStackDepth-2], R_MEMBASE ); // store from memory base - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - break; - case OP_STORE2: - #if DEBUG_VM - if(pass == 1) - printf("%08x STORE2\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "sthx", PPC_STHX, opStackIntRegisters[opStackDepth-1], - opStackIntRegisters[opStackDepth-2], R_MEMBASE ); // store from memory base - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - break; - case OP_STORE1: - #if DEBUG_VM - if(pass == 1) - printf("%08x STORE1\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "stbx", PPC_STBX, opStackIntRegisters[opStackDepth-1], - opStackIntRegisters[opStackDepth-2], R_MEMBASE ); // store from memory base - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - break; - - case OP_EQ: - #if DEBUG_VM - if(pass == 1) - printf("%08x EQ\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 4, 2, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (v&0x3ffffff) ); - break; - case OP_NE: - #if DEBUG_VM - if(pass == 1) - printf("%08x NE\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 12, 2, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 4, 2, v ); - - break; - case OP_LTI: - #if DEBUG_VM - if(pass == 1) - printf("%08x LTI\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 4, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 12, 0, v ); - break; - case OP_LEI: - #if DEBUG_VM - if(pass == 1) - printf("%08x LEI\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 12, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 4, 1, v ); - break; - case OP_GTI: - #if DEBUG_VM - if(pass == 1) - printf("%08x GTI\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 4, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 12, 1, v ); - break; - case OP_GEI: - #if DEBUG_VM - if(pass == 1) - printf("%08x GEI\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 12, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 4, 0, v ); - break; - case OP_LTU: - #if DEBUG_VM - if(pass == 1) - printf("%08x LTU\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 4, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 12, 0, v ); - break; - case OP_LEU: - #if DEBUG_VM - if(pass == 1) - printf("%08x LEU\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 12, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 4, 1, v ); - break; - case OP_GTU: - #if DEBUG_VM - if(pass == 1) - printf("%08x GTU\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 4, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 12, 1, v ); - break; - case OP_GEU: - #if DEBUG_VM - if(pass == 1) - printf("%08x GEU\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 12, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 4, 0, v ); - break; - - case OP_EQF: - #if DEBUG_VM - if(pass == 1) - printf("%08x EQF\n",instruction); - #endif - makeFloat(opStackDepth-1); - makeFloat(opStackDepth-2); - Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 4, 2, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 12, 2, v ); - break; - case OP_NEF: - #if DEBUG_VM - if(pass == 1) - printf("%08x NEF\n",instruction); - #endif - makeFloat(opStackDepth-1); - makeFloat(opStackDepth-2); - Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 12, 2, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 4, 2, v ); - break; - case OP_LTF: - #if DEBUG_VM - if(pass == 1) - printf("%08x LTF\n",instruction); - #endif - makeFloat(opStackDepth-1); - makeFloat(opStackDepth-2); - Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 4, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 12, 0, v ); - break; - case OP_LEF: - #if DEBUG_VM - if(pass == 1) - printf("%08x LEF\n",instruction); - #endif - makeFloat(opStackDepth-1); - makeFloat(opStackDepth-2); - Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 12, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 4, 1, v ); - break; - case OP_GTF: - #if DEBUG_VM - if(pass == 1) - printf("%08x GTF\n",instruction); - #endif - makeFloat(opStackDepth-1); - makeFloat(opStackDepth-2); - Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 4, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 12, 1, v ); - break; - case OP_GEF: - #if DEBUG_VM - if(pass == 1) - printf("%08x GEF\n",instruction); - #endif - makeFloat(opStackDepth-1); - makeFloat(opStackDepth-2); - Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - i = Constant4(); - jused[i] = 1; - InstImm( "bc", PPC_BC, 12, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( "bc", PPC_BC, 4, 0, v ); - break; - - case OP_NEGI: - #if DEBUG_VM - if(pass == 1) - printf("%08x NEGI\n",instruction); - #endif - assertInteger(opStackDepth-1); - InstImm( "subfic", PPC_SUBFIC, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], 0 ); - opStackLoadInstructionAddr[opStackDepth-1] = 0; - break; - case OP_ADD: - #if DEBUG_VM - if(pass == 1) - printf("%08x ADD\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "add", PPC_ADD, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-2] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_SUB: - #if DEBUG_VM - if(pass == 1) - printf("%08x SUB\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "subf", PPC_SUBF, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-2] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_DIVI: - #if DEBUG_VM - if(pass == 1) - printf("%08x DIVI\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "divw", PPC_DIVW, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_DIVU: - #if DEBUG_VM - if(pass == 1) - printf("%08x DIVU\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "divwu", PPC_DIVWU, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_MODI: - #if DEBUG_VM - if(pass == 1) - printf("%08x MODI\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "divw", PPC_DIVW, R_EA, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - Inst( "mullw", PPC_MULLW, R_EA, opStackIntRegisters[opStackDepth-1], R_EA ); - Inst( "subf", PPC_SUBF, opStackIntRegisters[opStackDepth-2], R_EA, opStackIntRegisters[opStackDepth-2] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_MODU: - #if DEBUG_VM - if(pass == 1) - printf("%08x MODU\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "divwu", PPC_DIVWU, R_EA, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - Inst( "mullw", PPC_MULLW, R_EA, opStackIntRegisters[opStackDepth-1], R_EA ); - Inst( "subf", PPC_SUBF, opStackIntRegisters[opStackDepth-2], R_EA, opStackIntRegisters[opStackDepth-2] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_MULI: - case OP_MULU: - #if DEBUG_VM - if(pass == 1) - printf("%08x MULI\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "mullw", PPC_MULLW, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-2] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_BAND: - #if DEBUG_VM - if(pass == 1) - printf("%08x BAND\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "and", PPC_AND, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_BOR: - #if DEBUG_VM - if(pass == 1) - printf("%08x BOR\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "or", PPC_OR, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_BXOR: - #if DEBUG_VM - if(pass == 1) - printf("%08x BXOR\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "xor", PPC_XOR, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_BCOM: - #if DEBUG_VM - if(pass == 1) - printf("%08x BCOM\n",instruction); - #endif - assertInteger(opStackDepth-1); - Inst( "nor", PPC_NOR, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1] ); - opStackLoadInstructionAddr[opStackDepth-1] = 0; - break; - case OP_LSH: - #if DEBUG_VM - if(pass == 1) - printf("%08x LSH\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "slw", PPC_SLW, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_RSHI: - #if DEBUG_VM - if(pass == 1) - printf("%08x RSHI\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "sraw", PPC_SRAW, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_RSHU: - #if DEBUG_VM - if(pass == 1) - printf("%08x RSHU\n",instruction); - #endif - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - Inst( "srw", PPC_SRW, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - - case OP_NEGF: - #if DEBUG_VM - if(pass == 1) - printf("%08x NEGF\n",instruction); - #endif - makeFloat(opStackDepth-1); - Inst( "fneg", PPC_FNEG, opStackFloatRegisters[opStackDepth-1], 0, opStackFloatRegisters[opStackDepth-1] ); - opStackLoadInstructionAddr[opStackDepth-1] = 0; - break; - case OP_ADDF: - #if DEBUG_VM - if(pass == 1) - printf("%08x ADDF\n",instruction); - #endif - makeFloat(opStackDepth-1); - makeFloat(opStackDepth-2); - Inst( "fadds", PPC_FADDS, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_SUBF: - #if DEBUG_VM - if(pass == 1) - printf("%08x SUBF\n",instruction); - #endif - makeFloat(opStackDepth-1); - makeFloat(opStackDepth-2); - Inst( "fsubs", PPC_FSUBS, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_DIVF: - #if DEBUG_VM - if(pass == 1) - printf("%08x DIVF\n",instruction); - #endif - makeFloat(opStackDepth-1); - makeFloat(opStackDepth-2); - Inst( "fdivs", PPC_FDIVS, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - case OP_MULF: - #if DEBUG_VM - if(pass == 1) - printf("%08x MULF\n",instruction); - #endif - makeFloat(opStackDepth-1); - makeFloat(opStackDepth-2); - Inst4( "fmuls", PPC_FMULS, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-2], 0, opStackFloatRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - - case OP_CVIF: - #if DEBUG_VM - if(pass == 1) - printf("%08x CVIF\n",instruction); - #endif - assertInteger(opStackDepth-1); - //makeInteger(opStackDepth-1); - v = (int)&itofConvert; - InstImmU( "addis", PPC_ADDIS, R_EA, 0, (v >> 16)&0xffff ); - InstImmU( "ori", PPC_ORI, R_EA, R_EA, v & 0xffff ); - InstImmU( "xoris", PPC_XORIS, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], 0x8000 ); - InstImm( "stw", PPC_STW, opStackIntRegisters[opStackDepth-1], R_EA, 12 ); - InstImm( "lfd", PPC_LFD, opStackFloatRegisters[opStackDepth-1], R_EA, 0 ); - Inst( "ori", PPC_ORI, 0, 0, 0); - Inst( "ori", PPC_ORI, 0, 0, 0); - Inst( "ori", PPC_ORI, 0, 0, 0); - InstImm( "lfd", PPC_LFD, 13, R_EA, 8 ); - Inst( "fsub", PPC_FSUB, opStackFloatRegisters[opStackDepth-1], 13, opStackFloatRegisters[opStackDepth-1] ); - opStackRegType[opStackDepth-1] = 2; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - // Inst( PPC_FRSP, R_TOP, 0, R_TOP ); - break; - case OP_CVFI: - #if DEBUG_VM - if(pass == 1) - printf("%08x CVFI\n",instruction); - #endif - makeFloat(opStackDepth-1); - - InstImm( "addi", PPC_ADDI, R_OPSTACK, R_OPSTACK, opStackDepth*4); - - Inst( "fctiwz", PPC_FCTIWZ, opStackFloatRegisters[opStackDepth-1], 0, opStackFloatRegisters[opStackDepth-1] ); - Inst( "stfiwx", PPC_STFIWX, opStackFloatRegisters[opStackDepth-1], 0, R_OPSTACK ); // save value to opstack (dummy area now) - Inst( "ori", PPC_ORI, 0, 0, 0); - Inst( "ori", PPC_ORI, 0, 0, 0); - Inst( "ori", PPC_ORI, 0, 0, 0); - Inst( "ori", PPC_ORI, 0, 0, 0); - InstImm( "lwz", PPC_LWZ, opStackIntRegisters[opStackDepth-1], R_OPSTACK, 0 ); - - InstImm( "addi", PPC_ADDI, R_OPSTACK, R_OPSTACK, -opStackDepth*4); - - opStackRegType[opStackDepth-1] = 1; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - break; - case OP_SEX8: - #if DEBUG_VM - if(pass == 1) - printf("%08x SEX8\n",instruction); - #endif - assertInteger(opStackDepth-1); - Inst( "extsb", PPC_EXTSB, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], 0 ); - opStackLoadInstructionAddr[opStackDepth-1] = 0; - break; - case OP_SEX16: - #if DEBUG_VM - if(pass == 1) - printf("%08x SEX16\n",instruction); - #endif - assertInteger(opStackDepth-1); - Inst( "extsh", PPC_EXTSH, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], 0 ); - opStackLoadInstructionAddr[opStackDepth-1] = 0; - break; - - case OP_BLOCK_COPY: - v = Constant4() >> 2; - #if DEBUG_VM - if(pass == 1) - printf("%08x BLOCK_COPY\t%08lx\n",instruction,v<<2); - #endif - assert(opStackDepth >= 2); - assertInteger(opStackDepth-1); - assertInteger(opStackDepth-2); - InstImmU( "addi", PPC_ADDI, R_EA, 0, v ); // count - // FIXME: range check - Inst( "mtctr", PPC_MTSPR, R_EA, 9, 0 ); // move to count register - - Inst( "add", PPC_ADD, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], R_MEMBASE ); - InstImm( "addi", PPC_ADDI, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], -4 ); - Inst( "add", PPC_ADD, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], R_MEMBASE ); - InstImm( "addi", PPC_ADDI, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], -4 ); - - InstImm( "lwzu", PPC_LWZU, R_EA, opStackIntRegisters[opStackDepth-1], 4 ); // source - InstImm( "stwu", PPC_STWU, R_EA, opStackIntRegisters[opStackDepth-2], 4 ); // dest - Inst( "b", PPC_BC | 0xfff8 , 16, 0, 0 ); // loop - opStackRegType[opStackDepth-1] = 0; - opStackRegType[opStackDepth-2] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-2] = 0; - opStackDepth -= 2; - break; - - case OP_JUMP: - #if DEBUG_VM - if(pass == 1) - printf("%08x JUMP\n",instruction); - #endif - assert(opStackDepth == 1); - assertInteger(opStackDepth-1); - - Inst( "rlwinm", PPC_RLWINM | ( 29 << 1 ), opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], 2 ); - // FIXME: range check - Inst( "lwzx", PPC_LWZX, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], R_INSTRUCTIONS ); - Inst( "mtctr", PPC_MTSPR, opStackIntRegisters[opStackDepth-1], 9, 0 ); // move to count register - Inst( "bctr", PPC_BCCTR, 20, 0, 0 ); // jump to the count register - opStackRegType[opStackDepth-1] = 0; - opStackLoadInstructionAddr[opStackDepth-1] = 0; - opStackDepth -= 1; - break; - default: - Com_Error( ERR_DROP, "VM_CompilePPC: bad opcode %i at instruction %i, offset %i", op, instruction, pc ); - } - pop0 = pop1; - pop1 = op; - assert(opStackDepth >= 0); - assert(opStackDepth < OP_STACK_MAX_DEPTH); - - //printf("%4d\t%s\n",opStackDepth,opnames[op]); - } - - Com_Printf( "VM file %s pass %d compiled to %i bytes of code\n", vm->name, (pass+1), compiledOfs*4 ); - - if ( pass == 0 ) { - // copy to an exact size buffer on the hunk - vm->codeLength = compiledOfs * 4; - vm->codeBase = Hunk_Alloc( vm->codeLength, h_low ); - Com_Memcpy( vm->codeBase, buf, vm->codeLength ); - - //printf("codeBase: %p\n",vm->codeBase); - - Z_Free( buf ); - - // offset all the instruction pointers for the new location - for ( i = 0 ; i < header->instructionCount ; i++ ) { - vm->instructionPointers[i] += (int)vm->codeBase; - //printf("%08x %08lx\n",i,vm->instructionPointers[i]); - } - - // go back over it in place now to fixup reletive jump targets - buf = (unsigned *)vm->codeBase; - } - } - if(0) - { - char buf[256]; - printf("wait..\n"); - gets(buf); - } - Z_Free( jused ); -} - -/* -============== -VM_CallCompiled - -This function is called directly by the generated code -============== -*/ -int VM_CallCompiled( vm_t *vm, int *args ) { - int stack[1024]; - int programStack; - int stackOnEntry; - byte *image; - - currentVM = vm; - - //printf("VM_CallCompiled: %p %08lx %08lx %08lx\n", - // vm, args[0],args[1],args[2]); - - // interpret the code - vm->currentlyInterpreting = qtrue; - - // we might be called recursively, so this might not be the very top - programStack = vm->programStack; - stackOnEntry = programStack; - image = vm->dataBase; - - // set up the stack frame - programStack -= 48; - - *(int *)&image[ programStack + 44] = args[9]; - *(int *)&image[ programStack + 40] = args[8]; - *(int *)&image[ programStack + 36] = args[7]; - *(int *)&image[ programStack + 32] = args[6]; - *(int *)&image[ programStack + 28] = args[5]; - *(int *)&image[ programStack + 24] = args[4]; - *(int *)&image[ programStack + 20] = args[3]; - *(int *)&image[ programStack + 16] = args[2]; - *(int *)&image[ programStack + 12] = args[1]; - *(int *)&image[ programStack + 8 ] = args[0]; - *(int *)&image[ programStack + 4 ] = 0; // return stack - *(int *)&image[ programStack ] = -1; // will terminate the loop on return - - // Cheesy... manually save registers used by VM call... - // off we go into generated code... - // the PPC calling standard says the parms will all go into R3 - R11, so - // no special asm code is needed here -#ifdef __GNUC__ - ((void(*)(int, int, int, int, int, int, int, int))(vm->codeBase))( - programStack, (int)&stack, - (int)image, vm->dataMask, (int)&AsmCall, - (int)vm->instructionPointers, vm->instructionPointersLength, - (int)vm ); -#else - ((void(*)(int, int, int, int, int, int, int, int))(vm->codeBase))( - programStack, (int)&stack, - (int)image, vm->dataMask, *(int *)&AsmCall /* skip function pointer header */, - (int)vm->instructionPointers, vm->instructionPointersLength, - (int)vm ); -#endif - vm->programStack = stackOnEntry; - - vm->currentlyInterpreting = qfalse; - - return stack[1]; -} - - -/* -================== -AsmCall - -Put this at end of file because gcc messes up debug line numbers -================== -*/ -#ifdef __GNUC__ - -void AsmCall( void ) { -asm ( - // pop off the destination instruction -" lwz r12,0(r4) \n" // RG_TOP, 0(RG_OPSTACK) -" addi r4,r4,-4 \n" // RG_OPSTACK, RG_OPSTACK, -4 \n" - - // see if it is a system trap -" cmpwi r12,0 \n" // RG_TOP, 0 \n" -" bc 12,0, systemTrap \n" - - // calling another VM function, so lookup in instructionPointers -" slwi r12,r12,2 \n" // RG_TOP,RG_TOP,2 - // FIXME: range check -" lwzx r12, r8, r12 \n" // RG_TOP, RG_INSTRUCTIONS(RG_TOP) -" mtctr r12 \n" // RG_TOP -); - -#if defined(MACOS_X) && defined(__OPTIMIZE__) - // On Mac OS X, gcc doesn't push a frame when we are optimized, so trying to tear it down results in grave disorder. -#warning Mac OS X optimization on, not popping GCC AsmCall frame -#else - // Mac OS X Server and unoptimized compiles include a GCC AsmCall frame - asm ( -" lwz r1,0(r1) \n" // pop off the GCC AsmCall frame -" lmw r30,-8(r1) \n" -); -#endif - -asm ( -" bcctr 20,0 \n" // when it hits a leave, it will branch to the current link register - - // calling a system trap -"systemTrap: \n" - // convert to positive system call number -" subfic r12,r12,-1 \n" - - // save all our registers, including the current link register -" mflr r13 \n" // RG_SECOND // copy off our link register -" addi r1,r1,-92 \n" // required 24 byets of linkage, 32 bytes of parameter, plus our saves -" stw r3,56(r1) \n" // RG_STACK, -36(REAL_STACK) -" stw r4,60(r1) \n" // RG_OPSTACK, 4(RG_REAL_STACK) -" stw r5,64(r1) \n" // RG_MEMBASE, 8(RG_REAL_STACK) -" stw r6,68(r1) \n" // RG_MEMMASK, 12(RG_REAL_STACK) -" stw r7,72(r1) \n" // RG_ASMCALL, 16(RG_REAL_STACK) -" stw r8,76(r1) \n" // RG_INSTRUCTIONS, 20(RG_REAL_STACK) -" stw r9,80(r1) \n" // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK) -" stw r10,84(r1) \n" // RG_VM, 28(RG_REAL_STACK) -" stw r13,88(r1) \n" // RG_SECOND, 32(RG_REAL_STACK) // link register - - // save the vm stack position to allow recursive VM entry -" addi r13,r3,-4 \n" // RG_TOP, RG_STACK, -4 -" stw r13,0(r10) \n" //RG_TOP, VM_OFFSET_PROGRAM_STACK(RG_VM) - - // save the system call number as the 0th parameter -" add r3,r3,r5 \n" // r3, RG_STACK, RG_MEMBASE // r3 is the first parameter to vm->systemCalls -" stwu r12,4(r3) \n" // RG_TOP, 4(r3) - - // make the system call with the address of all the VM parms as a parameter - // vm->systemCalls( &parms ) -" lwz r12,4(r10) \n" // RG_TOP, VM_OFFSET_SYSTEM_CALL(RG_VM) -" mtctr r12 \n" // RG_TOP -" bcctrl 20,0 \n" -" mr r12,r3 \n" // RG_TOP, r3 - - // pop our saved registers -" lwz r3,56(r1) \n" // RG_STACK, 0(RG_REAL_STACK) -" lwz r4,60(r1) \n" // RG_OPSTACK, 4(RG_REAL_STACK) -" lwz r5,64(r1) \n" // RG_MEMBASE, 8(RG_REAL_STACK) -" lwz r6,68(r1) \n" // RG_MEMMASK, 12(RG_REAL_STACK) -" lwz r7,72(r1) \n" // RG_ASMCALL, 16(RG_REAL_STACK) -" lwz r8,76(r1) \n" // RG_INSTRUCTIONS, 20(RG_REAL_STACK) -" lwz r9,80(r1) \n" // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK) -" lwz r10,84(r1) \n" // RG_VM, 28(RG_REAL_STACK) -" lwz r13,88(r1) \n" // RG_SECOND, 32(RG_REAL_STACK) -" addi r1,r1,92 \n" // RG_REAL_STACK, RG_REAL_STACK, 36 - - // restore the old link register -" mtlr r13 \n" // RG_SECOND - - // save off the return value -" stwu r12,4(r4) \n" // RG_TOP, 0(RG_OPSTACK) - - // GCC adds its own prolog / epliog code - ); -} -#else - -// codewarrior version - -void asm AsmCall( void ) { - - // pop off the destination instruction - - lwz r12,0(r4) // RG_TOP, 0(RG_OPSTACK) - - addi r4,r4,-4 // RG_OPSTACK, RG_OPSTACK, -4 - - - - // see if it is a system trap - - cmpwi r12,0 // RG_TOP, 0 - - bc 12,0, systemTrap - - - - // calling another VM function, so lookup in instructionPointers - - slwi r12,r12,2 // RG_TOP,RG_TOP,2 - - // FIXME: range check - - lwzx r12, r8, r12 // RG_TOP, RG_INSTRUCTIONS(RG_TOP) - - mtctr r12 // RG_TOP - - - - bcctr 20,0 // when it hits a leave, it will branch to the current link register - - - - // calling a system trap - -systemTrap: - - // convert to positive system call number - - subfic r12,r12,-1 - - - - // save all our registers, including the current link register - - mflr r13 // RG_SECOND // copy off our link register - - addi r1,r1,-92 // required 24 byets of linkage, 32 bytes of parameter, plus our saves - - stw r3,56(r1) // RG_STACK, -36(REAL_STACK) - - stw r4,60(r1) // RG_OPSTACK, 4(RG_REAL_STACK) - - stw r5,64(r1) // RG_MEMBASE, 8(RG_REAL_STACK) - - stw r6,68(r1) // RG_MEMMASK, 12(RG_REAL_STACK) - - stw r7,72(r1) // RG_ASMCALL, 16(RG_REAL_STACK) - - stw r8,76(r1) // RG_INSTRUCTIONS, 20(RG_REAL_STACK) - - stw r9,80(r1) // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK) - - stw r10,84(r1) // RG_VM, 28(RG_REAL_STACK) - - stw r13,88(r1) // RG_SECOND, 32(RG_REAL_STACK) // link register - - - - // save the vm stack position to allow recursive VM entry - - addi r13,r3,-4 // RG_TOP, RG_STACK, -4 - - stw r13,0(r10) //RG_TOP, VM_OFFSET_PROGRAM_STACK(RG_VM) - - - - // save the system call number as the 0th parameter - - add r3,r3,r5 // r3, RG_STACK, RG_MEMBASE // r3 is the first parameter to vm->systemCalls - - stwu r12,4(r3) // RG_TOP, 4(r3) - - - - // make the system call with the address of all the VM parms as a parameter - - // vm->systemCalls( &parms ) - - lwz r12,4(r10) // RG_TOP, VM_OFFSET_SYSTEM_CALL(RG_VM) - - - - // perform macos cross fragment fixup crap - - lwz r9,0(r12) - - stw r2,52(r1) // save old TOC - - lwz r2,4(r12) - - - - mtctr r9 // RG_TOP - - bcctrl 20,0 - - - - lwz r2,52(r1) // restore TOC - - - - mr r12,r3 // RG_TOP, r3 - - - - // pop our saved registers - - lwz r3,56(r1) // RG_STACK, 0(RG_REAL_STACK) - - lwz r4,60(r1) // RG_OPSTACK, 4(RG_REAL_STACK) - - lwz r5,64(r1) // RG_MEMBASE, 8(RG_REAL_STACK) - - lwz r6,68(r1) // RG_MEMMASK, 12(RG_REAL_STACK) - - lwz r7,72(r1) // RG_ASMCALL, 16(RG_REAL_STACK) - - lwz r8,76(r1) // RG_INSTRUCTIONS, 20(RG_REAL_STACK) - - lwz r9,80(r1) // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK) - - lwz r10,84(r1) // RG_VM, 28(RG_REAL_STACK) - - lwz r13,88(r1) // RG_SECOND, 32(RG_REAL_STACK) - - addi r1,r1,92 // RG_REAL_STACK, RG_REAL_STACK, 36 - - - - // restore the old link register - - mtlr r13 // RG_SECOND - - - - // save off the return value - - stwu r12,4(r4) // RG_TOP, 0(RG_OPSTACK) - - - - blr - -} - - - - -#endif diff --git a/tools/quake3/bspc/deps/qcommon/vm_sparc.h b/tools/quake3/bspc/deps/qcommon/vm_sparc.h deleted file mode 100644 index dbed6278..00000000 --- a/tools/quake3/bspc/deps/qcommon/vm_sparc.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef VM_SPARC_H -#define VM_SPARC_H - -/* integer regs */ -#define G0 0 -#define G1 1 -#define G2 2 -#define G3 3 -#define G4 4 -#define G5 5 -#define G6 6 -#define G7 7 -#define O0 8 -#define O1 9 -#define O2 10 -#define O3 11 -#define O4 12 -#define O5 13 -#define O6 14 -#define O7 15 -#define L0 16 -#define L1 17 -#define L2 18 -#define L3 19 -#define L4 20 -#define L5 21 -#define L6 22 -#define L7 23 -#define I0 24 -#define I1 25 -#define I2 26 -#define I3 27 -#define I4 28 -#define I5 29 -#define I6 30 -#define I7 31 - -/* float regs */ -#define F0 0 -#define F1 1 -#define F2 2 -#define F3 3 -#define F4 4 -#define F5 5 -#define F6 6 -#define F7 7 -#define F8 8 -#define F9 9 -#define F10 10 -#define F11 11 -#define F12 12 -#define F13 13 -#define F14 14 -#define F15 15 -#define F16 16 -#define F17 17 -#define F18 18 -#define F19 19 -#define F20 20 -#define F21 21 -#define F22 22 -#define F23 23 -#define F24 24 -#define F25 25 -#define F26 26 -#define F27 27 -#define F28 28 -#define F29 29 -#define F30 30 -#define F31 31 - -/* state registers */ -#define Y_REG 0 -#define CCR_REG 2 -#define ASI_REG 3 -#define FPRS_REG 6 - -#endif diff --git a/tools/quake3/bspc/deps/qcommon/vm_x86.c b/tools/quake3/bspc/deps/qcommon/vm_x86.c deleted file mode 100644 index f9b09820..00000000 --- a/tools/quake3/bspc/deps/qcommon/vm_x86.c +++ /dev/null @@ -1,1196 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// vm_x86.c -- load time compiler and execution environment for x86 - -#include "vm_local.h" - -#ifdef __FreeBSD__ // rb0101023 -#include -#endif - -#ifndef _WIN32 -#include // for PROT_ stuff -#endif - -/* - - eax scratch - ebx scratch - ecx scratch (required for shifts) - edx scratch (required for divisions) - esi program stack - edi opstack - -*/ - -// TTimo: initialised the statics, this fixes a crash when entering a compiled VM -static byte *buf = NULL; -static byte *jused = NULL; -static int compiledOfs = 0; -static byte *code = NULL; -static int pc = 0; - -static int *instructionPointers = NULL; - -#define FTOL_PTR - -#ifdef _WIN32 - -#if defined( FTOL_PTR ) -int _ftol( float ); -static int ftolPtr = (int)_ftol; -#endif - -void AsmCall( void ); -static int asmCallPtr = (int)AsmCall; - -#else // _WIN32 - -#if defined( FTOL_PTR ) -// bk001213 - BEWARE: does not work! UI menu etc. broken - stack! -// bk001119 - added: int gftol( float x ) { return (int)x; } - -int qftol( void ); // bk001213 - label, see unix/ftol.nasm -int qftol027F( void ); // bk001215 - fixed FPU control variants -int qftol037F( void ); -int qftol0E7F( void ); // bk010102 - fixed bogus bits (duh) -int qftol0F7F( void ); - - -static int ftolPtr = (int)qftol0F7F; -#endif // FTOL_PTR - -void doAsmCall( void ); -static int asmCallPtr = (int)doAsmCall; -#endif // !_WIN32 - - -static int callMask = 0; // bk001213 - init - -static int instruction, pass; -static int lastConst = 0; -static int oc0, oc1, pop0, pop1; - -typedef enum -{ - LAST_COMMAND_NONE = 0, - LAST_COMMAND_MOV_EDI_EAX, - LAST_COMMAND_SUB_DI_4, - LAST_COMMAND_SUB_DI_8, -} ELastCommand; - -static ELastCommand LastCommand; - -/* -================= -AsmCall -================= -*/ -#ifdef _WIN32 -__declspec( naked ) void AsmCall( void ) { -int programStack; -int *opStack; -int syscallNum; -vm_t* savedVM; - -__asm { - mov eax, dword ptr [edi] - sub edi, 4 - or eax,eax - jl systemCall - // calling another vm function - shl eax,2 - add eax, dword ptr [instructionPointers] - call dword ptr [eax] - mov eax, dword ptr [edi] - and eax, [callMask] - ret -systemCall: - - // convert negative num to system call number - // and store right before the first arg - neg eax - dec eax - - push ebp - mov ebp, esp - sub esp, __LOCAL_SIZE - - mov dword ptr syscallNum, eax // so C code can get at it - mov dword ptr programStack, esi // so C code can get at it - mov dword ptr opStack, edi - - push ecx - push esi // we may call recursively, so the - push edi // statics aren't guaranteed to be around -} - - savedVM = currentVM; - - // save the stack to allow recursive VM entry - currentVM->programStack = programStack - 4; - *(int *)((byte *)currentVM->dataBase + programStack + 4) = syscallNum; -//VM_LogSyscalls( (int *)((byte *)currentVM->dataBase + programStack + 4) ); - *(opStack+1) = currentVM->systemCall( (int *)((byte *)currentVM->dataBase + programStack + 4) ); - - currentVM = savedVM; - -_asm { - pop edi - pop esi - pop ecx - add edi, 4 // we added the return value - - mov esp, ebp - pop ebp - - ret -} - -} - -#else //!_WIN32 - -static int callProgramStack; -static int *callOpStack; -static int callSyscallNum; - -void callAsmCall(void) -{ - vm_t *savedVM; - int *callOpStack2; - - savedVM = currentVM; - callOpStack2 = callOpStack; - - // save the stack to allow recursive VM entry - currentVM->programStack = callProgramStack - 4; - *(int *)((byte *)currentVM->dataBase + callProgramStack + 4) = callSyscallNum; -//VM_LogSyscalls( (int *)((byte *)currentVM->dataBase + programStack + 4) ); - *(callOpStack2+1) = currentVM->systemCall( (int *)((byte *)currentVM->dataBase + callProgramStack + 4) ); - - currentVM = savedVM; -} - -void AsmCall( void ) { - __asm__("doAsmCall: \n\t" \ - " movl (%%edi),%%eax \n\t" \ - " subl $4,%%edi \n\t" \ - " orl %%eax,%%eax \n\t" \ - " jl systemCall \n\t" \ - " shll $2,%%eax \n\t" \ - " addl %3,%%eax \n\t" \ - " call *(%%eax) \n\t" \ - " movl (%%edi),%%eax \n\t" \ - " andl callMask, %%eax \n\t" \ - " jmp doret \n\t" \ - "systemCall: \n\t" \ - " negl %%eax \n\t" \ - " decl %%eax \n\t" \ - " movl %%eax,%0 \n\t" \ - " movl %%esi,%1 \n\t" \ - " movl %%edi,%2 \n\t" \ - " pushl %%ecx \n\t" \ - " pushl %%esi \n\t" \ - " pushl %%edi \n\t" \ - " call callAsmCall \n\t" \ - " popl %%edi \n\t" \ - " popl %%esi \n\t" \ - " popl %%ecx \n\t" \ - " addl $4,%%edi \n\t" \ - "doret: \n\t" \ - " ret \n\t" \ - : "=rm" (callSyscallNum), "=rm" (callProgramStack), "=rm" (callOpStack) \ - : "rm" (instructionPointers) \ - : "ax", "di", "si", "cx" \ - ); -} -#endif - -static int Constant4( void ) { - int v; - - v = code[pc] | (code[pc+1]<<8) | (code[pc+2]<<16) | (code[pc+3]<<24); - pc += 4; - return v; -} - -static int Constant1( void ) { - int v; - - v = code[pc]; - pc += 1; - return v; -} - -static void Emit1( int v ) -{ - buf[ compiledOfs ] = v; - compiledOfs++; - - LastCommand = LAST_COMMAND_NONE; -} - -#if 0 -static void Emit2( int v ) { - Emit1( v & 255 ); - Emit1( ( v >> 8 ) & 255 ); -} -#endif - -static void Emit4( int v ) { - Emit1( v & 255 ); - Emit1( ( v >> 8 ) & 255 ); - Emit1( ( v >> 16 ) & 255 ); - Emit1( ( v >> 24 ) & 255 ); -} - -static int Hex( int c ) { - if ( c >= 'a' && c <= 'f' ) { - return 10 + c - 'a'; - } - if ( c >= 'A' && c <= 'F' ) { - return 10 + c - 'A'; - } - if ( c >= '0' && c <= '9' ) { - return c - '0'; - } - - Com_Error( ERR_DROP, "Hex: bad char '%c'", c ); - - return 0; -} -static void EmitString( const char *string ) { - int c1, c2; - int v; - - while ( 1 ) { - c1 = string[0]; - c2 = string[1]; - - v = ( Hex( c1 ) << 4 ) | Hex( c2 ); - Emit1( v ); - - if ( !string[2] ) { - break; - } - string += 3; - } -} - - - -static void EmitCommand(ELastCommand command) -{ - switch(command) - { - case LAST_COMMAND_MOV_EDI_EAX: - EmitString( "89 07" ); // mov dword ptr [edi], eax - break; - - case LAST_COMMAND_SUB_DI_4: - EmitString( "83 EF 04" ); // sub edi, 4 - break; - - case LAST_COMMAND_SUB_DI_8: - EmitString( "83 EF 08" ); // sub edi, 8 - break; - default: - break; - } - LastCommand = command; -} - -static void EmitAddEDI4(vm_t *vm) { - if (LastCommand == LAST_COMMAND_SUB_DI_4 && jused[instruction-1] == 0) - { // sub di,4 - compiledOfs -= 3; - vm->instructionPointers[ instruction-1 ] = compiledOfs; - return; - } - if (LastCommand == LAST_COMMAND_SUB_DI_8 && jused[instruction-1] == 0) - { // sub di,8 - compiledOfs -= 3; - vm->instructionPointers[ instruction-1 ] = compiledOfs; - EmitString( "83 EF 04" ); // sub edi,4 - return; - } - EmitString( "83 C7 04" ); // add edi,4 -} - -static void EmitMovEAXEDI(vm_t *vm) { - if (LastCommand == LAST_COMMAND_MOV_EDI_EAX) - { // mov [edi], eax - compiledOfs -= 2; - vm->instructionPointers[ instruction-1 ] = compiledOfs; - return; - } - if (pop1 == OP_DIVI || pop1 == OP_DIVU || pop1 == OP_MULI || pop1 == OP_MULU || - pop1 == OP_STORE4 || pop1 == OP_STORE2 || pop1 == OP_STORE1 ) - { - return; - } - if (pop1 == OP_CONST && buf[compiledOfs-6] == 0xC7 && buf[compiledOfs-5] == 0x07 ) - { // mov edi, 0x123456 - compiledOfs -= 6; - vm->instructionPointers[ instruction-1 ] = compiledOfs; - EmitString( "B8" ); // mov eax, 0x12345678 - Emit4( lastConst ); - return; - } - EmitString( "8B 07" ); // mov eax, dword ptr [edi] -} - -qboolean EmitMovEBXEDI(vm_t *vm, int andit) { - if (LastCommand == LAST_COMMAND_MOV_EDI_EAX) - { // mov [edi], eax - compiledOfs -= 2; - vm->instructionPointers[ instruction-1 ] = compiledOfs; - EmitString( "8B D8"); // mov bx, eax - return qfalse; - } - if (pop1 == OP_DIVI || pop1 == OP_DIVU || pop1 == OP_MULI || pop1 == OP_MULU || - pop1 == OP_STORE4 || pop1 == OP_STORE2 || pop1 == OP_STORE1 ) - { - EmitString( "8B D8"); // mov bx, eax - return qfalse; - } - if (pop1 == OP_CONST && buf[compiledOfs-6] == 0xC7 && buf[compiledOfs-5] == 0x07 ) - { // mov edi, 0x123456 - compiledOfs -= 6; - vm->instructionPointers[ instruction-1 ] = compiledOfs; - EmitString( "BB" ); // mov ebx, 0x12345678 - if (andit) { - Emit4( lastConst & andit ); - } else { - Emit4( lastConst ); - } - return qtrue; - } - - EmitString( "8B 1F" ); // mov ebx, dword ptr [edi] - return qfalse; -} - -/* -================= -VM_Compile -================= -*/ -void VM_Compile( vm_t *vm, vmHeader_t *header ) { - int op; - int maxLength; - int v; - int i; - qboolean opt; - - // allocate a very large temp buffer, we will shrink it later - maxLength = header->codeLength * 8; - buf = Z_Malloc( maxLength ); - jused = Z_Malloc(header->instructionCount + 2 ); - - Com_Memset(jused, 0, header->instructionCount+2); - - for(pass=0;pass<2;pass++) { - oc0 = -23423; - oc1 = -234354; - pop0 = -43435; - pop1 = -545455; - - // translate all instructions - pc = 0; - instruction = 0; - code = (byte *)header + header->codeOffset; - compiledOfs = 0; - - LastCommand = LAST_COMMAND_NONE; - - while ( instruction < header->instructionCount ) { - if ( compiledOfs > maxLength - 16 ) { - Com_Error( ERR_FATAL, "VM_CompileX86: maxLength exceeded" ); - } - - vm->instructionPointers[ instruction ] = compiledOfs; - instruction++; - - if ( pc > header->codeLength ) { - Com_Error( ERR_FATAL, "VM_CompileX86: pc > header->codeLength" ); - } - - op = code[ pc ]; - pc++; - switch ( op ) { - case 0: - break; - case OP_BREAK: - EmitString( "CC" ); // int 3 - break; - case OP_ENTER: - EmitString( "81 EE" ); // sub esi, 0x12345678 - Emit4( Constant4() ); - break; - case OP_CONST: - if (code[pc+4] == OP_LOAD4) { - EmitAddEDI4(vm); - EmitString( "BB" ); // mov ebx, 0x12345678 - Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); - EmitString( "8B 03" ); // mov eax, dword ptr [ebx] - EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax - pc++; // OP_LOAD4 - instruction += 1; - break; - } - if (code[pc+4] == OP_LOAD2) { - EmitAddEDI4(vm); - EmitString( "BB" ); // mov ebx, 0x12345678 - Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); - EmitString( "0F B7 03" ); // movzx eax, word ptr [ebx] - EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax - pc++; // OP_LOAD4 - instruction += 1; - break; - } - if (code[pc+4] == OP_LOAD1) { - EmitAddEDI4(vm); - EmitString( "BB" ); // mov ebx, 0x12345678 - Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); - EmitString( "0F B6 03" ); // movzx eax, byte ptr [ebx] - EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax - pc++; // OP_LOAD4 - instruction += 1; - break; - } - if (code[pc+4] == OP_STORE4) { - opt = EmitMovEBXEDI(vm, (vm->dataMask & ~3)); - EmitString( "B8" ); // mov eax, 0x12345678 - Emit4( Constant4() ); -// if (!opt) { -// EmitString( "81 E3" ); // and ebx, 0x12345678 -// Emit4( vm->dataMask & ~3 ); -// } - EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax - Emit4( (int)vm->dataBase ); - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - pc++; // OP_STORE4 - instruction += 1; - break; - } - if (code[pc+4] == OP_STORE2) { - opt = EmitMovEBXEDI(vm, (vm->dataMask & ~1)); - EmitString( "B8" ); // mov eax, 0x12345678 - Emit4( Constant4() ); -// if (!opt) { -// EmitString( "81 E3" ); // and ebx, 0x12345678 -// Emit4( vm->dataMask & ~1 ); -// } - EmitString( "66 89 83" ); // mov word ptr [ebx+0x12345678], eax - Emit4( (int)vm->dataBase ); - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - pc++; // OP_STORE4 - instruction += 1; - break; - } - if (code[pc+4] == OP_STORE1) { - opt = EmitMovEBXEDI(vm, vm->dataMask); - EmitString( "B8" ); // mov eax, 0x12345678 - Emit4( Constant4() ); -// if (!opt) { -// EmitString( "81 E3" ); // and ebx, 0x12345678 -// Emit4( vm->dataMask ); -// } - EmitString( "88 83" ); // mov byte ptr [ebx+0x12345678], eax - Emit4( (int)vm->dataBase ); - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - pc++; // OP_STORE4 - instruction += 1; - break; - } - if (code[pc+4] == OP_ADD) { - EmitString( "81 07" ); // add dword ptr [edi], 0x1234567 - Emit4( Constant4() ); - pc++; // OP_ADD - instruction += 1; - break; - } - if (code[pc+4] == OP_SUB) { - EmitString( "81 2F" ); // sub dword ptr [edi], 0x1234567 - Emit4( Constant4() ); - pc++; // OP_ADD - instruction += 1; - break; - } - EmitAddEDI4(vm); - EmitString( "C7 07" ); // mov dword ptr [edi], 0x12345678 - lastConst = Constant4(); - Emit4( lastConst ); - if (code[pc] == OP_JUMP) { - jused[lastConst] = 1; - } - break; - case OP_LOCAL: - EmitAddEDI4(vm); - EmitString( "8D 86" ); // lea eax, [0x12345678 + esi] - oc0 = oc1; - oc1 = Constant4(); - Emit4( oc1 ); - EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax - break; - case OP_ARG: - EmitMovEAXEDI(vm); // mov eax,dword ptr [edi] - EmitString( "89 86" ); // mov dword ptr [esi+database],eax - // FIXME: range check - Emit4( Constant1() + (int)vm->dataBase ); - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_CALL: - EmitString( "C7 86" ); // mov dword ptr [esi+database],0x12345678 - Emit4( (int)vm->dataBase ); - Emit4( pc ); - EmitString( "FF 15" ); // call asmCallPtr - Emit4( (int)&asmCallPtr ); - break; - case OP_PUSH: - EmitAddEDI4(vm); - break; - case OP_POP: - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_LEAVE: - v = Constant4(); - EmitString( "81 C6" ); // add esi, 0x12345678 - Emit4( v ); - EmitString( "C3" ); // ret - break; - case OP_LOAD4: - if (code[pc] == OP_CONST && code[pc+5] == OP_ADD && code[pc+6] == OP_STORE4) { - if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { - compiledOfs -= 11; - vm->instructionPointers[ instruction-1 ] = compiledOfs; - } - pc++; // OP_CONST - v = Constant4(); - EmitMovEBXEDI(vm, vm->dataMask); - if (v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { - EmitString( "FF 83"); // inc dword ptr [ebx + 0x12345678] - Emit4( (int)vm->dataBase ); - } else { - EmitString( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678] - Emit4( (int)vm->dataBase ); - EmitString( "05" ); // add eax, const - Emit4( v ); - if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { - EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax - Emit4( (int)vm->dataBase ); - } else { - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - EmitString( "8B 1F" ); // mov ebx, dword ptr [edi] - EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax - Emit4( (int)vm->dataBase ); - } - } - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - pc++; // OP_ADD - pc++; // OP_STORE - instruction += 3; - break; - } - - if (code[pc] == OP_CONST && code[pc+5] == OP_SUB && code[pc+6] == OP_STORE4) { - if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { - compiledOfs -= 11; - vm->instructionPointers[ instruction-1 ] = compiledOfs; - } - EmitMovEBXEDI(vm, vm->dataMask); - EmitString( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678] - Emit4( (int)vm->dataBase ); - pc++; // OP_CONST - v = Constant4(); - if (v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { - EmitString( "FF 8B"); // dec dword ptr [ebx + 0x12345678] - Emit4( (int)vm->dataBase ); - } else { - EmitString( "2D" ); // sub eax, const - Emit4( v ); - if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { - EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax - Emit4( (int)vm->dataBase ); - } else { - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - EmitString( "8B 1F" ); // mov ebx, dword ptr [edi] - EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax - Emit4( (int)vm->dataBase ); - } - } - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - pc++; // OP_SUB - pc++; // OP_STORE - instruction += 3; - break; - } - - if (buf[compiledOfs-2] == 0x89 && buf[compiledOfs-1] == 0x07) { - compiledOfs -= 2; - vm->instructionPointers[ instruction-1 ] = compiledOfs; - EmitString( "8B 80"); // mov eax, dword ptr [eax + 0x1234567] - Emit4( (int)vm->dataBase ); - EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax - break; - } - EmitMovEBXEDI(vm, vm->dataMask); - EmitString( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678] - Emit4( (int)vm->dataBase ); - EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax - break; - case OP_LOAD2: - EmitMovEBXEDI(vm, vm->dataMask); - EmitString( "0F B7 83" ); // movzx eax, word ptr [ebx + 0x12345678] - Emit4( (int)vm->dataBase ); - EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax - break; - case OP_LOAD1: - EmitMovEBXEDI(vm, vm->dataMask); - EmitString( "0F B6 83" ); // movzx eax, byte ptr [ebx + 0x12345678] - Emit4( (int)vm->dataBase ); - EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax - break; - case OP_STORE4: - EmitMovEAXEDI(vm); - EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] -// if (pop1 != OP_CALL) { -// EmitString( "81 E3" ); // and ebx, 0x12345678 -// Emit4( vm->dataMask & ~3 ); -// } - EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax - Emit4( (int)vm->dataBase ); - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - break; - case OP_STORE2: - EmitMovEAXEDI(vm); - EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] -// EmitString( "81 E3" ); // and ebx, 0x12345678 -// Emit4( vm->dataMask & ~1 ); - EmitString( "66 89 83" ); // mov word ptr [ebx+0x12345678], eax - Emit4( (int)vm->dataBase ); - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - break; - case OP_STORE1: - EmitMovEAXEDI(vm); - EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] -// EmitString( "81 E3" ); // and ebx, 0x12345678 -// Emit4( vm->dataMask ); - EmitString( "88 83" ); // mov byte ptr [ebx+0x12345678], eax - Emit4( (int)vm->dataBase ); - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - break; - - case OP_EQ: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] - EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] - EmitString( "75 06" ); // jne +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_NE: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] - EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] - EmitString( "74 06" ); // je +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_LTI: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] - EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] - EmitString( "7D 06" ); // jnl +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_LEI: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] - EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] - EmitString( "7F 06" ); // jnle +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_GTI: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] - EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] - EmitString( "7E 06" ); // jng +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_GEI: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] - EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] - EmitString( "7C 06" ); // jnge +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_LTU: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] - EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] - EmitString( "73 06" ); // jnb +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_LEU: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] - EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] - EmitString( "77 06" ); // jnbe +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_GTU: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] - EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] - EmitString( "76 06" ); // jna +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_GEU: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] - EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] - EmitString( "72 06" ); // jnae +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_EQF: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "D9 47 04" ); // fld dword ptr [edi+4] - EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] - EmitString( "DF E0" ); // fnstsw ax - EmitString( "F6 C4 40" ); // test ah,0x40 - EmitString( "74 06" ); // je +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_NEF: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "D9 47 04" ); // fld dword ptr [edi+4] - EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] - EmitString( "DF E0" ); // fnstsw ax - EmitString( "F6 C4 40" ); // test ah,0x40 - EmitString( "75 06" ); // jne +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_LTF: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "D9 47 04" ); // fld dword ptr [edi+4] - EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] - EmitString( "DF E0" ); // fnstsw ax - EmitString( "F6 C4 01" ); // test ah,0x01 - EmitString( "74 06" ); // je +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_LEF: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "D9 47 04" ); // fld dword ptr [edi+4] - EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] - EmitString( "DF E0" ); // fnstsw ax - EmitString( "F6 C4 41" ); // test ah,0x41 - EmitString( "74 06" ); // je +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_GTF: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "D9 47 04" ); // fld dword ptr [edi+4] - EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] - EmitString( "DF E0" ); // fnstsw ax - EmitString( "F6 C4 41" ); // test ah,0x41 - EmitString( "75 06" ); // jne +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_GEF: - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - EmitString( "D9 47 04" ); // fld dword ptr [edi+4] - EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] - EmitString( "DF E0" ); // fnstsw ax - EmitString( "F6 C4 01" ); // test ah,0x01 - EmitString( "75 06" ); // jne +6 - EmitString( "FF 25" ); // jmp [0x12345678] - v = Constant4(); - jused[v] = 1; - Emit4( (int)vm->instructionPointers + v*4 ); - break; - case OP_NEGI: - EmitString( "F7 1F" ); // neg dword ptr [edi] - break; - case OP_ADD: - EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] - EmitString( "01 47 FC" ); // add dword ptr [edi-4],eax - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_SUB: - EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] - EmitString( "29 47 FC" ); // sub dword ptr [edi-4],eax - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_DIVI: - EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] - EmitString( "99" ); // cdq - EmitString( "F7 3F" ); // idiv dword ptr [edi] - EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_DIVU: - EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] - EmitString( "33 D2" ); // xor edx, edx - EmitString( "F7 37" ); // div dword ptr [edi] - EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_MODI: - EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] - EmitString( "99" ); // cdq - EmitString( "F7 3F" ); // idiv dword ptr [edi] - EmitString( "89 57 FC" ); // mov dword ptr [edi-4],edx - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_MODU: - EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] - EmitString( "33 D2" ); // xor edx, edx - EmitString( "F7 37" ); // div dword ptr [edi] - EmitString( "89 57 FC" ); // mov dword ptr [edi-4],edx - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_MULI: - EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] - EmitString( "F7 2F" ); // imul dword ptr [edi] - EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_MULU: - EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] - EmitString( "F7 27" ); // mul dword ptr [edi] - EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_BAND: - EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] - EmitString( "21 47 FC" ); // and dword ptr [edi-4],eax - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_BOR: - EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] - EmitString( "09 47 FC" ); // or dword ptr [edi-4],eax - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_BXOR: - EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] - EmitString( "31 47 FC" ); // xor dword ptr [edi-4],eax - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_BCOM: - EmitString( "F7 17" ); // not dword ptr [edi] - break; - case OP_LSH: - EmitString( "8B 0F" ); // mov ecx, dword ptr [edi] - EmitString( "D3 67 FC" ); // shl dword ptr [edi-4], cl - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_RSHI: - EmitString( "8B 0F" ); // mov ecx, dword ptr [edi] - EmitString( "D3 7F FC" ); // sar dword ptr [edi-4], cl - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_RSHU: - EmitString( "8B 0F" ); // mov ecx, dword ptr [edi] - EmitString( "D3 6F FC" ); // shr dword ptr [edi-4], cl - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_NEGF: - EmitString( "D9 07" ); // fld dword ptr [edi] - EmitString( "D9 E0" ); // fchs - EmitString( "D9 1F" ); // fstp dword ptr [edi] - break; - case OP_ADDF: - EmitString( "D9 47 FC" ); // fld dword ptr [edi-4] - EmitString( "D8 07" ); // fadd dword ptr [edi] - EmitString( "D9 5F FC" ); // fstp dword ptr [edi-4] - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - break; - case OP_SUBF: - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - EmitString( "D9 07" ); // fld dword ptr [edi] - EmitString( "D8 67 04" ); // fsub dword ptr [edi+4] - EmitString( "D9 1F" ); // fstp dword ptr [edi] - break; - case OP_DIVF: - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - EmitString( "D9 07" ); // fld dword ptr [edi] - EmitString( "D8 77 04" ); // fdiv dword ptr [edi+4] - EmitString( "D9 1F" ); // fstp dword ptr [edi] - break; - case OP_MULF: - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - EmitString( "D9 07" ); // fld dword ptr [edi] - EmitString( "D8 4f 04" ); // fmul dword ptr [edi+4] - EmitString( "D9 1F" ); // fstp dword ptr [edi] - break; - case OP_CVIF: - EmitString( "DB 07" ); // fild dword ptr [edi] - EmitString( "D9 1F" ); // fstp dword ptr [edi] - break; - case OP_CVFI: -#ifndef FTOL_PTR // WHENHELLISFROZENOVER // bk001213 - was used in 1.17 - // not IEEE complient, but simple and fast - EmitString( "D9 07" ); // fld dword ptr [edi] - EmitString( "DB 1F" ); // fistp dword ptr [edi] -#else // FTOL_PTR - // call the library conversion function - EmitString( "D9 07" ); // fld dword ptr [edi] - EmitString( "FF 15" ); // call ftolPtr - Emit4( (int)&ftolPtr ); - EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax -#endif - break; - case OP_SEX8: - EmitString( "0F BE 07" ); // movsx eax, byte ptr [edi] - EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax - break; - case OP_SEX16: - EmitString( "0F BF 07" ); // movsx eax, word ptr [edi] - EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax - break; - - case OP_BLOCK_COPY: - // FIXME: range check - EmitString( "56" ); // push esi - EmitString( "57" ); // push edi - EmitString( "8B 37" ); // mov esi,[edi] - EmitString( "8B 7F FC" ); // mov edi,[edi-4] - EmitString( "B9" ); // mov ecx,0x12345678 - Emit4( Constant4() >> 2 ); - EmitString( "B8" ); // mov eax, datamask - Emit4( vm->dataMask ); - EmitString( "BB" ); // mov ebx, database - Emit4( (int)vm->dataBase ); - EmitString( "23 F0" ); // and esi, eax - EmitString( "03 F3" ); // add esi, ebx - EmitString( "23 F8" ); // and edi, eax - EmitString( "03 FB" ); // add edi, ebx - EmitString( "F3 A5" ); // rep movsd - EmitString( "5F" ); // pop edi - EmitString( "5E" ); // pop esi - EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 - break; - - case OP_JUMP: - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - EmitString( "8B 47 04" ); // mov eax,dword ptr [edi+4] - // FIXME: range check - EmitString( "FF 24 85" ); // jmp dword ptr [instructionPointers + eax * 4] - Emit4( (int)vm->instructionPointers ); - break; - default: - Com_Error( ERR_DROP, "VM_CompileX86: bad opcode %i at offset %i", op, pc ); - } - pop0 = pop1; - pop1 = op; - } - } - - // copy to an exact size buffer on the hunk - vm->codeLength = compiledOfs; - vm->codeBase = Hunk_Alloc( compiledOfs, h_low ); - Com_Memcpy( vm->codeBase, buf, compiledOfs ); - Z_Free( buf ); - Z_Free( jused ); - Com_Printf( "VM file %s compiled to %i bytes of code\n", vm->name, compiledOfs ); - - // offset all the instruction pointers for the new location - for ( i = 0 ; i < header->instructionCount ; i++ ) { - vm->instructionPointers[i] += (int)vm->codeBase; - } - -#if 0 // ndef _WIN32 - // Must make the newly generated code executable - { - int r; - unsigned long addr; - int psize = getpagesize(); - - addr = ((int)vm->codeBase & ~(psize-1)) - psize; - - r = mprotect((char*)addr, vm->codeLength + (int)vm->codeBase - addr + psize, - PROT_READ | PROT_WRITE | PROT_EXEC ); - - if (r < 0) - Com_Error( ERR_FATAL, "mprotect failed to change PROT_EXEC" ); - } -#endif - -} - -/* -============== -VM_CallCompiled - -This function is called directly by the generated code -============== -*/ -#ifndef DLL_ONLY // bk010215 - for DLL_ONLY dedicated servers/builds w/o VM -int VM_CallCompiled( vm_t *vm, int *args ) { - int stack[1024]; - int programCounter; - int programStack; - int stackOnEntry; - byte *image; - void *entryPoint; - void *opStack; - int *oldInstructionPointers; - - oldInstructionPointers = instructionPointers; - - currentVM = vm; - instructionPointers = vm->instructionPointers; - - // interpret the code - vm->currentlyInterpreting = qtrue; - - callMask = vm->dataMask; - - // we might be called recursively, so this might not be the very top - programStack = vm->programStack; - stackOnEntry = programStack; - - // set up the stack frame - image = vm->dataBase; - - programCounter = 0; - - programStack -= 48; - - *(int *)&image[ programStack + 44] = args[9]; - *(int *)&image[ programStack + 40] = args[8]; - *(int *)&image[ programStack + 36] = args[7]; - *(int *)&image[ programStack + 32] = args[6]; - *(int *)&image[ programStack + 28] = args[5]; - *(int *)&image[ programStack + 24] = args[4]; - *(int *)&image[ programStack + 20] = args[3]; - *(int *)&image[ programStack + 16] = args[2]; - *(int *)&image[ programStack + 12] = args[1]; - *(int *)&image[ programStack + 8 ] = args[0]; - *(int *)&image[ programStack + 4 ] = 0; // return stack - *(int *)&image[ programStack ] = -1; // will terminate the loop on return - - // off we go into generated code... - entryPoint = vm->codeBase; - opStack = &stack; - -#ifdef _WIN32 - __asm { - pushad - mov esi, programStack; - mov edi, opStack - call entryPoint - mov programStack, esi - mov opStack, edi - popad - } -#else - { - static int memProgramStack; - static void *memOpStack; - static void *memEntryPoint; - - memProgramStack = programStack; - memOpStack = opStack; - memEntryPoint = entryPoint; - - __asm__(" pushal \r\n" \ - " movl %0,%%esi \r\n" \ - " movl %1,%%edi \r\n" \ - " call *%2 \r\n" \ - " movl %%esi,%0 \r\n" \ - " movl %%edi,%1 \r\n" \ - " popal \r\n" \ - : "=m" (memProgramStack), "=m" (memOpStack) \ - : "m" (memEntryPoint), "0" (memProgramStack), "1" (memOpStack) \ - : "si", "di" \ - ); - - programStack = memProgramStack; - opStack = memOpStack; - } -#endif - - if ( opStack != &stack[1] ) { - Com_Error( ERR_DROP, "opStack corrupted in compiled code" ); - } - if ( programStack != stackOnEntry - 48 ) { - Com_Error( ERR_DROP, "programStack corrupted in compiled code" ); - } - - vm->programStack = stackOnEntry; - - // in case we were recursively called by another vm - instructionPointers = oldInstructionPointers; - - return *(int *)opStack; -} -#endif // !DLL_ONLY - - diff --git a/tools/quake3/bspc/faces.c b/tools/quake3/bspc/faces.c deleted file mode 100644 index f05f5ae9..00000000 --- a/tools/quake3/bspc/faces.c +++ /dev/null @@ -1,978 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// faces.c - -#include "qbsp.h" -#include "l_mem.h" - -/* - - some faces will be removed before saving, but still form nodes: - - the insides of sky volumes - meeting planes of different water current volumes - -*/ - -// undefine for dumb linear searches -#define USE_HASHING - -#define INTEGRAL_EPSILON 0.01 -#define POINT_EPSILON 0.5 -#define OFF_EPSILON 0.5 - -int c_merge; -int c_subdivide; - -int c_totalverts; -int c_uniqueverts; -int c_degenerate; -int c_tjunctions; -int c_faceoverflows; -int c_facecollapse; -int c_badstartverts; - -#define MAX_SUPERVERTS 512 -int superverts[MAX_SUPERVERTS]; -int numsuperverts; - -face_t *edgefaces[MAX_MAP_EDGES][2]; -int firstmodeledge = 1; -int firstmodelface; - -int c_tryedges; - -vec3_t edge_dir; -vec3_t edge_start; -vec_t edge_len; - -int num_edge_verts; -int edge_verts[MAX_MAP_VERTS]; - -face_t *NewFaceFromFace (face_t *f); - -//=========================================================================== - -typedef struct hashvert_s -{ - struct hashvert_s *next; - int num; -} hashvert_t; - - -#define HASH_SIZE 64 - - -int vertexchain[MAX_MAP_VERTS]; // the next vertex in a hash chain -int hashverts[HASH_SIZE*HASH_SIZE]; // a vertex number, or 0 for no verts - -face_t *edgefaces[MAX_MAP_EDGES][2]; - -//============================================================================ - - -unsigned HashVec (vec3_t vec) -{ - int x, y; - - x = (4096 + (int)(vec[0]+0.5)) >> 7; - y = (4096 + (int)(vec[1]+0.5)) >> 7; - - if ( x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE ) - Error ("HashVec: point outside valid range"); - - return y*HASH_SIZE + x; -} - -#ifdef USE_HASHING -/* -============= -GetVertex - -Uses hashing -============= -*/ -int GetVertexnum (vec3_t in) -{ - int h; - int i; - float *p; - vec3_t vert; - int vnum; - - c_totalverts++; - - for (i=0 ; i<3 ; i++) - { - if ( fabs(in[i] - Q_rint(in[i])) < INTEGRAL_EPSILON) - vert[i] = Q_rint(in[i]); - else - vert[i] = in[i]; - } - - h = HashVec (vert); - - for (vnum=hashverts[h] ; vnum ; vnum=vertexchain[vnum]) - { - p = dvertexes[vnum].point; - if ( fabs(p[0]-vert[0]) 4096) - Error ("GetVertexnum: outside +/- 4096"); - } - - // search for an existing vertex match - for (i=0, dv=dvertexes ; ipoint[j]; - if ( d > POINT_EPSILON || d < -POINT_EPSILON) - break; - } - if (j == 3) - return i; // a match - } - - // new point - if (numvertexes == MAX_MAP_VERTS) - Error ("MAX_MAP_VERTS"); - VectorCopy (v, dv->point); - numvertexes++; - c_uniqueverts++; - - return numvertexes-1; -} -#endif - - -/* -================== -FaceFromSuperverts - -The faces vertexes have been added to the superverts[] array, -and there may be more there than can be held in a face (MAXEDGES). - -If less, the faces vertexnums[] will be filled in, otherwise -face will reference a tree of split[] faces until all of the -vertexnums can be added. - -superverts[base] will become face->vertexnums[0], and the others -will be circularly filled in. -================== -*/ -void FaceFromSuperverts (node_t *node, face_t *f, int base) -{ - face_t *newf; - int remaining; - int i; - - remaining = numsuperverts; - while (remaining > MAXEDGES) - { // must split into two faces, because of vertex overload - c_faceoverflows++; - - newf = f->split[0] = NewFaceFromFace (f); - newf = f->split[0]; - newf->next = node->faces; - node->faces = newf; - - newf->numpoints = MAXEDGES; - for (i=0 ; ivertexnums[i] = superverts[(i+base)%numsuperverts]; - - f->split[1] = NewFaceFromFace (f); - f = f->split[1]; - f->next = node->faces; - node->faces = f; - - remaining -= (MAXEDGES-2); - base = (base+MAXEDGES-1)%numsuperverts; - } - - // copy the vertexes back to the face - f->numpoints = remaining; - for (i=0 ; ivertexnums[i] = superverts[(i+base)%numsuperverts]; -} - - -/* -================== -EmitFaceVertexes -================== -*/ -void EmitFaceVertexes (node_t *node, face_t *f) -{ - winding_t *w; - int i; - - if (f->merged || f->split[0] || f->split[1]) - return; - - w = f->w; - for (i=0 ; inumpoints ; i++) - { - if (noweld) - { // make every point unique - if (numvertexes == MAX_MAP_VERTS) - Error ("MAX_MAP_VERTS"); - superverts[i] = numvertexes; - VectorCopy (w->p[i], dvertexes[numvertexes].point); - numvertexes++; - c_uniqueverts++; - c_totalverts++; - } - else - superverts[i] = GetVertexnum (w->p[i]); - } - numsuperverts = w->numpoints; - - // this may fragment the face if > MAXEDGES - FaceFromSuperverts (node, f, 0); -} - -/* -================== -EmitVertexes_r -================== -*/ -void EmitVertexes_r (node_t *node) -{ - int i; - face_t *f; - - if (node->planenum == PLANENUM_LEAF) - return; - - for (f=node->faces ; f ; f=f->next) - { - EmitFaceVertexes (node, f); - } - - for (i=0 ; i<2 ; i++) - EmitVertexes_r (node->children[i]); -} - - -#ifdef USE_HASHING -/* -========== -FindEdgeVerts - -Uses the hash tables to cut down to a small number -========== -*/ -void FindEdgeVerts (vec3_t v1, vec3_t v2) -{ - int x1, x2, y1, y2, t; - int x, y; - int vnum; - -#if 0 -{ - int i; - num_edge_verts = numvertexes-1; - for (i=0 ; i> 7; - y1 = (4096 + (int)(v1[1]+0.5)) >> 7; - x2 = (4096 + (int)(v2[0]+0.5)) >> 7; - y2 = (4096 + (int)(v2[1]+0.5)) >> 7; - - if (x1 > x2) - { - t = x1; - x1 = x2; - x2 = t; - } - if (y1 > y2) - { - t = y1; - y1 = y2; - y2 = t; - } -#if 0 - x1--; - x2++; - y1--; - y2++; - if (x1 < 0) - x1 = 0; - if (x2 >= HASH_SIZE) - x2 = HASH_SIZE; - if (y1 < 0) - y1 = 0; - if (y2 >= HASH_SIZE) - y2 = HASH_SIZE; -#endif - num_edge_verts = 0; - for (x=x1 ; x <= x2 ; x++) - { - for (y=y1 ; y <= y2 ; y++) - { - for (vnum=hashverts[y*HASH_SIZE+x] ; vnum ; vnum=vertexchain[vnum]) - { - edge_verts[num_edge_verts++] = vnum; - } - } - } -} - -#else -/* -========== -FindEdgeVerts - -Forced a dumb check of everything -========== -*/ -void FindEdgeVerts (vec3_t v1, vec3_t v2) -{ - int i; - - num_edge_verts = numvertexes-1; - for (i=0 ; i= end) - continue; // off an end - VectorMA (edge_start, dist, edge_dir, exact); - VectorSubtract (p, exact, off); - error = VectorLength (off); - - if (fabs(error) > OFF_EPSILON) - continue; // not on the edge - - // break the edge - c_tjunctions++; - TestEdge (start, dist, p1, j, k+1); - TestEdge (dist, end, j, p2, k+1); - return; - } - - // the edge p1 to p2 is now free of tjunctions - if (numsuperverts >= MAX_SUPERVERTS) - Error ("MAX_SUPERVERTS"); - superverts[numsuperverts] = p1; - numsuperverts++; -} - -/* -================== -FixFaceEdges - -================== -*/ -void FixFaceEdges (node_t *node, face_t *f) -{ - int p1, p2; - int i; - vec3_t e2; - vec_t len; - int count[MAX_SUPERVERTS], start[MAX_SUPERVERTS]; - int base; - - if (f->merged || f->split[0] || f->split[1]) - return; - - numsuperverts = 0; - - for (i=0 ; inumpoints ; i++) - { - p1 = f->vertexnums[i]; - p2 = f->vertexnums[(i+1)%f->numpoints]; - - VectorCopy (dvertexes[p1].point, edge_start); - VectorCopy (dvertexes[p2].point, e2); - - FindEdgeVerts (edge_start, e2); - - VectorSubtract (e2, edge_start, edge_dir); - len = VectorNormalize(edge_dir); - - start[i] = numsuperverts; - TestEdge (0, len, p1, p2, 0); - - count[i] = numsuperverts - start[i]; - } - - if (numsuperverts < 3) - { // entire face collapsed - f->numpoints = 0; - c_facecollapse++; - return; - } - - // we want to pick a vertex that doesn't have tjunctions - // on either side, which can cause artifacts on trifans, - // especially underwater - for (i=0 ; inumpoints ; i++) - { - if (count[i] == 1 && count[(i+f->numpoints-1)%f->numpoints] == 1) - break; - } - if (i == f->numpoints) - { - f->badstartvert = true; - c_badstartverts++; - base = 0; - } - else - { // rotate the vertex order - base = start[i]; - } - - // this may fragment the face if > MAXEDGES - FaceFromSuperverts (node, f, base); -} - -/* -================== -FixEdges_r -================== -*/ -void FixEdges_r (node_t *node) -{ - int i; - face_t *f; - - if (node->planenum == PLANENUM_LEAF) - return; - - for (f=node->faces ; f ; f=f->next) - FixFaceEdges (node, f); - - for (i=0 ; i<2 ; i++) - FixEdges_r (node->children[i]); -} - -/* -=========== -FixTjuncs - -=========== -*/ -void FixTjuncs (node_t *headnode) -{ - // snap and merge all vertexes - qprintf ("---- snap verts ----\n"); - memset (hashverts, 0, sizeof(hashverts)); - c_totalverts = 0; - c_uniqueverts = 0; - c_faceoverflows = 0; - EmitVertexes_r (headnode); - qprintf ("%i unique from %i\n", c_uniqueverts, c_totalverts); - - // break edges on tjunctions - qprintf ("---- tjunc ----\n"); - c_tryedges = 0; - c_degenerate = 0; - c_facecollapse = 0; - c_tjunctions = 0; - if (!notjunc) - FixEdges_r (headnode); - qprintf ("%5i edges degenerated\n", c_degenerate); - qprintf ("%5i faces degenerated\n", c_facecollapse); - qprintf ("%5i edges added by tjunctions\n", c_tjunctions); - qprintf ("%5i faces added by tjunctions\n", c_faceoverflows); - qprintf ("%5i bad start verts\n", c_badstartverts); -} - - -//======================================================== - -int c_faces; - -face_t *AllocFace (void) -{ - face_t *f; - - f = GetMemory(sizeof(*f)); - memset (f, 0, sizeof(*f)); - c_faces++; - - return f; -} - -face_t *NewFaceFromFace (face_t *f) -{ - face_t *newf; - - newf = AllocFace (); - *newf = *f; - newf->merged = NULL; - newf->split[0] = newf->split[1] = NULL; - newf->w = NULL; - return newf; -} - -void FreeFace (face_t *f) -{ - if (f->w) - FreeWinding (f->w); - FreeMemory(f); - c_faces--; -} - -//======================================================== - -/* -================== -GetEdge - -Called by writebsp. -Don't allow four way edges -================== -*/ -int GetEdge2 (int v1, int v2, face_t *f) -{ - dedge_t *edge; - int i; - - c_tryedges++; - - if (!noshare) - { - for (i=firstmodeledge ; i < numedges ; i++) - { - edge = &dedges[i]; - if (v1 == edge->v[1] && v2 == edge->v[0] - && edgefaces[i][0]->contents == f->contents) - { - if (edgefaces[i][1]) - // printf ("WARNING: multiple backward edge\n"); - continue; - edgefaces[i][1] = f; - return -i; - } - #if 0 - if (v1 == edge->v[0] && v2 == edge->v[1]) - { - printf ("WARNING: multiple forward edge\n"); - return i; - } - #endif - } - } - -// emit an edge - if (numedges >= MAX_MAP_EDGES) - Error ("numedges == MAX_MAP_EDGES"); - edge = &dedges[numedges]; - numedges++; - edge->v[0] = v1; - edge->v[1] = v2; - edgefaces[numedges-1][0] = f; - - return numedges-1; -} - -/* -=========================================================================== - -FACE MERGING - -=========================================================================== -*/ - -/* -============= -TryMerge - -If two polygons share a common edge and the edges that meet at the -common points are both inside the other polygons, merge them - -Returns NULL if the faces couldn't be merged, or the new face. -The originals will NOT be freed. -============= -*/ -face_t *TryMerge (face_t *f1, face_t *f2, vec3_t planenormal) -{ - face_t *newf; - winding_t *nw; - - if (!f1->w || !f2->w) - return NULL; - if (f1->texinfo != f2->texinfo) - return NULL; - if (f1->planenum != f2->planenum) // on front and back sides - return NULL; - if (f1->contents != f2->contents) - return NULL; - - - nw = TryMergeWinding (f1->w, f2->w, planenormal); - if (!nw) - return NULL; - - c_merge++; - newf = NewFaceFromFace (f1); - newf->w = nw; - - f1->merged = newf; - f2->merged = newf; - - return newf; -} - -/* -=============== -MergeNodeFaces -=============== -*/ -void MergeNodeFaces (node_t *node) -{ - face_t *f1, *f2, *end; - face_t *merged; - plane_t *plane; - - plane = &mapplanes[node->planenum]; - merged = NULL; - - for (f1 = node->faces ; f1 ; f1 = f1->next) - { - if (f1->merged || f1->split[0] || f1->split[1]) - continue; - - for (f2 = node->faces ; f2 != f1 ; f2=f2->next) - { - if (f2->merged || f2->split[0] || f2->split[1]) - continue; - - //IDBUG: always passes the face's node's normal to TryMerge() - //regardless of which side the face is on. Approximately 50% of - //the time the face will be on the other side of node, and thus - //the result of the convex/concave test in TryMergeWinding(), - //which depends on the normal, is flipped. This causes faces - //that shouldn't be merged to be merged and faces that - //should be merged to not be merged. - //the following added line fixes this bug - //thanks to: Alexander Malmberg - plane = &mapplanes[f1->planenum]; - // - merged = TryMerge (f1, f2, plane->normal); - if (!merged) - continue; - - // add merged to the end of the node face list - // so it will be checked against all the faces again - for (end = node->faces ; end->next ; end = end->next) - ; - merged->next = NULL; - end->next = merged; - break; - } - } -} - -//===================================================================== - -/* -=============== -SubdivideFace - -Chop up faces that are larger than we want in the surface cache -=============== -*/ -void SubdivideFace (node_t *node, face_t *f) -{ - float mins, maxs; - vec_t v; - int axis, i; - texinfo_t *tex; - vec3_t temp; - vec_t dist; - winding_t *w, *frontw, *backw; - - if (f->merged) - return; - -// special (non-surface cached) faces don't need subdivision - tex = &texinfo[f->texinfo]; - - if ( tex->flags & (SURF_WARP|SURF_SKY) ) - { - return; - } - - for (axis = 0 ; axis < 2 ; axis++) - { - while (1) - { - mins = 999999; - maxs = -999999; - - VectorCopy (tex->vecs[axis], temp); - w = f->w; - for (i=0 ; inumpoints ; i++) - { - v = DotProduct (w->p[i], temp); - if (v < mins) - mins = v; - if (v > maxs) - maxs = v; - } -#if 0 - if (maxs - mins <= 0) - Error ("zero extents"); -#endif - if (axis == 2) - { // allow double high walls - if (maxs - mins <= subdivide_size/* *2 */) - break; - } - else if (maxs - mins <= subdivide_size) - break; - - // split it - c_subdivide++; - - v = VectorNormalize (temp); - - dist = (mins + subdivide_size - 16)/v; - - ClipWindingEpsilon (w, temp, dist, ON_EPSILON, &frontw, &backw); - if (!frontw || !backw) - Error ("SubdivideFace: didn't split the polygon"); - - f->split[0] = NewFaceFromFace (f); - f->split[0]->w = frontw; - f->split[0]->next = node->faces; - node->faces = f->split[0]; - - f->split[1] = NewFaceFromFace (f); - f->split[1]->w = backw; - f->split[1]->next = node->faces; - node->faces = f->split[1]; - - SubdivideFace (node, f->split[0]); - SubdivideFace (node, f->split[1]); - return; - } - } -} - -void SubdivideNodeFaces (node_t *node) -{ - face_t *f; - - for (f = node->faces ; f ; f=f->next) - { - SubdivideFace (node, f); - } -} - -//=========================================================================== - -int c_nodefaces; - - -/* -============ -FaceFromPortal - -============ -*/ -face_t *FaceFromPortal (portal_t *p, int pside) -{ - face_t *f; - side_t *side; - - side = p->side; - if (!side) - return NULL; // portal does not bridge different visible contents - - f = AllocFace (); - - f->texinfo = side->texinfo; - f->planenum = (side->planenum & ~1) | pside; - f->portal = p; - - if ( (p->nodes[pside]->contents & CONTENTS_WINDOW) - && VisibleContents(p->nodes[!pside]->contents^p->nodes[pside]->contents) == CONTENTS_WINDOW ) - return NULL; // don't show insides of windows - - if (pside) - { - f->w = ReverseWinding(p->winding); - f->contents = p->nodes[1]->contents; - } - else - { - f->w = CopyWinding(p->winding); - f->contents = p->nodes[0]->contents; - } - return f; -} - - -/* -=============== -MakeFaces_r - -If a portal will make a visible face, -mark the side that originally created it - - solid / empty : solid - solid / water : solid - water / empty : water - water / water : none -=============== -*/ -void MakeFaces_r (node_t *node) -{ - portal_t *p; - int s; - - // recurse down to leafs - if (node->planenum != PLANENUM_LEAF) - { - MakeFaces_r (node->children[0]); - MakeFaces_r (node->children[1]); - - // merge together all visible faces on the node - if (!nomerge) - MergeNodeFaces (node); - if (!nosubdiv) - SubdivideNodeFaces (node); - - return; - } - - // solid leafs never have visible faces - if (node->contents & CONTENTS_SOLID) - return; - - // see which portals are valid - for (p=node->portals ; p ; p = p->next[s]) - { - s = (p->nodes[1] == node); - - p->face[s] = FaceFromPortal (p, s); - if (p->face[s]) - { - c_nodefaces++; - p->face[s]->next = p->onnode->faces; - p->onnode->faces = p->face[s]; - } - } -} - -/* -============ -MakeFaces -============ -*/ -void MakeFaces (node_t *node) -{ - qprintf ("--- MakeFaces ---\n"); - c_merge = 0; - c_subdivide = 0; - c_nodefaces = 0; - - MakeFaces_r (node); - - qprintf ("%5i makefaces\n", c_nodefaces); - qprintf ("%5i merged\n", c_merge); - qprintf ("%5i subdivided\n", c_subdivide); -} diff --git a/tools/quake3/bspc/gldraw.c b/tools/quake3/bspc/gldraw.c deleted file mode 100644 index 18ed2c86..00000000 --- a/tools/quake3/bspc/gldraw.c +++ /dev/null @@ -1,232 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include -#include -#include -#include - -#include "qbsp.h" - -// can't use the glvertex3fv functions, because the vec3_t fields -// could be either floats or doubles, depending on DOUBLEVEC_T - -qboolean drawflag; -vec3_t draw_mins, draw_maxs; - - -#define WIN_SIZE 512 - -void InitWindow (void) -{ - auxInitDisplayMode (AUX_SINGLE | AUX_RGB); - auxInitPosition (0, 0, WIN_SIZE, WIN_SIZE); - auxInitWindow ("qcsg"); -} - -void Draw_ClearWindow (void) -{ - static int init; - int w, h, g; - vec_t mx, my; - - if (!drawflag) - return; - - if (!init) - { - init = true; - InitWindow (); - } - - glClearColor (1,0.8,0.8,0); - glClear (GL_COLOR_BUFFER_BIT); - - w = (draw_maxs[0] - draw_mins[0]); - h = (draw_maxs[1] - draw_mins[1]); - - mx = draw_mins[0] + w/2; - my = draw_mins[1] + h/2; - - g = w > h ? w : h; - - glLoadIdentity (); - gluPerspective (90, 1, 2, 16384); - gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0); - - glColor3f (0,0,0); -// glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - glDisable (GL_DEPTH_TEST); - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - -#if 0 - glColor4f (1,0,0,0.5); - glBegin (GL_POLYGON); - - glVertex3f (0, 500, 0); - glVertex3f (0, 900, 0); - glVertex3f (0, 900, 100); - glVertex3f (0, 500, 100); - - glEnd (); -#endif - - glFlush (); - -} - -void Draw_SetRed (void) -{ - if (!drawflag) - return; - - glColor3f (1,0,0); -} - -void Draw_SetGrey (void) -{ - if (!drawflag) - return; - - glColor3f (0.5,0.5,0.5); -} - -void Draw_SetBlack (void) -{ - if (!drawflag) - return; - - glColor3f (0,0,0); -} - -void DrawWinding (winding_t *w) -{ - int i; - - if (!drawflag) - return; - - glColor4f (0,0,0,0.5); - glBegin (GL_LINE_LOOP); - for (i=0 ; inumpoints ; i++) - glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] ); - glEnd (); - - glColor4f (0,1,0,0.3); - glBegin (GL_POLYGON); - for (i=0 ; inumpoints ; i++) - glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] ); - glEnd (); - - glFlush (); -} - -void DrawAuxWinding (winding_t *w) -{ - int i; - - if (!drawflag) - return; - - glColor4f (0,0,0,0.5); - glBegin (GL_LINE_LOOP); - for (i=0 ; inumpoints ; i++) - glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] ); - glEnd (); - - glColor4f (1,0,0,0.3); - glBegin (GL_POLYGON); - for (i=0 ; inumpoints ; i++) - glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] ); - glEnd (); - - glFlush (); -} - -//============================================================ - -#define GLSERV_PORT 25001 - -qboolean wins_init; -int draw_socket; - -void GLS_BeginScene (void) -{ - WSADATA winsockdata; - WORD wVersionRequested; - struct sockaddr_in address; - int r; - - if (!wins_init) - { - wins_init = true; - - wVersionRequested = MAKEWORD(1, 1); - - r = WSAStartup (MAKEWORD(1, 1), &winsockdata); - - if (r) - Error ("Winsock initialization failed."); - - } - - // connect a socket to the server - - draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (draw_socket == -1) - Error ("draw_socket failed"); - - address.sin_family = AF_INET; - address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - address.sin_port = GLSERV_PORT; - r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address)); - if (r == -1) - { - closesocket (draw_socket); - draw_socket = 0; - } -} - -void GLS_Winding (winding_t *w, int code) -{ - byte buf[1024]; - int i, j; - - if (!draw_socket) - return; - - ((int *)buf)[0] = w->numpoints; - ((int *)buf)[1] = code; - for (i=0 ; inumpoints ; i++) - for (j=0 ; j<3 ; j++) - ((float *)buf)[2+i*3+j] = w->p[i][j]; - - send (draw_socket, buf, w->numpoints*12+8, 0); -} - -void GLS_EndScene (void) -{ - closesocket (draw_socket); - draw_socket = 0; -} diff --git a/tools/quake3/bspc/glfile.c b/tools/quake3/bspc/glfile.c deleted file mode 100644 index e32821b7..00000000 --- a/tools/quake3/bspc/glfile.c +++ /dev/null @@ -1,149 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" - -int c_glfaces; - -int PortalVisibleSides (portal_t *p) -{ - int fcon, bcon; - - if (!p->onnode) - return 0; // outside - - fcon = p->nodes[0]->contents; - bcon = p->nodes[1]->contents; - - // same contents never create a face - if (fcon == bcon) - return 0; - - // FIXME: is this correct now? - if (!fcon) - return 1; - if (!bcon) - return 2; - return 0; -} - -void OutputWinding (winding_t *w, FILE *glview) -{ - static int level = 128; - vec_t light; - int i; - - fprintf (glview, "%i\n", w->numpoints); - level+=28; - light = (level&255)/255.0; - for (i=0 ; inumpoints ; i++) - { - fprintf (glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n", - w->p[i][0], - w->p[i][1], - w->p[i][2], - light, - light, - light); - } - fprintf (glview, "\n"); -} - -/* -============= -OutputPortal -============= -*/ -void OutputPortal (portal_t *p, FILE *glview) -{ - winding_t *w; - int sides; - - sides = PortalVisibleSides (p); - if (!sides) - return; - - c_glfaces++; - - w = p->winding; - - if (sides == 2) // back side - w = ReverseWinding (w); - - OutputWinding (w, glview); - - if (sides == 2) - FreeWinding(w); -} - -/* -============= -WriteGLView_r -============= -*/ -void WriteGLView_r (node_t *node, FILE *glview) -{ - portal_t *p, *nextp; - - if (node->planenum != PLANENUM_LEAF) - { - WriteGLView_r (node->children[0], glview); - WriteGLView_r (node->children[1], glview); - return; - } - - // write all the portals - for (p=node->portals ; p ; p=nextp) - { - if (p->nodes[0] == node) - { - OutputPortal (p, glview); - nextp = p->next[0]; - } - else - nextp = p->next[1]; - } -} - -/* -============= -WriteGLView -============= -*/ -void WriteGLView (tree_t *tree, char *source) -{ - char name[1024]; - FILE *glview; - - c_glfaces = 0; - sprintf (name, "%s%s.gl",outbase, source); - printf ("Writing %s\n", name); - - glview = fopen (name, "w"); - if (!glview) - Error ("Couldn't open %s", name); - WriteGLView_r (tree->headnode, glview); - fclose (glview); - - printf ("%5i c_glfaces\n", c_glfaces); -} - diff --git a/tools/quake3/bspc/l_bsp_ent.c b/tools/quake3/bspc/l_bsp_ent.c deleted file mode 100644 index 4811958a..00000000 --- a/tools/quake3/bspc/l_bsp_ent.c +++ /dev/null @@ -1,180 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "l_cmd.h" -#include "l_math.h" -#include "l_mem.h" -#include "l_log.h" -#include "botlib/l_script.h" -#include "l_bsp_ent.h" - -#define MAX_KEY 32 -#define MAX_VALUE 1024 - -int num_entities; -entity_t entities[MAX_MAP_ENTITIES]; - -void StripTrailing(char *e) -{ - char *s; - - s = e + strlen(e)-1; - while (s >= e && *s <= 32) - { - *s = 0; - s--; - } -} - -/* -================= -ParseEpair -================= -*/ -epair_t *ParseEpair(script_t *script) -{ - epair_t *e; - token_t token; - - e = GetMemory(sizeof(epair_t)); - memset (e, 0, sizeof(epair_t)); - - PS_ExpectAnyToken(script, &token); - StripDoubleQuotes(token.string); - if (strlen(token.string) >= MAX_KEY-1) - Error ("ParseEpair: token %s too long", token.string); - e->key = copystring(token.string); - PS_ExpectAnyToken(script, &token); - StripDoubleQuotes(token.string); - if (strlen(token.string) >= MAX_VALUE-1) - Error ("ParseEpair: token %s too long", token.string); - e->value = copystring(token.string); - - // strip trailing spaces - StripTrailing(e->key); - StripTrailing(e->value); - - return e; -} //end of the function ParseEpair - - -/* -================ -ParseEntity -================ -*/ -qboolean ParseEntity(script_t *script) -{ - epair_t *e; - entity_t *mapent; - token_t token; - - if (!PS_ReadToken(script, &token)) - return false; - - if (strcmp(token.string, "{")) - Error ("ParseEntity: { not found"); - - if (num_entities == MAX_MAP_ENTITIES) - Error ("num_entities == MAX_MAP_ENTITIES"); - - mapent = &entities[num_entities]; - num_entities++; - - do - { - if (!PS_ReadToken(script, &token)) - Error ("ParseEntity: EOF without closing brace"); - if (!strcmp(token.string, "}") ) - break; - PS_UnreadLastToken(script); - e = ParseEpair(script); - e->next = mapent->epairs; - mapent->epairs = e; - } while (1); - - return true; -} //end of the function ParseEntity - -void PrintEntity (entity_t *ent) -{ - epair_t *ep; - - printf ("------- entity %p -------\n", ent); - for (ep=ent->epairs ; ep ; ep=ep->next) - { - printf ("%s = %s\n", ep->key, ep->value); - } - -} - -void SetKeyValue (entity_t *ent, char *key, char *value) -{ - epair_t *ep; - - for (ep=ent->epairs ; ep ; ep=ep->next) - if (!strcmp (ep->key, key) ) - { - FreeMemory(ep->value); - ep->value = copystring(value); - return; - } - ep = GetMemory(sizeof(*ep)); - ep->next = ent->epairs; - ent->epairs = ep; - ep->key = copystring(key); - ep->value = copystring(value); -} - -char *ValueForKey (entity_t *ent, char *key) -{ - epair_t *ep; - - for (ep=ent->epairs ; ep ; ep=ep->next) - if (!strcmp (ep->key, key) ) - return ep->value; - return ""; -} - -vec_t FloatForKey (entity_t *ent, char *key) -{ - char *k; - - k = ValueForKey (ent, key); - return atof(k); -} - -void GetVectorForKey (entity_t *ent, char *key, vec3_t vec) -{ - char *k; - double v1, v2, v3; - - k = ValueForKey (ent, key); -// scanf into doubles, then assign, so it is vec_t size independent - v1 = v2 = v3 = 0; - sscanf (k, "%lf %lf %lf", &v1, &v2, &v3); - vec[0] = v1; - vec[1] = v2; - vec[2] = v3; -} - - diff --git a/tools/quake3/bspc/l_bsp_ent.h b/tools/quake3/bspc/l_bsp_ent.h deleted file mode 100644 index 461b91c7..00000000 --- a/tools/quake3/bspc/l_bsp_ent.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#ifndef MAX_MAP_ENTITIES -#define MAX_MAP_ENTITIES 2048 -#endif - -typedef struct epair_s -{ - struct epair_s *next; - char *key; - char *value; -} epair_t; - -typedef struct -{ - vec3_t origin; - int firstbrush; - int numbrushes; - epair_t *epairs; - // only valid for func_areaportals - int areaportalnum; - int portalareas[2]; - int modelnum; //for bsp 2 map conversion - qboolean wasdetail; //for SIN -} entity_t; - -extern int num_entities; -extern entity_t entities[MAX_MAP_ENTITIES]; - -void StripTrailing(char *e); -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); -qboolean ParseEntity(script_t *script); -epair_t *ParseEpair(script_t *script); -void PrintEntity(entity_t *ent); - diff --git a/tools/quake3/bspc/l_bsp_hl.c b/tools/quake3/bspc/l_bsp_hl.c deleted file mode 100644 index 46149ad2..00000000 --- a/tools/quake3/bspc/l_bsp_hl.c +++ /dev/null @@ -1,893 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "l_cmd.h" -#include "l_math.h" -#include "l_mem.h" -#include "l_log.h" -#include "botlib/l_script.h" -#include "l_bsp_hl.h" -#include "l_bsp_ent.h" - -//============================================================================= - -int hl_nummodels; -hl_dmodel_t *hl_dmodels;//[HL_MAX_MAP_MODELS]; -int hl_dmodels_checksum; - -int hl_visdatasize; -byte *hl_dvisdata;//[HL_MAX_MAP_VISIBILITY]; -int hl_dvisdata_checksum; - -int hl_lightdatasize; -byte *hl_dlightdata;//[HL_MAX_MAP_LIGHTING]; -int hl_dlightdata_checksum; - -int hl_texdatasize; -byte *hl_dtexdata;//[HL_MAX_MAP_MIPTEX]; // (dmiptexlump_t) -int hl_dtexdata_checksum; - -int hl_entdatasize; -char *hl_dentdata;//[HL_MAX_MAP_ENTSTRING]; -int hl_dentdata_checksum; - -int hl_numleafs; -hl_dleaf_t *hl_dleafs;//[HL_MAX_MAP_LEAFS]; -int hl_dleafs_checksum; - -int hl_numplanes; -hl_dplane_t *hl_dplanes;//[HL_MAX_MAP_PLANES]; -int hl_dplanes_checksum; - -int hl_numvertexes; -hl_dvertex_t *hl_dvertexes;//[HL_MAX_MAP_VERTS]; -int hl_dvertexes_checksum; - -int hl_numnodes; -hl_dnode_t *hl_dnodes;//[HL_MAX_MAP_NODES]; -int hl_dnodes_checksum; - -int hl_numtexinfo; -hl_texinfo_t *hl_texinfo;//[HL_MAX_MAP_TEXINFO]; -int hl_texinfo_checksum; - -int hl_numfaces; -hl_dface_t *hl_dfaces;//[HL_MAX_MAP_FACES]; -int hl_dfaces_checksum; - -int hl_numclipnodes; -hl_dclipnode_t *hl_dclipnodes;//[HL_MAX_MAP_CLIPNODES]; -int hl_dclipnodes_checksum; - -int hl_numedges; -hl_dedge_t *hl_dedges;//[HL_MAX_MAP_EDGES]; -int hl_dedges_checksum; - -int hl_nummarksurfaces; -unsigned short *hl_dmarksurfaces;//[HL_MAX_MAP_MARKSURFACES]; -int hl_dmarksurfaces_checksum; - -int hl_numsurfedges; -int *hl_dsurfedges;//[HL_MAX_MAP_SURFEDGES]; -int hl_dsurfedges_checksum; - -//int num_entities; -//entity_t entities[HL_MAX_MAP_ENTITIES]; - - -//#ifdef //ME - -int hl_bspallocated = false; -int hl_allocatedbspmem = 0; - -void HL_AllocMaxBSP(void) -{ - //models - hl_nummodels = 0; - hl_dmodels = (hl_dmodel_t *) GetMemory(HL_MAX_MAP_MODELS * sizeof(hl_dmodel_t)); - hl_allocatedbspmem = HL_MAX_MAP_MODELS * sizeof(hl_dmodel_t); - //visibility - hl_visdatasize = 0; - hl_dvisdata = (byte *) GetMemory(HL_MAX_MAP_VISIBILITY * sizeof(byte)); - hl_allocatedbspmem += HL_MAX_MAP_VISIBILITY * sizeof(byte); - //light data - hl_lightdatasize = 0; - hl_dlightdata = (byte *) GetMemory(HL_MAX_MAP_LIGHTING * sizeof(byte)); - hl_allocatedbspmem += HL_MAX_MAP_LIGHTING * sizeof(byte); - //texture data - hl_texdatasize = 0; - hl_dtexdata = (byte *) GetMemory(HL_MAX_MAP_MIPTEX * sizeof(byte)); // (dmiptexlump_t) - hl_allocatedbspmem += HL_MAX_MAP_MIPTEX * sizeof(byte); - //entities - hl_entdatasize = 0; - hl_dentdata = (char *) GetMemory(HL_MAX_MAP_ENTSTRING * sizeof(char)); - hl_allocatedbspmem += HL_MAX_MAP_ENTSTRING * sizeof(char); - //leaves - hl_numleafs = 0; - hl_dleafs = (hl_dleaf_t *) GetMemory(HL_MAX_MAP_LEAFS * sizeof(hl_dleaf_t)); - hl_allocatedbspmem += HL_MAX_MAP_LEAFS * sizeof(hl_dleaf_t); - //planes - hl_numplanes = 0; - hl_dplanes = (hl_dplane_t *) GetMemory(HL_MAX_MAP_PLANES * sizeof(hl_dplane_t)); - hl_allocatedbspmem += HL_MAX_MAP_PLANES * sizeof(hl_dplane_t); - //vertexes - hl_numvertexes = 0; - hl_dvertexes = (hl_dvertex_t *) GetMemory(HL_MAX_MAP_VERTS * sizeof(hl_dvertex_t)); - hl_allocatedbspmem += HL_MAX_MAP_VERTS * sizeof(hl_dvertex_t); - //nodes - hl_numnodes = 0; - hl_dnodes = (hl_dnode_t *) GetMemory(HL_MAX_MAP_NODES * sizeof(hl_dnode_t)); - hl_allocatedbspmem += HL_MAX_MAP_NODES * sizeof(hl_dnode_t); - //texture info - hl_numtexinfo = 0; - hl_texinfo = (hl_texinfo_t *) GetMemory(HL_MAX_MAP_TEXINFO * sizeof(hl_texinfo_t)); - hl_allocatedbspmem += HL_MAX_MAP_TEXINFO * sizeof(hl_texinfo_t); - //faces - hl_numfaces = 0; - hl_dfaces = (hl_dface_t *) GetMemory(HL_MAX_MAP_FACES * sizeof(hl_dface_t)); - hl_allocatedbspmem += HL_MAX_MAP_FACES * sizeof(hl_dface_t); - //clip nodes - hl_numclipnodes = 0; - hl_dclipnodes = (hl_dclipnode_t *) GetMemory(HL_MAX_MAP_CLIPNODES * sizeof(hl_dclipnode_t)); - hl_allocatedbspmem += HL_MAX_MAP_CLIPNODES * sizeof(hl_dclipnode_t); - //edges - hl_numedges = 0; - hl_dedges = (hl_dedge_t *) GetMemory(HL_MAX_MAP_EDGES * sizeof(hl_dedge_t)); - hl_allocatedbspmem += HL_MAX_MAP_EDGES, sizeof(hl_dedge_t); - //mark surfaces - hl_nummarksurfaces = 0; - hl_dmarksurfaces = (unsigned short *) GetMemory(HL_MAX_MAP_MARKSURFACES * sizeof(unsigned short)); - hl_allocatedbspmem += HL_MAX_MAP_MARKSURFACES * sizeof(unsigned short); - //surface edges - hl_numsurfedges = 0; - hl_dsurfedges = (int *) GetMemory(HL_MAX_MAP_SURFEDGES * sizeof(int)); - hl_allocatedbspmem += HL_MAX_MAP_SURFEDGES * sizeof(int); - //print allocated memory - Log_Print("allocated "); - PrintMemorySize(hl_allocatedbspmem); - Log_Print(" of BSP memory\n"); -} //end of the function HL_AllocMaxBSP - -void HL_FreeMaxBSP(void) -{ - //models - hl_nummodels = 0; - FreeMemory(hl_dmodels); - hl_dmodels = NULL; - //visibility - hl_visdatasize = 0; - FreeMemory(hl_dvisdata); - hl_dvisdata = NULL; - //light data - hl_lightdatasize = 0; - FreeMemory(hl_dlightdata); - hl_dlightdata = NULL; - //texture data - hl_texdatasize = 0; - FreeMemory(hl_dtexdata); - hl_dtexdata = NULL; - //entities - hl_entdatasize = 0; - FreeMemory(hl_dentdata); - hl_dentdata = NULL; - //leaves - hl_numleafs = 0; - FreeMemory(hl_dleafs); - hl_dleafs = NULL; - //planes - hl_numplanes = 0; - FreeMemory(hl_dplanes); - hl_dplanes = NULL; - //vertexes - hl_numvertexes = 0; - FreeMemory(hl_dvertexes); - hl_dvertexes = NULL; - //nodes - hl_numnodes = 0; - FreeMemory(hl_dnodes); - hl_dnodes = NULL; - //texture info - hl_numtexinfo = 0; - FreeMemory(hl_texinfo); - hl_texinfo = NULL; - //faces - hl_numfaces = 0; - FreeMemory(hl_dfaces); - hl_dfaces = NULL; - //clip nodes - hl_numclipnodes = 0; - FreeMemory(hl_dclipnodes); - hl_dclipnodes = NULL; - //edges - hl_numedges = 0; - FreeMemory(hl_dedges); - hl_dedges = NULL; - //mark surfaces - hl_nummarksurfaces = 0; - FreeMemory(hl_dmarksurfaces); - hl_dmarksurfaces = NULL; - //surface edges - hl_numsurfedges = 0; - FreeMemory(hl_dsurfedges); - hl_dsurfedges = NULL; - // - Log_Print("freed "); - PrintMemorySize(hl_allocatedbspmem); - Log_Print(" of BSP memory\n"); - hl_allocatedbspmem = 0; -} //end of the function HL_FreeMaxBSP -//#endif //ME - -/* -=============== -FastChecksum -=============== -*/ - -int FastChecksum(void *buffer, int bytes) -{ - int checksum = 0; - -// the result of FastChecksum isn't actually used anywhere -// while( bytes-- ) -// checksum = (checksum << 4) ^ *((char *)buffer)++; - - return checksum; -} - -/* -=============== -HL_CompressVis -=============== -*/ -int HL_CompressVis(byte *vis, byte *dest) -{ - int j; - int rep; - int visrow; - byte *dest_p; - - dest_p = dest; - visrow = (hl_numleafs + 7)>>3; - - for (j=0 ; j>3; - out = decompressed; - - do - { - if (*in) - { - *out++ = *in++; - continue; - } - - c = in[1]; - in += 2; - while (c) - { - *out++ = 0; - c--; - } - } while (out - decompressed < row); -} - -//============================================================================= - -/* -============= -HL_SwapBSPFile - -Byte swaps all data in a bsp file. -============= -*/ -void HL_SwapBSPFile (qboolean todisk) -{ - int i, j, k, c; - hl_dmodel_t *d; - hl_dmiptexlump_t *mtl; - - -// models - for (i = 0; i < hl_nummodels; i++) - { - d = &hl_dmodels[i]; - - for (j = 0; j < HL_MAX_MAP_HULLS; j++) - d->headnode[j] = LittleLong(d->headnode[j]); - - d->visleafs = LittleLong(d->visleafs); - d->firstface = LittleLong(d->firstface); - d->numfaces = LittleLong(d->numfaces); - - for (j = 0; j < 3; j++) - { - d->mins[j] = LittleFloat(d->mins[j]); - d->maxs[j] = LittleFloat(d->maxs[j]); - d->origin[j] = LittleFloat(d->origin[j]); - } - } - -// -// vertexes -// - for (i = 0; i < hl_numvertexes; i++) - { - for (j = 0; j < 3; j++) - hl_dvertexes[i].point[j] = LittleFloat (hl_dvertexes[i].point[j]); - } - -// -// planes -// - for (i=0 ; inummiptex; - else - c = LittleLong(mtl->nummiptex); - mtl->nummiptex = LittleLong (mtl->nummiptex); - for (i=0 ; idataofs[i] = LittleLong(mtl->dataofs[i]); - } - -// -// marksurfaces -// - for (i=0 ; ilumps[lump].filelen; - ofs = hl_header->lumps[lump].fileofs; - - if (length % size) { - Error ("LoadBSPFile: odd lump size"); - } - // somehow things got out of range - if ((length/size) > maxsize) { - printf("WARNING: exceeded max size for lump %d size %d > maxsize %d\n", lump, (length/size), maxsize); - length = maxsize * size; - } - if ( ofs + length > hl_fileLength ) { - printf("WARNING: exceeded file length for lump %d\n", lump); - length = hl_fileLength - ofs; - if ( length <= 0 ) { - return 0; - } - } - - memcpy (dest, (byte *)hl_header + ofs, length); - - return length / size; -} - -/* -============= -HL_LoadBSPFile -============= -*/ -void HL_LoadBSPFile (char *filename, int offset, int length) -{ - int i; - -// -// load the file header -// - hl_fileLength = LoadFile (filename, (void **)&hl_header, offset, length); - -// swap the header - for (i=0 ; i< sizeof(hl_dheader_t)/4 ; i++) - ((int *)hl_header)[i] = LittleLong ( ((int *)hl_header)[i]); - - if (hl_header->version != HL_BSPVERSION) - Error ("%s is version %i, not %i", filename, hl_header->version, HL_BSPVERSION); - - hl_nummodels = HL_CopyLump (HL_LUMP_MODELS, hl_dmodels, sizeof(hl_dmodel_t), HL_MAX_MAP_MODELS ); - hl_numvertexes = HL_CopyLump (HL_LUMP_VERTEXES, hl_dvertexes, sizeof(hl_dvertex_t), HL_MAX_MAP_VERTS ); - hl_numplanes = HL_CopyLump (HL_LUMP_PLANES, hl_dplanes, sizeof(hl_dplane_t), HL_MAX_MAP_PLANES ); - hl_numleafs = HL_CopyLump (HL_LUMP_LEAFS, hl_dleafs, sizeof(hl_dleaf_t), HL_MAX_MAP_LEAFS ); - hl_numnodes = HL_CopyLump (HL_LUMP_NODES, hl_dnodes, sizeof(hl_dnode_t), HL_MAX_MAP_NODES ); - hl_numtexinfo = HL_CopyLump (HL_LUMP_TEXINFO, hl_texinfo, sizeof(hl_texinfo_t), HL_MAX_MAP_TEXINFO ); - hl_numclipnodes = HL_CopyLump (HL_LUMP_CLIPNODES, hl_dclipnodes, sizeof(hl_dclipnode_t), HL_MAX_MAP_CLIPNODES ); - hl_numfaces = HL_CopyLump (HL_LUMP_FACES, hl_dfaces, sizeof(hl_dface_t), HL_MAX_MAP_FACES ); - hl_nummarksurfaces = HL_CopyLump (HL_LUMP_MARKSURFACES, hl_dmarksurfaces, sizeof(hl_dmarksurfaces[0]), HL_MAX_MAP_MARKSURFACES ); - hl_numsurfedges = HL_CopyLump (HL_LUMP_SURFEDGES, hl_dsurfedges, sizeof(hl_dsurfedges[0]), HL_MAX_MAP_SURFEDGES ); - hl_numedges = HL_CopyLump (HL_LUMP_EDGES, hl_dedges, sizeof(hl_dedge_t), HL_MAX_MAP_EDGES ); - - hl_texdatasize = HL_CopyLump (HL_LUMP_TEXTURES, hl_dtexdata, 1, HL_MAX_MAP_MIPTEX ); - hl_visdatasize = HL_CopyLump (HL_LUMP_VISIBILITY, hl_dvisdata, 1, HL_MAX_MAP_VISIBILITY ); - hl_lightdatasize = HL_CopyLump (HL_LUMP_LIGHTING, hl_dlightdata, 1, HL_MAX_MAP_LIGHTING ); - hl_entdatasize = HL_CopyLump (HL_LUMP_ENTITIES, hl_dentdata, 1, HL_MAX_MAP_ENTSTRING ); - - FreeMemory(hl_header); // everything has been copied out - -// -// swap everything -// - HL_SwapBSPFile (false); - - hl_dmodels_checksum = FastChecksum( hl_dmodels, hl_nummodels*sizeof(hl_dmodels[0]) ); - hl_dvertexes_checksum = FastChecksum( hl_dvertexes, hl_numvertexes*sizeof(hl_dvertexes[0]) ); - hl_dplanes_checksum = FastChecksum( hl_dplanes, hl_numplanes*sizeof(hl_dplanes[0]) ); - hl_dleafs_checksum = FastChecksum( hl_dleafs, hl_numleafs*sizeof(hl_dleafs[0]) ); - hl_dnodes_checksum = FastChecksum( hl_dnodes, hl_numnodes*sizeof(hl_dnodes[0]) ); - hl_texinfo_checksum = FastChecksum( hl_texinfo, hl_numtexinfo*sizeof(hl_texinfo[0]) ); - hl_dclipnodes_checksum = FastChecksum( hl_dclipnodes, hl_numclipnodes*sizeof(hl_dclipnodes[0]) ); - hl_dfaces_checksum = FastChecksum( hl_dfaces, hl_numfaces*sizeof(hl_dfaces[0]) ); - hl_dmarksurfaces_checksum = FastChecksum( hl_dmarksurfaces, hl_nummarksurfaces*sizeof(hl_dmarksurfaces[0]) ); - hl_dsurfedges_checksum = FastChecksum( hl_dsurfedges, hl_numsurfedges*sizeof(hl_dsurfedges[0]) ); - hl_dedges_checksum = FastChecksum( hl_dedges, hl_numedges*sizeof(hl_dedges[0]) ); - hl_dtexdata_checksum = FastChecksum( hl_dtexdata, hl_numedges*sizeof(hl_dtexdata[0]) ); - hl_dvisdata_checksum = FastChecksum( hl_dvisdata, hl_visdatasize*sizeof(hl_dvisdata[0]) ); - hl_dlightdata_checksum = FastChecksum( hl_dlightdata, hl_lightdatasize*sizeof(hl_dlightdata[0]) ); - hl_dentdata_checksum = FastChecksum( hl_dentdata, hl_entdatasize*sizeof(hl_dentdata[0]) ); - -} - -//============================================================================ - -FILE *wadfile; -hl_dheader_t outheader; - -void HL_AddLump (int lumpnum, void *data, int len) -{ - hl_lump_t *lump; - - lump = &hl_header->lumps[lumpnum]; - - lump->fileofs = LittleLong( ftell(wadfile) ); - lump->filelen = LittleLong(len); - SafeWrite (wadfile, data, (len+3)&~3); -} - -/* -============= -HL_WriteBSPFile - -Swaps the bsp file in place, so it should not be referenced again -============= -*/ -void HL_WriteBSPFile (char *filename) -{ - hl_header = &outheader; - memset (hl_header, 0, sizeof(hl_dheader_t)); - - HL_SwapBSPFile (true); - - hl_header->version = LittleLong (HL_BSPVERSION); - - wadfile = SafeOpenWrite (filename); - SafeWrite (wadfile, hl_header, sizeof(hl_dheader_t)); // overwritten later - - HL_AddLump (HL_LUMP_PLANES, hl_dplanes, hl_numplanes*sizeof(hl_dplane_t)); - HL_AddLump (HL_LUMP_LEAFS, hl_dleafs, hl_numleafs*sizeof(hl_dleaf_t)); - HL_AddLump (HL_LUMP_VERTEXES, hl_dvertexes, hl_numvertexes*sizeof(hl_dvertex_t)); - HL_AddLump (HL_LUMP_NODES, hl_dnodes, hl_numnodes*sizeof(hl_dnode_t)); - HL_AddLump (HL_LUMP_TEXINFO, hl_texinfo, hl_numtexinfo*sizeof(hl_texinfo_t)); - HL_AddLump (HL_LUMP_FACES, hl_dfaces, hl_numfaces*sizeof(hl_dface_t)); - HL_AddLump (HL_LUMP_CLIPNODES, hl_dclipnodes, hl_numclipnodes*sizeof(hl_dclipnode_t)); - HL_AddLump (HL_LUMP_MARKSURFACES, hl_dmarksurfaces, hl_nummarksurfaces*sizeof(hl_dmarksurfaces[0])); - HL_AddLump (HL_LUMP_SURFEDGES, hl_dsurfedges, hl_numsurfedges*sizeof(hl_dsurfedges[0])); - HL_AddLump (HL_LUMP_EDGES, hl_dedges, hl_numedges*sizeof(hl_dedge_t)); - HL_AddLump (HL_LUMP_MODELS, hl_dmodels, hl_nummodels*sizeof(hl_dmodel_t)); - - HL_AddLump (HL_LUMP_LIGHTING, hl_dlightdata, hl_lightdatasize); - HL_AddLump (HL_LUMP_VISIBILITY, hl_dvisdata, hl_visdatasize); - HL_AddLump (HL_LUMP_ENTITIES, hl_dentdata, hl_entdatasize); - HL_AddLump (HL_LUMP_TEXTURES, hl_dtexdata, hl_texdatasize); - - fseek (wadfile, 0, SEEK_SET); - SafeWrite (wadfile, hl_header, sizeof(hl_dheader_t)); - fclose (wadfile); -} - -//============================================================================ - -#define ENTRIES(a) (sizeof(a)/sizeof(*(a))) -#define ENTRYSIZE(a) (sizeof(*(a))) - -unsigned ArrayUsage( char *szItem, int items, int maxitems, int itemsize ) -{ - float percentage = maxitems ? items * 100.0 / maxitems : 0.0; - - qprintf("%-12s %7i/%-7i %7i/%-7i (%4.1f%%)", - szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage ); - if ( percentage > 80.0 ) - qprintf( "VERY FULL!\n" ); - else if ( percentage > 95.0 ) - qprintf( "SIZE DANGER!\n" ); - else if ( percentage > 99.9 ) - qprintf( "SIZE OVERFLOW!!!\n" ); - else - qprintf( "\n" ); - return items * itemsize; -} - -unsigned GlobUsage( char *szItem, int itemstorage, int maxstorage ) -{ - float percentage = maxstorage ? itemstorage * 100.0 / maxstorage : 0.0; - - qprintf("%-12s [variable] %7i/%-7i (%4.1f%%)", - szItem, itemstorage, maxstorage, percentage ); - if ( percentage > 80.0 ) - qprintf( "VERY FULL!\n" ); - else if ( percentage > 95.0 ) - qprintf( "SIZE DANGER!\n" ); - else if ( percentage > 99.9 ) - qprintf( "SIZE OVERFLOW!!!\n" ); - else - qprintf( "\n" ); - return itemstorage; -} - -/* -============= -HL_PrintBSPFileSizes - -Dumps info about current file -============= -*/ -void HL_PrintBSPFileSizes(void) -{ - int totalmemory = 0; - - qprintf("\n"); - qprintf("Object names Objects/Maxobjs Memory / Maxmem Fullness\n" ); - qprintf("------------ --------------- --------------- --------\n" ); - - totalmemory += ArrayUsage( "models", hl_nummodels, ENTRIES(hl_dmodels), ENTRYSIZE(hl_dmodels) ); - totalmemory += ArrayUsage( "planes", hl_numplanes, ENTRIES(hl_dplanes), ENTRYSIZE(hl_dplanes) ); - totalmemory += ArrayUsage( "vertexes", hl_numvertexes, ENTRIES(hl_dvertexes), ENTRYSIZE(hl_dvertexes) ); - totalmemory += ArrayUsage( "nodes", hl_numnodes, ENTRIES(hl_dnodes), ENTRYSIZE(hl_dnodes) ); - totalmemory += ArrayUsage( "texinfos", hl_numtexinfo, ENTRIES(hl_texinfo), ENTRYSIZE(hl_texinfo) ); - totalmemory += ArrayUsage( "faces", hl_numfaces, ENTRIES(hl_dfaces), ENTRYSIZE(hl_dfaces) ); - totalmemory += ArrayUsage( "clipnodes", hl_numclipnodes, ENTRIES(hl_dclipnodes), ENTRYSIZE(hl_dclipnodes) ); - totalmemory += ArrayUsage( "leaves", hl_numleafs, ENTRIES(hl_dleafs), ENTRYSIZE(hl_dleafs) ); - totalmemory += ArrayUsage( "marksurfaces",hl_nummarksurfaces,ENTRIES(hl_dmarksurfaces),ENTRYSIZE(hl_dmarksurfaces) ); - totalmemory += ArrayUsage( "surfedges", hl_numsurfedges, ENTRIES(hl_dsurfedges), ENTRYSIZE(hl_dsurfedges) ); - totalmemory += ArrayUsage( "edges", hl_numedges, ENTRIES(hl_dedges), ENTRYSIZE(hl_dedges) ); - - totalmemory += GlobUsage( "texdata", hl_texdatasize, sizeof(hl_dtexdata) ); - totalmemory += GlobUsage( "lightdata", hl_lightdatasize, sizeof(hl_dlightdata) ); - totalmemory += GlobUsage( "visdata", hl_visdatasize, sizeof(hl_dvisdata) ); - totalmemory += GlobUsage( "entdata", hl_entdatasize, sizeof(hl_dentdata) ); - - qprintf( "=== Total BSP file data space used: %d bytes ===\n\n", totalmemory ); -} - - - -/* -================= -ParseEpair -================= -* / -epair_t *ParseEpair (void) -{ - epair_t *e; - - e = malloc (sizeof(epair_t)); - memset (e, 0, sizeof(epair_t)); - - if (strlen(token) >= MAX_KEY-1) - Error ("ParseEpar: token too long"); - e->key = copystring(token); - GetToken (false); - if (strlen(token) >= MAX_VALUE-1) - Error ("ParseEpar: token too long"); - e->value = copystring(token); - - return e; -} //*/ - - -/* -================ -ParseEntity -================ -* / -qboolean ParseEntity (void) -{ - epair_t *e; - entity_t *mapent; - - if (!GetToken (true)) - return false; - - if (strcmp (token, "{") ) - Error ("ParseEntity: { not found"); - - if (num_entities == HL_MAX_MAP_ENTITIES) - Error ("num_entities == HL_MAX_MAP_ENTITIES"); - - mapent = &entities[num_entities]; - num_entities++; - - do - { - if (!GetToken (true)) - Error ("ParseEntity: EOF without closing brace"); - if (!strcmp (token, "}") ) - break; - e = ParseEpair (); - e->next = mapent->epairs; - mapent->epairs = e; - } while (1); - - return true; -} //*/ - -/* -================ -ParseEntities - -Parses the dentdata string into entities -================ -*/ -void HL_ParseEntities (void) -{ - script_t *script; - - num_entities = 0; - script = LoadScriptMemory(hl_dentdata, hl_entdatasize, "*Half-Life bsp file"); - SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES | - SCFL_NOSTRINGESCAPECHARS); - - while(ParseEntity(script)) - { - } //end while - - FreeScript(script); -} //end of the function HL_ParseEntities - - -/* -================ -UnparseEntities - -Generates the dentdata string from all the entities -================ -*/ -void HL_UnparseEntities (void) -{ - char *buf, *end; - epair_t *ep; - char line[2048]; - int i; - - buf = hl_dentdata; - end = buf; - *end = 0; - - for (i=0 ; inext) - { - sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value); - strcat (end, line); - end += strlen(line); - } - strcat (end,"}\n"); - end += 2; - - if (end > buf + HL_MAX_MAP_ENTSTRING) - Error ("Entity text too long"); - } - hl_entdatasize = end - buf + 1; -} //end of the function HL_UnparseEntities - - -/* -void SetKeyValue (entity_t *ent, char *key, char *value) -{ - epair_t *ep; - - for (ep=ent->epairs ; ep ; ep=ep->next) - if (!strcmp (ep->key, key) ) - { - free (ep->value); - ep->value = copystring(value); - return; - } - ep = malloc (sizeof(*ep)); - ep->next = ent->epairs; - ent->epairs = ep; - ep->key = copystring(key); - ep->value = copystring(value); -} - -char *ValueForKey (entity_t *ent, char *key) -{ - epair_t *ep; - - for (ep=ent->epairs ; ep ; ep=ep->next) - if (!strcmp (ep->key, key) ) - return ep->value; - return ""; -} - -vec_t FloatForKey (entity_t *ent, char *key) -{ - char *k; - - k = ValueForKey (ent, key); - return atof(k); -} - -void GetVectorForKey (entity_t *ent, char *key, vec3_t vec) -{ - char *k; - double v1, v2, v3; - - k = ValueForKey (ent, key); -// scanf into doubles, then assign, so it is vec_t size independent - v1 = v2 = v3 = 0; - sscanf (k, "%lf %lf %lf", &v1, &v2, &v3); - vec[0] = v1; - vec[1] = v2; - vec[2] = v3; -} //*/ diff --git a/tools/quake3/bspc/l_bsp_hl.h b/tools/quake3/bspc/l_bsp_hl.h deleted file mode 100644 index 41462020..00000000 --- a/tools/quake3/bspc/l_bsp_hl.h +++ /dev/null @@ -1,314 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// upper design bounds - -#define HL_MAX_MAP_HULLS 4 - -#define HL_MAX_MAP_MODELS 400 -#define HL_MAX_MAP_BRUSHES 4096 -#define HL_MAX_MAP_ENTITIES 1024 -#define HL_MAX_MAP_ENTSTRING (128*1024) - -#define HL_MAX_MAP_PLANES 32767 -#define HL_MAX_MAP_NODES 32767 // because negative shorts are contents -#define HL_MAX_MAP_CLIPNODES 32767 // -#define HL_MAX_MAP_LEAFS 8192 -#define HL_MAX_MAP_VERTS 65535 -#define HL_MAX_MAP_FACES 65535 -#define HL_MAX_MAP_MARKSURFACES 65535 -#define HL_MAX_MAP_TEXINFO 8192 -#define HL_MAX_MAP_EDGES 256000 -#define HL_MAX_MAP_SURFEDGES 512000 -#define HL_MAX_MAP_TEXTURES 512 -#define HL_MAX_MAP_MIPTEX 0x200000 -#define HL_MAX_MAP_LIGHTING 0x200000 -#define HL_MAX_MAP_VISIBILITY 0x200000 - -#define HL_MAX_MAP_PORTALS 65536 - -// key / value pair sizes - -#define MAX_KEY 32 -#define MAX_VALUE 1024 - -//============================================================================= - - -#define HL_BSPVERSION 30 -#define HL_TOOLVERSION 2 - - -typedef struct -{ - int fileofs, filelen; -} hl_lump_t; - -#define HL_LUMP_ENTITIES 0 -#define HL_LUMP_PLANES 1 -#define HL_LUMP_TEXTURES 2 -#define HL_LUMP_VERTEXES 3 -#define HL_LUMP_VISIBILITY 4 -#define HL_LUMP_NODES 5 -#define HL_LUMP_TEXINFO 6 -#define HL_LUMP_FACES 7 -#define HL_LUMP_LIGHTING 8 -#define HL_LUMP_CLIPNODES 9 -#define HL_LUMP_LEAFS 10 -#define HL_LUMP_MARKSURFACES 11 -#define HL_LUMP_EDGES 12 -#define HL_LUMP_SURFEDGES 13 -#define HL_LUMP_MODELS 14 - -#define HL_HEADER_LUMPS 15 - -typedef struct -{ - float mins[3], maxs[3]; - float origin[3]; - int headnode[HL_MAX_MAP_HULLS]; - int visleafs; // not including the solid leaf 0 - int firstface, numfaces; -} hl_dmodel_t; - -typedef struct -{ - int version; - hl_lump_t lumps[HL_HEADER_LUMPS]; -} hl_dheader_t; - -typedef struct -{ - int nummiptex; - int dataofs[4]; // [nummiptex] -} hl_dmiptexlump_t; - -#define MIPLEVELS 4 -typedef struct hl_miptex_s -{ - char name[16]; - unsigned width, height; - unsigned offsets[MIPLEVELS]; // four mip maps stored -} hl_miptex_t; - - -typedef struct -{ - float point[3]; -} hl_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 -} hl_dplane_t; - - - -#define HL_CONTENTS_EMPTY -1 -#define HL_CONTENTS_SOLID -2 -#define HL_CONTENTS_WATER -3 -#define HL_CONTENTS_SLIME -4 -#define HL_CONTENTS_LAVA -5 -#define HL_CONTENTS_SKY -6 -#define HL_CONTENTS_ORIGIN -7 // removed at csg time -#define HL_CONTENTS_CLIP -8 // changed to contents_solid - -#define HL_CONTENTS_CURRENT_0 -9 -#define HL_CONTENTS_CURRENT_90 -10 -#define HL_CONTENTS_CURRENT_180 -11 -#define HL_CONTENTS_CURRENT_270 -12 -#define HL_CONTENTS_CURRENT_UP -13 -#define HL_CONTENTS_CURRENT_DOWN -14 - -#define HL_CONTENTS_TRANSLUCENT -15 - -// !!! 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 -} hl_dnode_t; - -typedef struct -{ - int planenum; - short children[2]; // negative numbers are contents -} hl_dclipnode_t; - - -typedef struct hl_texinfo_s -{ - float vecs[2][4]; // [s/t][xyz offset] - int miptex; - int flags; -} hl_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 -} hl_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 -} hl_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 HL_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]; -} hl_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 hl_nummodels; -extern hl_dmodel_t *hl_dmodels;//[MAX_MAP_MODELS]; -extern int hl_dmodels_checksum; - -extern int hl_visdatasize; -extern byte *hl_dvisdata;//[MAX_MAP_VISIBILITY]; -extern int hl_dvisdata_checksum; - -extern int hl_lightdatasize; -extern byte *hl_dlightdata;//[MAX_MAP_LIGHTING]; -extern int hl_dlightdata_checksum; - -extern int hl_texdatasize; -extern byte *hl_dtexdata;//[MAX_MAP_MIPTEX]; // (dmiptexlump_t) -extern int hl_dtexdata_checksum; - -extern int hl_entdatasize; -extern char *hl_dentdata;//[MAX_MAP_ENTSTRING]; -extern int hl_dentdata_checksum; - -extern int hl_numleafs; -extern hl_dleaf_t *hl_dleafs;//[MAX_MAP_LEAFS]; -extern int hl_dleafs_checksum; - -extern int hl_numplanes; -extern hl_dplane_t *hl_dplanes;//[MAX_MAP_PLANES]; -extern int hl_dplanes_checksum; - -extern int hl_numvertexes; -extern hl_dvertex_t *hl_dvertexes;//[MAX_MAP_VERTS]; -extern int hl_dvertexes_checksum; - -extern int hl_numnodes; -extern hl_dnode_t *hl_dnodes;//[MAX_MAP_NODES]; -extern int hl_dnodes_checksum; - -extern int hl_numtexinfo; -extern hl_texinfo_t *hl_texinfo;//[MAX_MAP_TEXINFO]; -extern int hl_texinfo_checksum; - -extern int hl_numfaces; -extern hl_dface_t *hl_dfaces;//[MAX_MAP_FACES]; -extern int hl_dfaces_checksum; - -extern int hl_numclipnodes; -extern hl_dclipnode_t *hl_dclipnodes;//[MAX_MAP_CLIPNODES]; -extern int hl_dclipnodes_checksum; - -extern int hl_numedges; -extern hl_dedge_t *hl_dedges;//[MAX_MAP_EDGES]; -extern int hl_dedges_checksum; - -extern int hl_nummarksurfaces; -extern unsigned short *hl_dmarksurfaces;//[MAX_MAP_MARKSURFACES]; -extern int hl_dmarksurfaces_checksum; - -extern int hl_numsurfedges; -extern int *hl_dsurfedges;//[MAX_MAP_SURFEDGES]; -extern int hl_dsurfedges_checksum; - -int FastChecksum(void *buffer, int bytes); - -void HL_AllocMaxBSP(void); -void HL_FreeMaxBSP(void); - -void HL_DecompressVis(byte *in, byte *decompressed); -int HL_CompressVis(byte *vis, byte *dest); - -void HL_LoadBSPFile(char *filename, int offset, int length); -void HL_WriteBSPFile(char *filename); -void HL_PrintBSPFileSizes(void); -void HL_PrintBSPFileSizes(void); -void HL_ParseEntities(void); -void HL_UnparseEntities(void); - -#endif diff --git a/tools/quake3/bspc/l_bsp_q1.c b/tools/quake3/bspc/l_bsp_q1.c deleted file mode 100644 index 89078556..00000000 --- a/tools/quake3/bspc/l_bsp_q1.c +++ /dev/null @@ -1,620 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "l_cmd.h" -#include "l_math.h" -#include "l_mem.h" -#include "l_log.h" -#include "botlib/l_script.h" -#include "l_bsp_q1.h" -#include "l_bsp_ent.h" - -//============================================================================= - -int q1_nummodels; -q1_dmodel_t *q1_dmodels;//[MAX_MAP_MODELS]; - -int q1_visdatasize; -byte *q1_dvisdata;//[MAX_MAP_VISIBILITY]; - -int q1_lightdatasize; -byte *q1_dlightdata;//[MAX_MAP_LIGHTING]; - -int q1_texdatasize; -byte *q1_dtexdata;//[MAX_MAP_MIPTEX]; // (dmiptexlump_t) - -int q1_entdatasize; -char *q1_dentdata;//[MAX_MAP_ENTSTRING]; - -int q1_numleafs; -q1_dleaf_t *q1_dleafs;//[MAX_MAP_LEAFS]; - -int q1_numplanes; -q1_dplane_t *q1_dplanes;//[MAX_MAP_PLANES]; - -int q1_numvertexes; -q1_dvertex_t *q1_dvertexes;//[MAX_MAP_VERTS]; - -int q1_numnodes; -q1_dnode_t *q1_dnodes;//[MAX_MAP_NODES]; - -int q1_numtexinfo; -q1_texinfo_t *q1_texinfo;//[MAX_MAP_TEXINFO]; - -int q1_numfaces; -q1_dface_t *q1_dfaces;//[MAX_MAP_FACES]; - -int q1_numclipnodes; -q1_dclipnode_t *q1_dclipnodes;//[MAX_MAP_CLIPNODES]; - -int q1_numedges; -q1_dedge_t *q1_dedges;//[MAX_MAP_EDGES]; - -int q1_nummarksurfaces; -unsigned short *q1_dmarksurfaces;//[MAX_MAP_MARKSURFACES]; - -int q1_numsurfedges; -int *q1_dsurfedges;//[MAX_MAP_SURFEDGES]; - -//============================================================================= - -int q1_bspallocated = false; -int q1_allocatedbspmem = 0; - -void Q1_AllocMaxBSP(void) -{ - //models - q1_nummodels = 0; - q1_dmodels = (q1_dmodel_t *) GetMemory(Q1_MAX_MAP_MODELS * sizeof(q1_dmodel_t)); - q1_allocatedbspmem = Q1_MAX_MAP_MODELS * sizeof(q1_dmodel_t); - //visibility - q1_visdatasize = 0; - q1_dvisdata = (byte *) GetMemory(Q1_MAX_MAP_VISIBILITY * sizeof(byte)); - q1_allocatedbspmem += Q1_MAX_MAP_VISIBILITY * sizeof(byte); - //light data - q1_lightdatasize = 0; - q1_dlightdata = (byte *) GetMemory(Q1_MAX_MAP_LIGHTING * sizeof(byte)); - q1_allocatedbspmem += Q1_MAX_MAP_LIGHTING * sizeof(byte); - //texture data - q1_texdatasize = 0; - q1_dtexdata = (byte *) GetMemory(Q1_MAX_MAP_MIPTEX * sizeof(byte)); // (dmiptexlump_t) - q1_allocatedbspmem += Q1_MAX_MAP_MIPTEX * sizeof(byte); - //entities - q1_entdatasize = 0; - q1_dentdata = (char *) GetMemory(Q1_MAX_MAP_ENTSTRING * sizeof(char)); - q1_allocatedbspmem += Q1_MAX_MAP_ENTSTRING * sizeof(char); - //leaves - q1_numleafs = 0; - q1_dleafs = (q1_dleaf_t *) GetMemory(Q1_MAX_MAP_LEAFS * sizeof(q1_dleaf_t)); - q1_allocatedbspmem += Q1_MAX_MAP_LEAFS * sizeof(q1_dleaf_t); - //planes - q1_numplanes = 0; - q1_dplanes = (q1_dplane_t *) GetMemory(Q1_MAX_MAP_PLANES * sizeof(q1_dplane_t)); - q1_allocatedbspmem += Q1_MAX_MAP_PLANES * sizeof(q1_dplane_t); - //vertexes - q1_numvertexes = 0; - q1_dvertexes = (q1_dvertex_t *) GetMemory(Q1_MAX_MAP_VERTS * sizeof(q1_dvertex_t)); - q1_allocatedbspmem += Q1_MAX_MAP_VERTS * sizeof(q1_dvertex_t); - //nodes - q1_numnodes = 0; - q1_dnodes = (q1_dnode_t *) GetMemory(Q1_MAX_MAP_NODES * sizeof(q1_dnode_t)); - q1_allocatedbspmem += Q1_MAX_MAP_NODES * sizeof(q1_dnode_t); - //texture info - q1_numtexinfo = 0; - q1_texinfo = (q1_texinfo_t *) GetMemory(Q1_MAX_MAP_TEXINFO * sizeof(q1_texinfo_t)); - q1_allocatedbspmem += Q1_MAX_MAP_TEXINFO * sizeof(q1_texinfo_t); - //faces - q1_numfaces = 0; - q1_dfaces = (q1_dface_t *) GetMemory(Q1_MAX_MAP_FACES * sizeof(q1_dface_t)); - q1_allocatedbspmem += Q1_MAX_MAP_FACES * sizeof(q1_dface_t); - //clip nodes - q1_numclipnodes = 0; - q1_dclipnodes = (q1_dclipnode_t *) GetMemory(Q1_MAX_MAP_CLIPNODES * sizeof(q1_dclipnode_t)); - q1_allocatedbspmem += Q1_MAX_MAP_CLIPNODES * sizeof(q1_dclipnode_t); - //edges - q1_numedges = 0; - q1_dedges = (q1_dedge_t *) GetMemory(Q1_MAX_MAP_EDGES * sizeof(q1_dedge_t)); - q1_allocatedbspmem += Q1_MAX_MAP_EDGES, sizeof(q1_dedge_t); - //mark surfaces - q1_nummarksurfaces = 0; - q1_dmarksurfaces = (unsigned short *) GetMemory(Q1_MAX_MAP_MARKSURFACES * sizeof(unsigned short)); - q1_allocatedbspmem += Q1_MAX_MAP_MARKSURFACES * sizeof(unsigned short); - //surface edges - q1_numsurfedges = 0; - q1_dsurfedges = (int *) GetMemory(Q1_MAX_MAP_SURFEDGES * sizeof(int)); - q1_allocatedbspmem += Q1_MAX_MAP_SURFEDGES * sizeof(int); - //print allocated memory - Log_Print("allocated "); - PrintMemorySize(q1_allocatedbspmem); - Log_Print(" of BSP memory\n"); -} //end of the function Q1_AllocMaxBSP - -void Q1_FreeMaxBSP(void) -{ - //models - q1_nummodels = 0; - FreeMemory(q1_dmodels); - q1_dmodels = NULL; - //visibility - q1_visdatasize = 0; - FreeMemory(q1_dvisdata); - q1_dvisdata = NULL; - //light data - q1_lightdatasize = 0; - FreeMemory(q1_dlightdata); - q1_dlightdata = NULL; - //texture data - q1_texdatasize = 0; - FreeMemory(q1_dtexdata); - q1_dtexdata = NULL; - //entities - q1_entdatasize = 0; - FreeMemory(q1_dentdata); - q1_dentdata = NULL; - //leaves - q1_numleafs = 0; - FreeMemory(q1_dleafs); - q1_dleafs = NULL; - //planes - q1_numplanes = 0; - FreeMemory(q1_dplanes); - q1_dplanes = NULL; - //vertexes - q1_numvertexes = 0; - FreeMemory(q1_dvertexes); - q1_dvertexes = NULL; - //nodes - q1_numnodes = 0; - FreeMemory(q1_dnodes); - q1_dnodes = NULL; - //texture info - q1_numtexinfo = 0; - FreeMemory(q1_texinfo); - q1_texinfo = NULL; - //faces - q1_numfaces = 0; - FreeMemory(q1_dfaces); - q1_dfaces = NULL; - //clip nodes - q1_numclipnodes = 0; - FreeMemory(q1_dclipnodes); - q1_dclipnodes = NULL; - //edges - q1_numedges = 0; - FreeMemory(q1_dedges); - q1_dedges = NULL; - //mark surfaces - q1_nummarksurfaces = 0; - FreeMemory(q1_dmarksurfaces); - q1_dmarksurfaces = NULL; - //surface edges - q1_numsurfedges = 0; - FreeMemory(q1_dsurfedges); - q1_dsurfedges = NULL; - // - Log_Print("freed "); - PrintMemorySize(q1_allocatedbspmem); - Log_Print(" of BSP memory\n"); - q1_allocatedbspmem = 0; -} //end of the function Q1_FreeMaxBSP -//#endif //ME - -/* -============= -Q1_SwapBSPFile - -Byte swaps all data in a bsp file. -============= -*/ -void Q1_SwapBSPFile (qboolean todisk) -{ - int i, j, c; - q1_dmodel_t *d; - q1_dmiptexlump_t *mtl; - - -// models - for (i=0 ; iheadnode[j] = LittleLong (d->headnode[j]); - - d->visleafs = LittleLong (d->visleafs); - d->firstface = LittleLong (d->firstface); - d->numfaces = LittleLong (d->numfaces); - - for (j=0 ; j<3 ; j++) - { - d->mins[j] = LittleFloat(d->mins[j]); - d->maxs[j] = LittleFloat(d->maxs[j]); - d->origin[j] = LittleFloat(d->origin[j]); - } - } - -// -// vertexes -// - for (i=0 ; inummiptex; - else - c = LittleLong(mtl->nummiptex); - mtl->nummiptex = LittleLong (mtl->nummiptex); - for (i=0 ; idataofs[i] = LittleLong(mtl->dataofs[i]); - } - -// -// marksurfaces -// - for (i=0 ; ilumps[lump].filelen; - ofs = q1_header->lumps[lump].fileofs; - - if (length % size) { - Error ("LoadBSPFile: odd lump size"); - } - // somehow things got out of range - if ((length/size) > maxsize) { - printf("WARNING: exceeded max size for lump %d size %d > maxsize %d\n", lump, (length/size), maxsize); - length = maxsize * size; - } - if ( ofs + length > q1_fileLength ) { - printf("WARNING: exceeded file length for lump %d\n", lump); - length = q1_fileLength - ofs; - if ( length <= 0 ) { - return 0; - } - } - - memcpy (dest, (byte *)q1_header + ofs, length); - - return length / size; -} - -/* -============= -Q1_LoadBSPFile -============= -*/ -void Q1_LoadBSPFile(char *filename, int offset, int length) -{ - int i; - -// -// load the file header -// - q1_fileLength = LoadFile(filename, (void **)&q1_header, offset, length); - -// swap the header - for (i=0 ; i< sizeof(q1_dheader_t)/4 ; i++) - ((int *)q1_header)[i] = LittleLong ( ((int *)q1_header)[i]); - - if (q1_header->version != Q1_BSPVERSION) - Error ("%s is version %i, not %i", filename, i, Q1_BSPVERSION); - - q1_nummodels = Q1_CopyLump (Q1_LUMP_MODELS, q1_dmodels, sizeof(q1_dmodel_t), Q1_MAX_MAP_MODELS ); - q1_numvertexes = Q1_CopyLump (Q1_LUMP_VERTEXES, q1_dvertexes, sizeof(q1_dvertex_t), Q1_MAX_MAP_VERTS ); - q1_numplanes = Q1_CopyLump (Q1_LUMP_PLANES, q1_dplanes, sizeof(q1_dplane_t), Q1_MAX_MAP_PLANES ); - q1_numleafs = Q1_CopyLump (Q1_LUMP_LEAFS, q1_dleafs, sizeof(q1_dleaf_t), Q1_MAX_MAP_LEAFS ); - q1_numnodes = Q1_CopyLump (Q1_LUMP_NODES, q1_dnodes, sizeof(q1_dnode_t), Q1_MAX_MAP_NODES ); - q1_numtexinfo = Q1_CopyLump (Q1_LUMP_TEXINFO, q1_texinfo, sizeof(q1_texinfo_t), Q1_MAX_MAP_TEXINFO ); - q1_numclipnodes = Q1_CopyLump (Q1_LUMP_CLIPNODES, q1_dclipnodes, sizeof(q1_dclipnode_t), Q1_MAX_MAP_CLIPNODES ); - q1_numfaces = Q1_CopyLump (Q1_LUMP_FACES, q1_dfaces, sizeof(q1_dface_t), Q1_MAX_MAP_FACES ); - q1_nummarksurfaces = Q1_CopyLump (Q1_LUMP_MARKSURFACES, q1_dmarksurfaces, sizeof(q1_dmarksurfaces[0]), Q1_MAX_MAP_MARKSURFACES ); - q1_numsurfedges = Q1_CopyLump (Q1_LUMP_SURFEDGES, q1_dsurfedges, sizeof(q1_dsurfedges[0]), Q1_MAX_MAP_SURFEDGES ); - q1_numedges = Q1_CopyLump (Q1_LUMP_EDGES, q1_dedges, sizeof(q1_dedge_t), Q1_MAX_MAP_EDGES ); - - q1_texdatasize = Q1_CopyLump (Q1_LUMP_TEXTURES, q1_dtexdata, 1, Q1_MAX_MAP_MIPTEX ); - q1_visdatasize = Q1_CopyLump (Q1_LUMP_VISIBILITY, q1_dvisdata, 1, Q1_MAX_MAP_VISIBILITY ); - q1_lightdatasize = Q1_CopyLump (Q1_LUMP_LIGHTING, q1_dlightdata, 1, Q1_MAX_MAP_LIGHTING ); - q1_entdatasize = Q1_CopyLump (Q1_LUMP_ENTITIES, q1_dentdata, 1, Q1_MAX_MAP_ENTSTRING ); - - FreeMemory(q1_header); // everything has been copied out - -// -// swap everything -// - Q1_SwapBSPFile (false); -} - -//============================================================================ - -FILE *q1_wadfile; -q1_dheader_t q1_outheader; - -void Q1_AddLump (int lumpnum, void *data, int len) -{ - q1_lump_t *lump; - - lump = &q1_header->lumps[lumpnum]; - - lump->fileofs = LittleLong(ftell(q1_wadfile)); - lump->filelen = LittleLong(len); - SafeWrite(q1_wadfile, data, (len+3)&~3); -} - -/* -============= -Q1_WriteBSPFile - -Swaps the bsp file in place, so it should not be referenced again -============= -*/ -void Q1_WriteBSPFile (char *filename) -{ - q1_header = &q1_outheader; - memset (q1_header, 0, sizeof(q1_dheader_t)); - - Q1_SwapBSPFile (true); - - q1_header->version = LittleLong (Q1_BSPVERSION); - - q1_wadfile = SafeOpenWrite (filename); - SafeWrite (q1_wadfile, q1_header, sizeof(q1_dheader_t)); // overwritten later - - Q1_AddLump (Q1_LUMP_PLANES, q1_dplanes, q1_numplanes*sizeof(q1_dplane_t)); - Q1_AddLump (Q1_LUMP_LEAFS, q1_dleafs, q1_numleafs*sizeof(q1_dleaf_t)); - Q1_AddLump (Q1_LUMP_VERTEXES, q1_dvertexes, q1_numvertexes*sizeof(q1_dvertex_t)); - Q1_AddLump (Q1_LUMP_NODES, q1_dnodes, q1_numnodes*sizeof(q1_dnode_t)); - Q1_AddLump (Q1_LUMP_TEXINFO, q1_texinfo, q1_numtexinfo*sizeof(q1_texinfo_t)); - Q1_AddLump (Q1_LUMP_FACES, q1_dfaces, q1_numfaces*sizeof(q1_dface_t)); - Q1_AddLump (Q1_LUMP_CLIPNODES, q1_dclipnodes, q1_numclipnodes*sizeof(q1_dclipnode_t)); - Q1_AddLump (Q1_LUMP_MARKSURFACES, q1_dmarksurfaces, q1_nummarksurfaces*sizeof(q1_dmarksurfaces[0])); - Q1_AddLump (Q1_LUMP_SURFEDGES, q1_dsurfedges, q1_numsurfedges*sizeof(q1_dsurfedges[0])); - Q1_AddLump (Q1_LUMP_EDGES, q1_dedges, q1_numedges*sizeof(q1_dedge_t)); - Q1_AddLump (Q1_LUMP_MODELS, q1_dmodels, q1_nummodels*sizeof(q1_dmodel_t)); - - Q1_AddLump (Q1_LUMP_LIGHTING, q1_dlightdata, q1_lightdatasize); - Q1_AddLump (Q1_LUMP_VISIBILITY, q1_dvisdata, q1_visdatasize); - Q1_AddLump (Q1_LUMP_ENTITIES, q1_dentdata, q1_entdatasize); - Q1_AddLump (Q1_LUMP_TEXTURES, q1_dtexdata, q1_texdatasize); - - fseek (q1_wadfile, 0, SEEK_SET); - SafeWrite (q1_wadfile, q1_header, sizeof(q1_dheader_t)); - fclose (q1_wadfile); -} - -//============================================================================ - -/* -============= -Q1_PrintBSPFileSizes - -Dumps info about current file -============= -*/ -void Q1_PrintBSPFileSizes (void) -{ - printf ("%5i planes %6i\n" - ,q1_numplanes, (int)(q1_numplanes*sizeof(q1_dplane_t))); - printf ("%5i vertexes %6i\n" - ,q1_numvertexes, (int)(q1_numvertexes*sizeof(q1_dvertex_t))); - printf ("%5i nodes %6i\n" - ,q1_numnodes, (int)(q1_numnodes*sizeof(q1_dnode_t))); - printf ("%5i texinfo %6i\n" - ,q1_numtexinfo, (int)(q1_numtexinfo*sizeof(q1_texinfo_t))); - printf ("%5i faces %6i\n" - ,q1_numfaces, (int)(q1_numfaces*sizeof(q1_dface_t))); - printf ("%5i clipnodes %6i\n" - ,q1_numclipnodes, (int)(q1_numclipnodes*sizeof(q1_dclipnode_t))); - printf ("%5i leafs %6i\n" - ,q1_numleafs, (int)(q1_numleafs*sizeof(q1_dleaf_t))); - printf ("%5i marksurfaces %6i\n" - ,q1_nummarksurfaces, (int)(q1_nummarksurfaces*sizeof(q1_dmarksurfaces[0]))); - printf ("%5i surfedges %6i\n" - ,q1_numsurfedges, (int)(q1_numsurfedges*sizeof(q1_dmarksurfaces[0]))); - printf ("%5i edges %6i\n" - ,q1_numedges, (int)(q1_numedges*sizeof(q1_dedge_t))); - if (!q1_texdatasize) - printf (" 0 textures 0\n"); - else - printf ("%5i textures %6i\n",((q1_dmiptexlump_t*)q1_dtexdata)->nummiptex, q1_texdatasize); - printf (" lightdata %6i\n", q1_lightdatasize); - printf (" visdata %6i\n", q1_visdatasize); - printf (" entdata %6i\n", q1_entdatasize); -} //end of the function Q1_PrintBSPFileSizes - - -/* -================ -Q1_ParseEntities - -Parses the dentdata string into entities -================ -*/ -void Q1_ParseEntities (void) -{ - script_t *script; - - num_entities = 0; - script = LoadScriptMemory(q1_dentdata, q1_entdatasize, "*Quake1 bsp file"); - SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES | - SCFL_NOSTRINGESCAPECHARS); - - while(ParseEntity(script)) - { - } //end while - - FreeScript(script); -} //end of the function Q1_ParseEntities - - -/* -================ -Q1_UnparseEntities - -Generates the dentdata string from all the entities -================ -*/ -void Q1_UnparseEntities (void) -{ - char *buf, *end; - epair_t *ep; - char line[2048]; - int i; - - buf = q1_dentdata; - end = buf; - *end = 0; - - for (i=0 ; inext) - { - sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value); - strcat (end, line); - end += strlen(line); - } - strcat (end,"}\n"); - end += 2; - - if (end > buf + Q1_MAX_MAP_ENTSTRING) - Error ("Entity text too long"); - } - q1_entdatasize = end - buf + 1; -} //end of the function Q1_UnparseEntities diff --git a/tools/quake3/bspc/l_bsp_q1.h b/tools/quake3/bspc/l_bsp_q1.h deleted file mode 100644 index 7cf459ce..00000000 --- a/tools/quake3/bspc/l_bsp_q1.h +++ /dev/null @@ -1,275 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - - -// upper design bounds - -#define Q1_MAX_MAP_HULLS 4 - -#define Q1_MAX_MAP_MODELS 256 -#define Q1_MAX_MAP_BRUSHES 4096 -#define Q1_MAX_MAP_ENTITIES 1024 -#define Q1_MAX_MAP_ENTSTRING 65536 - -#define Q1_MAX_MAP_PLANES 8192 -#define Q1_MAX_MAP_NODES 32767 // because negative shorts are contents -#define Q1_MAX_MAP_CLIPNODES 32767 // -#define Q1_MAX_MAP_LEAFS 32767 // -#define Q1_MAX_MAP_VERTS 65535 -#define Q1_MAX_MAP_FACES 65535 -#define Q1_MAX_MAP_MARKSURFACES 65535 -#define Q1_MAX_MAP_TEXINFO 4096 -#define Q1_MAX_MAP_EDGES 256000 -#define Q1_MAX_MAP_SURFEDGES 512000 -#define Q1_MAX_MAP_MIPTEX 0x200000 -#define Q1_MAX_MAP_LIGHTING 0x100000 -#define Q1_MAX_MAP_VISIBILITY 0x100000 - -// key / value pair sizes - -#define MAX_KEY 32 -#define MAX_VALUE 1024 - -//============================================================================= - - -#define Q1_BSPVERSION 29 - -typedef struct -{ - int fileofs, filelen; -} q1_lump_t; - -#define Q1_LUMP_ENTITIES 0 -#define Q1_LUMP_PLANES 1 -#define Q1_LUMP_TEXTURES 2 -#define Q1_LUMP_VERTEXES 3 -#define Q1_LUMP_VISIBILITY 4 -#define Q1_LUMP_NODES 5 -#define Q1_LUMP_TEXINFO 6 -#define Q1_LUMP_FACES 7 -#define Q1_LUMP_LIGHTING 8 -#define Q1_LUMP_CLIPNODES 9 -#define Q1_LUMP_LEAFS 10 -#define Q1_LUMP_MARKSURFACES 11 -#define Q1_LUMP_EDGES 12 -#define Q1_LUMP_SURFEDGES 13 -#define Q1_LUMP_MODELS 14 - -#define Q1_HEADER_LUMPS 15 - -typedef struct -{ - float mins[3], maxs[3]; - float origin[3]; - int headnode[Q1_MAX_MAP_HULLS]; - int visleafs; // not including the solid leaf 0 - int firstface, numfaces; -} q1_dmodel_t; - -typedef struct -{ - int version; - q1_lump_t lumps[Q1_HEADER_LUMPS]; -} q1_dheader_t; - -typedef struct -{ - int nummiptex; - int dataofs[4]; // [nummiptex] -} q1_dmiptexlump_t; - -#define MIPLEVELS 4 -typedef struct q1_miptex_s -{ - char name[16]; - unsigned width, height; - unsigned offsets[MIPLEVELS]; // four mip maps stored -} q1_miptex_t; - - -typedef struct -{ - float point[3]; -} q1_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 -} q1_dplane_t; - - - -#define Q1_CONTENTS_EMPTY -1 -#define Q1_CONTENTS_SOLID -2 -#define Q1_CONTENTS_WATER -3 -#define Q1_CONTENTS_SLIME -4 -#define Q1_CONTENTS_LAVA -5 -#define Q1_CONTENTS_SKY -6 - -// !!! if this is changed, it must be changed in asm_i386.h too !!! -typedef struct -{ - int planenum; - short children[2]; // negative numbers are -(leafs+1), not nodes - short mins[3]; // for sphere culling - short maxs[3]; - unsigned short firstface; - unsigned short numfaces; // counting both sides -} q1_dnode_t; - -typedef struct -{ - int planenum; - short children[2]; // negative numbers are contents -} q1_dclipnode_t; - - -typedef struct q1_texinfo_s -{ - float vecs[2][4]; // [s/t][xyz offset] - int miptex; - int flags; -} q1_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 -} q1_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 -} q1_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 Q1_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]; -} q1_dleaf_t; - -//============================================================================ - -#ifndef QUAKE_GAME - -// the utilities get to be lazy and just use large static arrays - -extern int q1_nummodels; -extern q1_dmodel_t *q1_dmodels;//[MAX_MAP_MODELS]; - -extern int q1_visdatasize; -extern byte *q1_dvisdata;//[MAX_MAP_VISIBILITY]; - -extern int q1_lightdatasize; -extern byte *q1_dlightdata;//[MAX_MAP_LIGHTING]; - -extern int q1_texdatasize; -extern byte *q1_dtexdata;//[MAX_MAP_MIPTEX]; // (dmiptexlump_t) - -extern int q1_entdatasize; -extern char *q1_dentdata;//[MAX_MAP_ENTSTRING]; - -extern int q1_numleafs; -extern q1_dleaf_t *q1_dleafs;//[MAX_MAP_LEAFS]; - -extern int q1_numplanes; -extern q1_dplane_t *q1_dplanes;//[MAX_MAP_PLANES]; - -extern int q1_numvertexes; -extern q1_dvertex_t *q1_dvertexes;//[MAX_MAP_VERTS]; - -extern int q1_numnodes; -extern q1_dnode_t *q1_dnodes;//[MAX_MAP_NODES]; - -extern int q1_numtexinfo; -extern q1_texinfo_t *q1_texinfo;//[MAX_MAP_TEXINFO]; - -extern int q1_numfaces; -extern q1_dface_t *q1_dfaces;//[MAX_MAP_FACES]; - -extern int q1_numclipnodes; -extern q1_dclipnode_t *q1_dclipnodes;//[MAX_MAP_CLIPNODES]; - -extern int q1_numedges; -extern q1_dedge_t *q1_dedges;//[MAX_MAP_EDGES]; - -extern int q1_nummarksurfaces; -extern unsigned short *q1_dmarksurfaces;//[MAX_MAP_MARKSURFACES]; - -extern int q1_numsurfedges; -extern int *q1_dsurfedges;//[MAX_MAP_SURFEDGES]; - - -void Q1_AllocMaxBSP(void); -void Q1_FreeMaxBSP(void); -void Q1_LoadBSPFile(char *filename, int offset, int length); -void Q1_WriteBSPFile(char *filename); -void Q1_PrintBSPFileSizes(void); -void Q1_ParseEntities(void); -void Q1_UnparseEntities(void); - -#endif diff --git a/tools/quake3/bspc/l_bsp_q2.c b/tools/quake3/bspc/l_bsp_q2.c deleted file mode 100644 index 83b81c15..00000000 --- a/tools/quake3/bspc/l_bsp_q2.c +++ /dev/null @@ -1,1134 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "l_cmd.h" -#include "l_math.h" -#include "l_mem.h" -#include "l_log.h" -#include "l_poly.h" -#include "botlib/l_script.h" -#include "q2files.h" -#include "l_bsp_q2.h" -#include "l_bsp_ent.h" - -#define q2_dmodel_t dmodel_t -#define q2_lump_t lump_t -#define q2_dheader_t dheader_t -#define q2_dmodel_t dmodel_t -#define q2_dvertex_t dvertex_t -#define q2_dplane_t dplane_t -#define q2_dnode_t dnode_t -#define q2_texinfo_t texinfo_t -#define q2_dedge_t dedge_t -#define q2_dface_t dface_t -#define q2_dleaf_t dleaf_t -#define q2_dbrushside_t dbrushside_t -#define q2_dbrush_t dbrush_t -#define q2_dvis_t dvis_t -#define q2_dareaportal_t dareaportal_t -#define q2_darea_t darea_t - -#define q2_nummodels nummodels -#define q2_dmodels dmodels -#define q2_numleafs numleafs -#define q2_dleafs dleafs -#define q2_numplanes numplanes -#define q2_dplanes dplanes -#define q2_numvertexes numvertexes -#define q2_dvertexes dvertexes -#define q2_numnodes numnodes -#define q2_dnodes dnodes -#define q2_numtexinfo numtexinfo -#define q2_texinfo texinfo -#define q2_numfaces numfaces -#define q2_dfaces dfaces -#define q2_numedges numedges -#define q2_dedges dedges -#define q2_numleaffaces numleaffaces -#define q2_dleaffaces dleaffaces -#define q2_numleafbrushes numleafbrushes -#define q2_dleafbrushes dleafbrushes -#define q2_dsurfedges dsurfedges -#define q2_numbrushes numbrushes -#define q2_dbrushes dbrushes -#define q2_numbrushsides numbrushsides -#define q2_dbrushsides dbrushsides -#define q2_numareas numareas -#define q2_dareas dareas -#define q2_numareaportals numareaportals -#define q2_dareaportals dareaportals - -void GetLeafNums (void); - -//============================================================================= - -int nummodels; -dmodel_t *dmodels;//[MAX_MAP_MODELS]; - -int visdatasize; -byte *dvisdata;//[MAX_MAP_VISIBILITY]; -dvis_t *dvis;// = (dvis_t *)dvisdata; - -int lightdatasize; -byte *dlightdata;//[MAX_MAP_LIGHTING]; - -int entdatasize; -char *dentdata;//[MAX_MAP_ENTSTRING]; - -int numleafs; -dleaf_t *dleafs;//[MAX_MAP_LEAFS]; - -int numplanes; -dplane_t *dplanes;//[MAX_MAP_PLANES]; - -int numvertexes; -dvertex_t *dvertexes;//[MAX_MAP_VERTS]; - -int numnodes; -dnode_t *dnodes;//[MAX_MAP_NODES]; - -//NOTE: must be static for q2 .map to q2 .bsp -int numtexinfo; -texinfo_t texinfo[MAX_MAP_TEXINFO]; - -int numfaces; -dface_t *dfaces;//[MAX_MAP_FACES]; - -int numedges; -dedge_t *dedges;//[MAX_MAP_EDGES]; - -int numleaffaces; -unsigned short *dleaffaces;//[MAX_MAP_LEAFFACES]; - -int numleafbrushes; -unsigned short *dleafbrushes;//[MAX_MAP_LEAFBRUSHES]; - -int numsurfedges; -int *dsurfedges;//[MAX_MAP_SURFEDGES]; - -int numbrushes; -dbrush_t *dbrushes;//[MAX_MAP_BRUSHES]; - -int numbrushsides; -dbrushside_t *dbrushsides;//[MAX_MAP_BRUSHSIDES]; - -int numareas; -darea_t *dareas;//[MAX_MAP_AREAS]; - -int numareaportals; -dareaportal_t *dareaportals;//[MAX_MAP_AREAPORTALS]; - -#define MAX_MAP_DPOP 256 -byte dpop[MAX_MAP_DPOP]; - -// -char brushsidetextured[MAX_MAP_BRUSHSIDES]; - -//#ifdef ME - -int bspallocated = false; -int allocatedbspmem = 0; - -void Q2_AllocMaxBSP(void) -{ - //models - nummodels = 0; - dmodels = (dmodel_t *) GetClearedMemory(MAX_MAP_MODELS * sizeof(dmodel_t)); - allocatedbspmem += MAX_MAP_MODELS * sizeof(dmodel_t); - //vis data - visdatasize = 0; - dvisdata = (byte *) GetClearedMemory(MAX_MAP_VISIBILITY * sizeof(byte)); - dvis = (dvis_t *) dvisdata; - allocatedbspmem += MAX_MAP_VISIBILITY * sizeof(byte); - //light data - lightdatasize = 0; - dlightdata = (byte *) GetClearedMemory(MAX_MAP_LIGHTING * sizeof(byte)); - allocatedbspmem += MAX_MAP_LIGHTING * sizeof(byte); - //entity data - entdatasize = 0; - dentdata = (char *) GetClearedMemory(MAX_MAP_ENTSTRING * sizeof(char)); - allocatedbspmem += MAX_MAP_ENTSTRING * sizeof(char); - //leafs - numleafs = 0; - dleafs = (dleaf_t *) GetClearedMemory(MAX_MAP_LEAFS * sizeof(dleaf_t)); - allocatedbspmem += MAX_MAP_LEAFS * sizeof(dleaf_t); - //planes - numplanes = 0; - dplanes = (dplane_t *) GetClearedMemory(MAX_MAP_PLANES * sizeof(dplane_t)); - allocatedbspmem += MAX_MAP_PLANES * sizeof(dplane_t); - //vertexes - numvertexes = 0; - dvertexes = (dvertex_t *) GetClearedMemory(MAX_MAP_VERTS * sizeof(dvertex_t)); - allocatedbspmem += MAX_MAP_VERTS * sizeof(dvertex_t); - //nodes - numnodes = 0; - dnodes = (dnode_t *) GetClearedMemory(MAX_MAP_NODES * sizeof(dnode_t)); - allocatedbspmem += MAX_MAP_NODES * sizeof(dnode_t); - /* - //texture info - numtexinfo = 0; - texinfo = (texinfo_t *) GetClearedMemory(MAX_MAP_TEXINFO * sizeof(texinfo_t)); - allocatedbspmem += MAX_MAP_TEXINFO * sizeof(texinfo_t); - //*/ - //faces - numfaces = 0; - dfaces = (dface_t *) GetClearedMemory(MAX_MAP_FACES * sizeof(dface_t)); - allocatedbspmem += MAX_MAP_FACES * sizeof(dface_t); - //edges - numedges = 0; - dedges = (dedge_t *) GetClearedMemory(MAX_MAP_EDGES * sizeof(dedge_t)); - allocatedbspmem += MAX_MAP_EDGES * sizeof(dedge_t); - //leaf faces - numleaffaces = 0; - dleaffaces = (unsigned short *) GetClearedMemory(MAX_MAP_LEAFFACES * sizeof(unsigned short)); - allocatedbspmem += MAX_MAP_LEAFFACES * sizeof(unsigned short); - //leaf brushes - numleafbrushes = 0; - dleafbrushes = (unsigned short *) GetClearedMemory(MAX_MAP_LEAFBRUSHES * sizeof(unsigned short)); - allocatedbspmem += MAX_MAP_LEAFBRUSHES * sizeof(unsigned short); - //surface edges - numsurfedges = 0; - dsurfedges = (int *) GetClearedMemory(MAX_MAP_SURFEDGES * sizeof(int)); - allocatedbspmem += MAX_MAP_SURFEDGES * sizeof(int); - //brushes - numbrushes = 0; - dbrushes = (dbrush_t *) GetClearedMemory(MAX_MAP_BRUSHES * sizeof(dbrush_t)); - allocatedbspmem += MAX_MAP_BRUSHES * sizeof(dbrush_t); - //brushsides - numbrushsides = 0; - dbrushsides = (dbrushside_t *) GetClearedMemory(MAX_MAP_BRUSHSIDES * sizeof(dbrushside_t)); - allocatedbspmem += MAX_MAP_BRUSHSIDES * sizeof(dbrushside_t); - //areas - numareas = 0; - dareas = (darea_t *) GetClearedMemory(MAX_MAP_AREAS * sizeof(darea_t)); - allocatedbspmem += MAX_MAP_AREAS * sizeof(darea_t); - //area portals - numareaportals = 0; - dareaportals = (dareaportal_t *) GetClearedMemory(MAX_MAP_AREAPORTALS * sizeof(dareaportal_t)); - allocatedbspmem += MAX_MAP_AREAPORTALS * sizeof(dareaportal_t); - //print allocated memory - Log_Print("allocated "); - PrintMemorySize(allocatedbspmem); - Log_Print(" of BSP memory\n"); -} //end of the function Q2_AllocMaxBSP - -void Q2_FreeMaxBSP(void) -{ - //models - nummodels = 0; - FreeMemory(dmodels); - dmodels = NULL; - //vis data - visdatasize = 0; - FreeMemory(dvisdata); - dvisdata = NULL; - dvis = NULL; - //light data - lightdatasize = 0; - FreeMemory(dlightdata); - dlightdata = NULL; - //entity data - entdatasize = 0; - FreeMemory(dentdata); - dentdata = NULL; - //leafs - numleafs = 0; - FreeMemory(dleafs); - dleafs = NULL; - //planes - numplanes = 0; - FreeMemory(dplanes); - dplanes = NULL; - //vertexes - numvertexes = 0; - FreeMemory(dvertexes); - dvertexes = NULL; - //nodes - numnodes = 0; - FreeMemory(dnodes); - dnodes = NULL; - /* - //texture info - numtexinfo = 0; - FreeMemory(texinfo); - texinfo = NULL; - //*/ - //faces - numfaces = 0; - FreeMemory(dfaces); - dfaces = NULL; - //edges - numedges = 0; - FreeMemory(dedges); - dedges = NULL; - //leaf faces - numleaffaces = 0; - FreeMemory(dleaffaces); - dleaffaces = NULL; - //leaf brushes - numleafbrushes = 0; - FreeMemory(dleafbrushes); - dleafbrushes = NULL; - //surface edges - numsurfedges = 0; - FreeMemory(dsurfedges); - dsurfedges = NULL; - //brushes - numbrushes = 0; - FreeMemory(dbrushes); - dbrushes = NULL; - //brushsides - numbrushsides = 0; - FreeMemory(dbrushsides); - dbrushsides = NULL; - //areas - numareas = 0; - FreeMemory(dareas); - dareas = NULL; - //area portals - numareaportals = 0; - FreeMemory(dareaportals); - dareaportals = NULL; - // - Log_Print("freed "); - PrintMemorySize(allocatedbspmem); - Log_Print(" of BSP memory\n"); - allocatedbspmem = 0; -} //end of the function Q2_FreeMaxBSP - -#define WCONVEX_EPSILON 0.5 - -int InsideWinding(winding_t *w, vec3_t point, int planenum) -{ - int i; - float dist; - vec_t *v1, *v2; - vec3_t normal, edgevec; - dplane_t *plane; - - for (i = 1; i <= w->numpoints; i++) - { - v1 = w->p[i % w->numpoints]; - v2 = w->p[(i + 1) % w->numpoints]; - - VectorSubtract(v2, v1, edgevec); - plane = &dplanes[planenum]; - CrossProduct(plane->normal, edgevec, normal); - VectorNormalize(normal); - dist = DotProduct(normal, v1); - // - if (DotProduct(normal, point) - dist > WCONVEX_EPSILON) return false; - } //end for - return true; -} //end of the function InsideWinding - -int InsideFace(dface_t *face, vec3_t point) -{ - int i, edgenum, side; - float dist; - vec_t *v1, *v2; - vec3_t normal, edgevec; - dplane_t *plane; - - for (i = 0; i < face->numedges; i++) - { - //get the first and second vertex of the edge - edgenum = dsurfedges[face->firstedge + i]; - side = edgenum < 0; - v1 = dvertexes[dedges[abs(edgenum)].v[side]].point; - v2 = dvertexes[dedges[abs(edgenum)].v[!side]].point; - //create a plane through the edge vector, orthogonal to the face plane - //and with the normal vector pointing out of the face - VectorSubtract(v1, v2, edgevec); - plane = &dplanes[face->planenum]; - CrossProduct(plane->normal, edgevec, normal); - VectorNormalize(normal); - dist = DotProduct(normal, v1); - // - if (DotProduct(normal, point) - dist > WCONVEX_EPSILON) return false; - } //end for - return true; -} //end of the function InsideFace -//=========================================================================== -// returns the amount the face and the winding overlap -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float Q2_FaceOnWinding(q2_dface_t *face, winding_t *winding) -{ - int i, edgenum, side; - float dist, area; - q2_dplane_t plane; - vec_t *v1, *v2; - vec3_t normal, edgevec; - winding_t *w; - - // - w = CopyWinding(winding); - memcpy(&plane, &q2_dplanes[face->planenum], sizeof(q2_dplane_t)); - //check on which side of the plane the face is - if (face->side) - { - VectorNegate(plane.normal, plane.normal); - plane.dist = -plane.dist; - } //end if - for (i = 0; i < face->numedges && w; i++) - { - //get the first and second vertex of the edge - edgenum = q2_dsurfedges[face->firstedge + i]; - side = edgenum > 0; - //if the face plane is flipped - v1 = q2_dvertexes[q2_dedges[abs(edgenum)].v[side]].point; - v2 = q2_dvertexes[q2_dedges[abs(edgenum)].v[!side]].point; - //create a plane through the edge vector, orthogonal to the face plane - //and with the normal vector pointing inward - VectorSubtract(v1, v2, edgevec); - CrossProduct(edgevec, plane.normal, normal); - VectorNormalize(normal); - dist = DotProduct(normal, v1); - // - ChopWindingInPlace(&w, normal, dist, -0.1); //CLIP_EPSILON - } //end for - if (w) - { - area = WindingArea(w); - FreeWinding(w); - return area; - } //end if - return 0; -} //end of the function Q2_FaceOnWinding -//=========================================================================== -// creates a winding for the given brush side on the given brush -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -winding_t *Q2_BrushSideWinding(dbrush_t *brush, dbrushside_t *baseside) -{ - int i; - dplane_t *baseplane, *plane; - winding_t *w; - dbrushside_t *side; - - //create a winding for the brush side with the given planenumber - baseplane = &dplanes[baseside->planenum]; - w = BaseWindingForPlane(baseplane->normal, baseplane->dist); - for (i = 0; i < brush->numsides && w; i++) - { - side = &dbrushsides[brush->firstside + i]; - //don't chop with the base plane - if (side->planenum == baseside->planenum) continue; - //also don't use planes that are almost equal - plane = &dplanes[side->planenum]; - if (DotProduct(baseplane->normal, plane->normal) > 0.999 - && fabs(baseplane->dist - plane->dist) < 0.01) continue; - // - plane = &dplanes[side->planenum^1]; - ChopWindingInPlace(&w, plane->normal, plane->dist, -0.1); //CLIP_EPSILON); - } //end for - return w; -} //end of the function Q2_BrushSideWinding -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Q2_HintSkipBrush(dbrush_t *brush) -{ - int j; - dbrushside_t *brushside; - - for (j = 0; j < brush->numsides; j++) - { - brushside = &dbrushsides[brush->firstside + j]; - if (brushside->texinfo > 0) - { - if (texinfo[brushside->texinfo].flags & (SURF_SKIP|SURF_HINT)) - { - return true; - } //end if - } //end if - } //end for - return false; -} //end of the function Q2_HintSkipBrush -//=========================================================================== -// fix screwed brush texture references -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WindingIsTiny(winding_t *w); - -void Q2_FixTextureReferences(void) -{ - int i, j, k, we; - dbrushside_t *brushside; - dbrush_t *brush; - dface_t *face; - winding_t *w; - - memset(brushsidetextured, false, MAX_MAP_BRUSHSIDES); - //go over all the brushes - for (i = 0; i < numbrushes; i++) - { - brush = &dbrushes[i]; - //hint brushes are not textured - if (Q2_HintSkipBrush(brush)) continue; - //go over all the sides of the brush - for (j = 0; j < brush->numsides; j++) - { - brushside = &dbrushsides[brush->firstside + j]; - // - w = Q2_BrushSideWinding(brush, brushside); - if (!w) - { - brushsidetextured[brush->firstside + j] = true; - continue; - } //end if - else - { - //RemoveEqualPoints(w, 0.2); - if (WindingIsTiny(w)) - { - FreeWinding(w); - brushsidetextured[brush->firstside + j] = true; - continue; - } //end if - else - { - we = WindingError(w); - if (we == WE_NOTENOUGHPOINTS - || we == WE_SMALLAREA - || we == WE_POINTBOGUSRANGE -// || we == WE_NONCONVEX - ) - { - FreeWinding(w); - brushsidetextured[brush->firstside + j] = true; - continue; - } //end if - } //end else - } //end else - if (WindingArea(w) < 20) - { - brushsidetextured[brush->firstside + j] = true; - } //end if - //find a face for texturing this brush - for (k = 0; k < numfaces; k++) - { - face = &dfaces[k]; - //if the face is in the same plane as the brush side - if ((face->planenum&~1) != (brushside->planenum&~1)) continue; - //if the face is partly or totally on the brush side - if (Q2_FaceOnWinding(face, w)) - { - brushside->texinfo = face->texinfo; - brushsidetextured[brush->firstside + j] = true; - break; - } //end if - } //end for - FreeWinding(w); - } //end for - } //end for -} //end of the function Q2_FixTextureReferences*/ - -//#endif //ME - - -/* -=============== -CompressVis - -=============== -*/ -int Q2_CompressVis (byte *vis, byte *dest) -{ - int j; - int rep; - int visrow; - byte *dest_p; - - dest_p = dest; -// visrow = (r_numvisleafs + 7)>>3; - visrow = (dvis->numclusters + 7)>>3; - - for (j=0 ; j>3; - row = (dvis->numclusters+7)>>3; - out = decompressed; - - do - { - if (*in) - { - *out++ = *in++; - continue; - } - - c = in[1]; - if (!c) - Error ("DecompressVis: 0 repeat"); - in += 2; - while (c) - { - *out++ = 0; - c--; - } - } while (out - decompressed < row); -} - -//============================================================================= - -/* -============= -SwapBSPFile - -Byte swaps all data in a bsp file. -============= -*/ -void Q2_SwapBSPFile (qboolean todisk) -{ - int i, j; - dmodel_t *d; - - -// models - for (i=0 ; ifirstface = LittleLong (d->firstface); - d->numfaces = LittleLong (d->numfaces); - d->headnode = LittleLong (d->headnode); - - for (j=0 ; j<3 ; j++) - { - d->mins[j] = LittleFloat(d->mins[j]); - d->maxs[j] = LittleFloat(d->maxs[j]); - d->origin[j] = LittleFloat(d->origin[j]); - } - } - -// -// vertexes -// - for (i=0 ; inumclusters; - else - j = LittleLong(dvis->numclusters); - dvis->numclusters = LittleLong (dvis->numclusters); - for (i=0 ; ibitofs[i][0] = LittleLong (dvis->bitofs[i][0]); - dvis->bitofs[i][1] = LittleLong (dvis->bitofs[i][1]); - } -} //end of the function Q2_SwapBSPFile - - -dheader_t *header; - -int Q2_CopyLump (int lump, void *dest, int size, int maxsize) -{ - int length, ofs; - - length = header->lumps[lump].filelen; - ofs = header->lumps[lump].fileofs; - - if (length % size) - Error ("LoadBSPFile: odd lump size"); - - if ((length/size) > maxsize) - Error ("Q2_LoadBSPFile: exceeded max size for lump %d size %d > maxsize %d\n", lump, (length/size), maxsize); - - memcpy (dest, (byte *)header + ofs, length); - - return length / size; -} //end of the function Q2_CopyLump - -/* -============= -LoadBSPFile -============= -*/ -void Q2_LoadBSPFile(char *filename, int offset, int length) -{ - int i; - -// -// load the file header -// - LoadFile (filename, (void **)&header, offset, length); - -// swap the header - for (i=0 ; i< sizeof(dheader_t)/4 ; i++) - ((int *)header)[i] = LittleLong ( ((int *)header)[i]); - - if (header->ident != IDBSPHEADER) - Error ("%s is not a IBSP file", filename); - if (header->version != BSPVERSION) - Error ("%s is version %i, not %i", filename, header->version, BSPVERSION); - - nummodels = Q2_CopyLump (LUMP_MODELS, dmodels, sizeof(dmodel_t), MAX_MAP_MODELS); - numvertexes = Q2_CopyLump (LUMP_VERTEXES, dvertexes, sizeof(dvertex_t), MAX_MAP_VERTS); - numplanes = Q2_CopyLump (LUMP_PLANES, dplanes, sizeof(dplane_t), MAX_MAP_PLANES); - numleafs = Q2_CopyLump (LUMP_LEAFS, dleafs, sizeof(dleaf_t), MAX_MAP_LEAFS); - numnodes = Q2_CopyLump (LUMP_NODES, dnodes, sizeof(dnode_t), MAX_MAP_NODES); - numtexinfo = Q2_CopyLump (LUMP_TEXINFO, texinfo, sizeof(texinfo_t), MAX_MAP_TEXINFO); - numfaces = Q2_CopyLump (LUMP_FACES, dfaces, sizeof(dface_t), MAX_MAP_FACES); - numleaffaces = Q2_CopyLump (LUMP_LEAFFACES, dleaffaces, sizeof(dleaffaces[0]), MAX_MAP_LEAFFACES); - numleafbrushes = Q2_CopyLump (LUMP_LEAFBRUSHES, dleafbrushes, sizeof(dleafbrushes[0]), MAX_MAP_LEAFBRUSHES); - numsurfedges = Q2_CopyLump (LUMP_SURFEDGES, dsurfedges, sizeof(dsurfedges[0]), MAX_MAP_SURFEDGES); - numedges = Q2_CopyLump (LUMP_EDGES, dedges, sizeof(dedge_t), MAX_MAP_EDGES); - numbrushes = Q2_CopyLump (LUMP_BRUSHES, dbrushes, sizeof(dbrush_t), MAX_MAP_BRUSHES); - numbrushsides = Q2_CopyLump (LUMP_BRUSHSIDES, dbrushsides, sizeof(dbrushside_t), MAX_MAP_BRUSHSIDES); - numareas = Q2_CopyLump (LUMP_AREAS, dareas, sizeof(darea_t), MAX_MAP_AREAS); - numareaportals = Q2_CopyLump (LUMP_AREAPORTALS, dareaportals, sizeof(dareaportal_t), MAX_MAP_AREAPORTALS); - - visdatasize = Q2_CopyLump (LUMP_VISIBILITY, dvisdata, 1, MAX_MAP_VISIBILITY); - lightdatasize = Q2_CopyLump (LUMP_LIGHTING, dlightdata, 1, MAX_MAP_LIGHTING); - entdatasize = Q2_CopyLump (LUMP_ENTITIES, dentdata, 1, MAX_MAP_ENTSTRING); - - Q2_CopyLump (LUMP_POP, dpop, 1, MAX_MAP_DPOP); - - FreeMemory(header); // everything has been copied out - -// -// swap everything -// - Q2_SwapBSPFile (false); - - Q2_FixTextureReferences(); -} //end of the function Q2_LoadBSPFile - - -/* -============= -LoadBSPFileTexinfo - -Only loads the texinfo lump, so qdata can scan for textures -============= -*/ -void Q2_LoadBSPFileTexinfo (char *filename) -{ - int i; - FILE *f; - int length, ofs; - - header = GetMemory(sizeof(dheader_t)); - - f = fopen (filename, "rb"); - fread (header, sizeof(dheader_t), 1, f); - -// swap the header - for (i=0 ; i< sizeof(dheader_t)/4 ; i++) - ((int *)header)[i] = LittleLong ( ((int *)header)[i]); - - if (header->ident != IDBSPHEADER) - Error ("%s is not a IBSP file", filename); - if (header->version != BSPVERSION) - Error ("%s is version %i, not %i", filename, header->version, BSPVERSION); - - - length = header->lumps[LUMP_TEXINFO].filelen; - ofs = header->lumps[LUMP_TEXINFO].fileofs; - - fseek (f, ofs, SEEK_SET); - fread (texinfo, length, 1, f); - fclose (f); - - numtexinfo = length / sizeof(texinfo_t); - - FreeMemory(header); // everything has been copied out - - Q2_SwapBSPFile (false); -} //end of the function Q2_LoadBSPFileTexinfo - - -//============================================================================ - -FILE *wadfile; -dheader_t outheader; - -void Q2_AddLump (int lumpnum, void *data, int len) -{ - lump_t *lump; - - lump = &header->lumps[lumpnum]; - - lump->fileofs = LittleLong( ftell(wadfile) ); - lump->filelen = LittleLong(len); - SafeWrite (wadfile, data, (len+3)&~3); -} //end of the function Q2_AddLump - -/* -============= -WriteBSPFile - -Swaps the bsp file in place, so it should not be referenced again -============= -*/ -void Q2_WriteBSPFile (char *filename) -{ - header = &outheader; - memset (header, 0, sizeof(dheader_t)); - - Q2_SwapBSPFile (true); - - header->ident = LittleLong (IDBSPHEADER); - header->version = LittleLong (BSPVERSION); - - wadfile = SafeOpenWrite (filename); - SafeWrite (wadfile, header, sizeof(dheader_t)); // overwritten later - - Q2_AddLump (LUMP_PLANES, dplanes, numplanes*sizeof(dplane_t)); - Q2_AddLump (LUMP_LEAFS, dleafs, numleafs*sizeof(dleaf_t)); - Q2_AddLump (LUMP_VERTEXES, dvertexes, numvertexes*sizeof(dvertex_t)); - Q2_AddLump (LUMP_NODES, dnodes, numnodes*sizeof(dnode_t)); - Q2_AddLump (LUMP_TEXINFO, texinfo, numtexinfo*sizeof(texinfo_t)); - Q2_AddLump (LUMP_FACES, dfaces, numfaces*sizeof(dface_t)); - Q2_AddLump (LUMP_BRUSHES, dbrushes, numbrushes*sizeof(dbrush_t)); - Q2_AddLump (LUMP_BRUSHSIDES, dbrushsides, numbrushsides*sizeof(dbrushside_t)); - Q2_AddLump (LUMP_LEAFFACES, dleaffaces, numleaffaces*sizeof(dleaffaces[0])); - Q2_AddLump (LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes*sizeof(dleafbrushes[0])); - Q2_AddLump (LUMP_SURFEDGES, dsurfedges, numsurfedges*sizeof(dsurfedges[0])); - Q2_AddLump (LUMP_EDGES, dedges, numedges*sizeof(dedge_t)); - Q2_AddLump (LUMP_MODELS, dmodels, nummodels*sizeof(dmodel_t)); - Q2_AddLump (LUMP_AREAS, dareas, numareas*sizeof(darea_t)); - Q2_AddLump (LUMP_AREAPORTALS, dareaportals, numareaportals*sizeof(dareaportal_t)); - - Q2_AddLump (LUMP_LIGHTING, dlightdata, lightdatasize); - Q2_AddLump (LUMP_VISIBILITY, dvisdata, visdatasize); - Q2_AddLump (LUMP_ENTITIES, dentdata, entdatasize); - Q2_AddLump (LUMP_POP, dpop, sizeof(dpop)); - - fseek (wadfile, 0, SEEK_SET); - SafeWrite (wadfile, header, sizeof(dheader_t)); - fclose (wadfile); -} //end of the function Q2_WriteBSPFile - -//============================================================================ - -/* -============= -PrintBSPFileSizes - -Dumps info about current file -============= -*/ -void Q2_PrintBSPFileSizes (void) -{ - if (!num_entities) - Q2_ParseEntities(); - - printf ("%6i models %7i\n" - ,nummodels, (int)(nummodels*sizeof(dmodel_t))); - printf ("%6i brushes %7i\n" - ,numbrushes, (int)(numbrushes*sizeof(dbrush_t))); - printf ("%6i brushsides %7i\n" - ,numbrushsides, (int)(numbrushsides*sizeof(dbrushside_t))); - printf ("%6i planes %7i\n" - ,numplanes, (int)(numplanes*sizeof(dplane_t))); - printf ("%6i texinfo %7i\n" - ,numtexinfo, (int)(numtexinfo*sizeof(texinfo_t))); - printf ("%6i entdata %7i\n", num_entities, entdatasize); - - printf ("\n"); - - printf ("%6i vertexes %7i\n" - ,numvertexes, (int)(numvertexes*sizeof(dvertex_t))); - printf ("%6i nodes %7i\n" - ,numnodes, (int)(numnodes*sizeof(dnode_t))); - printf ("%6i faces %7i\n" - ,numfaces, (int)(numfaces*sizeof(dface_t))); - printf ("%6i leafs %7i\n" - ,numleafs, (int)(numleafs*sizeof(dleaf_t))); - printf ("%6i leaffaces %7i\n" - ,numleaffaces, (int)(numleaffaces*sizeof(dleaffaces[0]))); - printf ("%6i leafbrushes %7i\n" - ,numleafbrushes, (int)(numleafbrushes*sizeof(dleafbrushes[0]))); - printf ("%6i surfedges %7i\n" - ,numsurfedges, (int)(numsurfedges*sizeof(dsurfedges[0]))); - printf ("%6i edges %7i\n" - ,numedges, (int)(numedges*sizeof(dedge_t))); -//NEW - printf ("%6i areas %7i\n" - ,numareas, (int)(numareas*sizeof(darea_t))); - printf ("%6i areaportals %7i\n" - ,numareaportals, (int)(numareaportals*sizeof(dareaportal_t))); -//ENDNEW - printf (" lightdata %7i\n", lightdatasize); - printf (" visdata %7i\n", visdatasize); -} //end of the function Q2_PrintBSPFileSizes - -/* -================ -ParseEntities - -Parses the dentdata string into entities -================ -*/ -void Q2_ParseEntities (void) -{ - script_t *script; - - num_entities = 0; - script = LoadScriptMemory(dentdata, entdatasize, "*Quake2 bsp file"); - SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES | - SCFL_NOSTRINGESCAPECHARS); - - while(ParseEntity(script)) - { - } //end while - - FreeScript(script); -} //end of the function Q2_ParseEntities - - -/* -================ -UnparseEntities - -Generates the dentdata string from all the entities -================ -*/ -void Q2_UnparseEntities (void) -{ - char *buf, *end; - epair_t *ep; - char line[2048]; - int i; - char key[1024], value[1024]; - - buf = dentdata; - end = buf; - *end = 0; - - for (i=0 ; inext) - { - strcpy (key, ep->key); - StripTrailing (key); - strcpy (value, ep->value); - StripTrailing (value); - - sprintf (line, "\"%s\" \"%s\"\n", key, value); - strcat (end, line); - end += strlen(line); - } - strcat (end,"}\n"); - end += 2; - - if (end > buf + MAX_MAP_ENTSTRING) - Error ("Entity text too long"); - } - entdatasize = end - buf + 1; -} //end of the function Q2_UnparseEntities - diff --git a/tools/quake3/bspc/l_bsp_q2.h b/tools/quake3/bspc/l_bsp_q2.h deleted file mode 100644 index f0720df9..00000000 --- a/tools/quake3/bspc/l_bsp_q2.h +++ /dev/null @@ -1,98 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#ifndef ME -#define ME -#endif //ME - -extern int nummodels; -extern dmodel_t *dmodels;//[MAX_MAP_MODELS]; - -extern int visdatasize; -extern byte *dvisdata;//[MAX_MAP_VISIBILITY]; -extern dvis_t *dvis; - -extern int lightdatasize; -extern byte *dlightdata;//[MAX_MAP_LIGHTING]; - -extern int entdatasize; -extern char *dentdata;//[MAX_MAP_ENTSTRING]; - -extern int numleafs; -extern dleaf_t *dleafs;//[MAX_MAP_LEAFS]; - -extern int numplanes; -extern dplane_t *dplanes;//[MAX_MAP_PLANES]; - -extern int numvertexes; -extern dvertex_t *dvertexes;//[MAX_MAP_VERTS]; - -extern int numnodes; -extern dnode_t *dnodes;//[MAX_MAP_NODES]; - -extern int numtexinfo; -extern texinfo_t texinfo[MAX_MAP_TEXINFO]; - -extern int numfaces; -extern dface_t *dfaces;//[MAX_MAP_FACES]; - -extern int numedges; -extern dedge_t *dedges;//[MAX_MAP_EDGES]; - -extern int numleaffaces; -extern unsigned short *dleaffaces;//[MAX_MAP_LEAFFACES]; - -extern int numleafbrushes; -extern unsigned short *dleafbrushes;//[MAX_MAP_LEAFBRUSHES]; - -extern int numsurfedges; -extern int *dsurfedges;//[MAX_MAP_SURFEDGES]; - -extern int numareas; -extern darea_t *dareas;//[MAX_MAP_AREAS]; - -extern int numareaportals; -extern dareaportal_t *dareaportals;//[MAX_MAP_AREAPORTALS]; - -extern int numbrushes; -extern dbrush_t *dbrushes;//[MAX_MAP_BRUSHES]; - -extern int numbrushsides; -extern dbrushside_t *dbrushsides;//[MAX_MAP_BRUSHSIDES]; - -extern byte dpop[256]; - -extern char brushsidetextured[MAX_MAP_BRUSHSIDES]; - -void Q2_AllocMaxBSP(void); -void Q2_FreeMaxBSP(void); - -void Q2_DecompressVis(byte *in, byte *decompressed); -int Q2_CompressVis(byte *vis, byte *dest); - -void Q2_LoadBSPFile(char *filename, int offset, int length); -void Q2_LoadBSPFileTexinfo(char *filename); // just for qdata -void Q2_WriteBSPFile(char *filename); -void Q2_PrintBSPFileSizes(void); -void Q2_ParseEntities(void); -void Q2_UnparseEntities(void); - diff --git a/tools/quake3/bspc/l_bsp_q3.c b/tools/quake3/bspc/l_bsp_q3.c deleted file mode 100644 index 7588b860..00000000 --- a/tools/quake3/bspc/l_bsp_q3.c +++ /dev/null @@ -1,824 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "l_cmd.h" -#include "l_math.h" -#include "l_mem.h" -#include "l_log.h" -#include "l_poly.h" -#include "botlib/l_script.h" -#include "l_qfiles.h" -#include "l_bsp_q3.h" -#include "l_bsp_ent.h" - -void Q3_ParseEntities (void); -void Q3_PrintBSPFileSizes(void); - -void GetLeafNums (void); - -//============================================================================= - -#define WCONVEX_EPSILON 0.5 - - -int q3_nummodels; -q3_dmodel_t *q3_dmodels;//[MAX_MAP_MODELS]; - -int q3_numShaders; -q3_dshader_t *q3_dshaders;//[Q3_MAX_MAP_SHADERS]; - -int q3_entdatasize; -char *q3_dentdata;//[Q3_MAX_MAP_ENTSTRING]; - -int q3_numleafs; -q3_dleaf_t *q3_dleafs;//[Q3_MAX_MAP_LEAFS]; - -int q3_numplanes; -q3_dplane_t *q3_dplanes;//[Q3_MAX_MAP_PLANES]; - -int q3_numnodes; -q3_dnode_t *q3_dnodes;//[Q3_MAX_MAP_NODES]; - -int q3_numleafsurfaces; -int *q3_dleafsurfaces;//[Q3_MAX_MAP_LEAFFACES]; - -int q3_numleafbrushes; -int *q3_dleafbrushes;//[Q3_MAX_MAP_LEAFBRUSHES]; - -int q3_numbrushes; -q3_dbrush_t *q3_dbrushes;//[Q3_MAX_MAP_BRUSHES]; - -int q3_numbrushsides; -q3_dbrushside_t *q3_dbrushsides;//[Q3_MAX_MAP_BRUSHSIDES]; - -int q3_numLightBytes; -byte *q3_lightBytes;//[Q3_MAX_MAP_LIGHTING]; - -int q3_numGridPoints; -byte *q3_gridData;//[Q3_MAX_MAP_LIGHTGRID]; - -int q3_numVisBytes; -byte *q3_visBytes;//[Q3_MAX_MAP_VISIBILITY]; - -int q3_numDrawVerts; -q3_drawVert_t *q3_drawVerts;//[Q3_MAX_MAP_DRAW_VERTS]; - -int q3_numDrawIndexes; -int *q3_drawIndexes;//[Q3_MAX_MAP_DRAW_INDEXES]; - -int q3_numDrawSurfaces; -q3_dsurface_t *q3_drawSurfaces;//[Q3_MAX_MAP_DRAW_SURFS]; - -int q3_numFogs; -q3_dfog_t *q3_dfogs;//[Q3_MAX_MAP_FOGS]; - -char q3_dbrushsidetextured[Q3_MAX_MAP_BRUSHSIDES]; - -extern qboolean forcesidesvisible; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q3_FreeMaxBSP(void) -{ - if (q3_dmodels) FreeMemory(q3_dmodels); - q3_dmodels = NULL; - q3_nummodels = 0; - if (q3_dshaders) FreeMemory(q3_dshaders); - q3_dshaders = NULL; - q3_numShaders = 0; - if (q3_dentdata) FreeMemory(q3_dentdata); - q3_dentdata = NULL; - q3_entdatasize = 0; - if (q3_dleafs) FreeMemory(q3_dleafs); - q3_dleafs = NULL; - q3_numleafs = 0; - if (q3_dplanes) FreeMemory(q3_dplanes); - q3_dplanes = NULL; - q3_numplanes = 0; - if (q3_dnodes) FreeMemory(q3_dnodes); - q3_dnodes = NULL; - q3_numnodes = 0; - if (q3_dleafsurfaces) FreeMemory(q3_dleafsurfaces); - q3_dleafsurfaces = NULL; - q3_numleafsurfaces = 0; - if (q3_dleafbrushes) FreeMemory(q3_dleafbrushes); - q3_dleafbrushes = NULL; - q3_numleafbrushes = 0; - if (q3_dbrushes) FreeMemory(q3_dbrushes); - q3_dbrushes = NULL; - q3_numbrushes = 0; - if (q3_dbrushsides) FreeMemory(q3_dbrushsides); - q3_dbrushsides = NULL; - q3_numbrushsides = 0; - if (q3_lightBytes) FreeMemory(q3_lightBytes); - q3_lightBytes = NULL; - q3_numLightBytes = 0; - if (q3_gridData) FreeMemory(q3_gridData); - q3_gridData = NULL; - q3_numGridPoints = 0; - if (q3_visBytes) FreeMemory(q3_visBytes); - q3_visBytes = NULL; - q3_numVisBytes = 0; - if (q3_drawVerts) FreeMemory(q3_drawVerts); - q3_drawVerts = NULL; - q3_numDrawVerts = 0; - if (q3_drawIndexes) FreeMemory(q3_drawIndexes); - q3_drawIndexes = NULL; - q3_numDrawIndexes = 0; - if (q3_drawSurfaces) FreeMemory(q3_drawSurfaces); - q3_drawSurfaces = NULL; - q3_numDrawSurfaces = 0; - if (q3_dfogs) FreeMemory(q3_dfogs); - q3_dfogs = NULL; - q3_numFogs = 0; -} //end of the function Q3_FreeMaxBSP - - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q3_PlaneFromPoints(vec3_t p0, vec3_t p1, vec3_t p2, vec3_t normal, float *dist) -{ - vec3_t t1, t2; - - VectorSubtract(p0, p1, t1); - VectorSubtract(p2, p1, t2); - CrossProduct(t1, t2, normal); - VectorNormalize(normal); - - *dist = DotProduct(p0, normal); -} //end of the function PlaneFromPoints -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q3_SurfacePlane(q3_dsurface_t *surface, vec3_t normal, float *dist) -{ - int i; - float *p0, *p1, *p2; - vec3_t t1, t2; - - p0 = q3_drawVerts[surface->firstVert].xyz; - for (i = 1; i < surface->numVerts-1; i++) - { - p1 = q3_drawVerts[surface->firstVert + ((i) % surface->numVerts)].xyz; - p2 = q3_drawVerts[surface->firstVert + ((i+1) % surface->numVerts)].xyz; - VectorSubtract(p0, p1, t1); - VectorSubtract(p2, p1, t2); - CrossProduct(t1, t2, normal); - VectorNormalize(normal); - if (VectorLength(normal)) break; - } //end for*/ -/* - float dot; - for (i = 0; i < surface->numVerts; i++) - { - p0 = q3_drawVerts[surface->firstVert + ((i) % surface->numVerts)].xyz; - p1 = q3_drawVerts[surface->firstVert + ((i+1) % surface->numVerts)].xyz; - p2 = q3_drawVerts[surface->firstVert + ((i+2) % surface->numVerts)].xyz; - VectorSubtract(p0, p1, t1); - VectorSubtract(p2, p1, t2); - VectorNormalize(t1); - VectorNormalize(t2); - dot = DotProduct(t1, t2); - if (dot > -0.9 && dot < 0.9 && - VectorLength(t1) > 0.1 && VectorLength(t2) > 0.1) break; - } //end for - CrossProduct(t1, t2, normal); - VectorNormalize(normal); -*/ - if (VectorLength(normal) < 0.9) - { - printf("surface %td bogus normal vector %f %f %f\n", surface - q3_drawSurfaces, normal[0], normal[1], normal[2]); - printf("t1 = %f %f %f, t2 = %f %f %f\n", t1[0], t1[1], t1[2], t2[0], t2[1], t2[2]); - for (i = 0; i < surface->numVerts; i++) - { - p1 = q3_drawVerts[surface->firstVert + ((i) % surface->numVerts)].xyz; - Log_Print("p%d = %f %f %f\n", i, p1[0], p1[1], p1[2]); - } //end for - } //end if - *dist = DotProduct(p0, normal); -} //end of the function Q3_SurfacePlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -q3_dplane_t *q3_surfaceplanes; - -void Q3_CreatePlanarSurfacePlanes(void) -{ - int i; - q3_dsurface_t *surface; - - Log_Print("creating planar surface planes...\n"); - q3_surfaceplanes = (q3_dplane_t *) GetClearedMemory(q3_numDrawSurfaces * sizeof(q3_dplane_t)); - - for (i = 0; i < q3_numDrawSurfaces; i++) - { - surface = &q3_drawSurfaces[i]; - if (surface->surfaceType != MST_PLANAR) continue; - Q3_SurfacePlane(surface, q3_surfaceplanes[i].normal, &q3_surfaceplanes[i].dist); - //Log_Print("normal = %f %f %f, dist = %f\n", q3_surfaceplanes[i].normal[0], - // q3_surfaceplanes[i].normal[1], - // q3_surfaceplanes[i].normal[2], q3_surfaceplanes[i].dist); - } //end for -} //end of the function Q3_CreatePlanarSurfacePlanes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -/* -void Q3_SurfacePlane(q3_dsurface_t *surface, vec3_t normal, float *dist) -{ - //take the plane information from the lightmap vector - //VectorCopy(surface->lightmapVecs[2], normal); - //calculate plane dist with first surface vertex -// *dist = DotProduct(q3_drawVerts[surface->firstVert].xyz, normal); - Q3_PlaneFromPoints(q3_drawVerts[surface->firstVert].xyz, - q3_drawVerts[surface->firstVert+1].xyz, - q3_drawVerts[surface->firstVert+2].xyz, normal, dist); -} //end of the function Q3_SurfacePlane*/ -//=========================================================================== -// returns the amount the face and the winding overlap -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float Q3_FaceOnWinding(q3_dsurface_t *surface, winding_t *winding) -{ - int i; - float dist, area; - q3_dplane_t plane; - vec_t *v1, *v2; - vec3_t normal, edgevec; - winding_t *w; - - //copy the winding before chopping - w = CopyWinding(winding); - //retrieve the surface plane - Q3_SurfacePlane(surface, plane.normal, &plane.dist); - //chop the winding with the surface edge planes - for (i = 0; i < surface->numVerts && w; i++) - { - v1 = q3_drawVerts[surface->firstVert + ((i) % surface->numVerts)].xyz; - v2 = q3_drawVerts[surface->firstVert + ((i+1) % surface->numVerts)].xyz; - //create a plane through the edge from v1 to v2, orthogonal to the - //surface plane and with the normal vector pointing inward - VectorSubtract(v2, v1, edgevec); - CrossProduct(edgevec, plane.normal, normal); - VectorNormalize(normal); - dist = DotProduct(normal, v1); - // - ChopWindingInPlace(&w, normal, dist, -0.1); //CLIP_EPSILON - } //end for - if (w) - { - area = WindingArea(w); - FreeWinding(w); - return area; - } //end if - return 0; -} //end of the function Q3_FaceOnWinding -//=========================================================================== -// creates a winding for the given brush side on the given brush -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -winding_t *Q3_BrushSideWinding(q3_dbrush_t *brush, q3_dbrushside_t *baseside) -{ - int i; - q3_dplane_t *baseplane, *plane; - winding_t *w; - q3_dbrushside_t *side; - - //create a winding for the brush side with the given planenumber - baseplane = &q3_dplanes[baseside->planeNum]; - w = BaseWindingForPlane(baseplane->normal, baseplane->dist); - for (i = 0; i < brush->numSides && w; i++) - { - side = &q3_dbrushsides[brush->firstSide + i]; - //don't chop with the base plane - if (side->planeNum == baseside->planeNum) continue; - //also don't use planes that are almost equal - plane = &q3_dplanes[side->planeNum]; - if (DotProduct(baseplane->normal, plane->normal) > 0.999 - && fabs(baseplane->dist - plane->dist) < 0.01) continue; - // - plane = &q3_dplanes[side->planeNum^1]; - ChopWindingInPlace(&w, plane->normal, plane->dist, -0.1); //CLIP_EPSILON); - } //end for - return w; -} //end of the function Q3_BrushSideWinding -//=========================================================================== -// fix screwed brush texture references -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WindingIsTiny(winding_t *w); - -void Q3_FindVisibleBrushSides(void) -{ - int i, j, k, we, numtextured, numsides; - float dot; - q3_dplane_t *plane; - q3_dbrushside_t *brushside; - q3_dbrush_t *brush; - q3_dsurface_t *surface; - winding_t *w; - - memset(q3_dbrushsidetextured, false, Q3_MAX_MAP_BRUSHSIDES); - // - numsides = 0; - //create planes for the planar surfaces - Q3_CreatePlanarSurfacePlanes(); - Log_Print("searching visible brush sides...\n"); - Log_Print("%6d brush sides", numsides); - //go over all the brushes - for (i = 0; i < q3_numbrushes; i++) - { - brush = &q3_dbrushes[i]; - //go over all the sides of the brush - for (j = 0; j < brush->numSides; j++) - { - qprintf("\r%6d", numsides++); - brushside = &q3_dbrushsides[brush->firstSide + j]; - // - w = Q3_BrushSideWinding(brush, brushside); - if (!w) - { - q3_dbrushsidetextured[brush->firstSide + j] = true; - continue; - } //end if - else - { - //RemoveEqualPoints(w, 0.2); - if (WindingIsTiny(w)) - { - FreeWinding(w); - q3_dbrushsidetextured[brush->firstSide + j] = true; - continue; - } //end if - else - { - we = WindingError(w); - if (we == WE_NOTENOUGHPOINTS - || we == WE_SMALLAREA - || we == WE_POINTBOGUSRANGE -// || we == WE_NONCONVEX - ) - { - FreeWinding(w); - q3_dbrushsidetextured[brush->firstSide + j] = true; - continue; - } //end if - } //end else - } //end else - if (WindingArea(w) < 20) - { - q3_dbrushsidetextured[brush->firstSide + j] = true; - continue; - } //end if - //find a face for texturing this brush - for (k = 0; k < q3_numDrawSurfaces; k++) - { - surface = &q3_drawSurfaces[k]; - if (surface->surfaceType != MST_PLANAR) continue; - // - //Q3_SurfacePlane(surface, plane.normal, &plane.dist); - plane = &q3_surfaceplanes[k]; - //the surface plane and the brush side plane should be pretty much the same - if (fabs(fabs(plane->dist) - fabs(q3_dplanes[brushside->planeNum].dist)) > 5) continue; - dot = DotProduct(plane->normal, q3_dplanes[brushside->planeNum].normal); - if (dot > -0.9 && dot < 0.9) continue; - //if the face is partly or totally on the brush side - if (Q3_FaceOnWinding(surface, w)) - { - q3_dbrushsidetextured[brush->firstSide + j] = true; - //Log_Write("Q3_FaceOnWinding"); - break; - } //end if - } //end for - FreeWinding(w); - } //end for - } //end for - qprintf("\r%6d brush sides\n", numsides); - numtextured = 0; - for (i = 0; i < q3_numbrushsides; i++) - { - if (forcesidesvisible) q3_dbrushsidetextured[i] = true; - if (q3_dbrushsidetextured[i]) numtextured++; - } //end for - Log_Print("%d brush sides textured out of %d\n", numtextured, q3_numbrushsides); -} //end of the function Q3_FindVisibleBrushSides - -/* -============= -Q3_SwapBlock - -If all values are 32 bits, this can be used to swap everything -============= -*/ -void Q3_SwapBlock( int *block, int sizeOfBlock ) { - int i; - - sizeOfBlock >>= 2; - for ( i = 0 ; i < sizeOfBlock ; i++ ) { - block[i] = LittleLong( block[i] ); - } -} //end of the function Q3_SwapBlock - -/* -============= -Q3_SwapBSPFile - -Byte swaps all data in a bsp file. -============= -*/ -void Q3_SwapBSPFile( void ) { - int i; - - // models - Q3_SwapBlock( (int *)q3_dmodels, q3_nummodels * sizeof( q3_dmodels[0] ) ); - - // shaders (don't swap the name) - for ( i = 0 ; i < q3_numShaders ; i++ ) { - q3_dshaders[i].contentFlags = LittleLong( q3_dshaders[i].contentFlags ); - q3_dshaders[i].surfaceFlags = LittleLong( q3_dshaders[i].surfaceFlags ); - } - - // planes - Q3_SwapBlock( (int *)q3_dplanes, q3_numplanes * sizeof( q3_dplanes[0] ) ); - - // nodes - Q3_SwapBlock( (int *)q3_dnodes, q3_numnodes * sizeof( q3_dnodes[0] ) ); - - // leafs - Q3_SwapBlock( (int *)q3_dleafs, q3_numleafs * sizeof( q3_dleafs[0] ) ); - - // leaffaces - Q3_SwapBlock( (int *)q3_dleafsurfaces, q3_numleafsurfaces * sizeof( q3_dleafsurfaces[0] ) ); - - // leafbrushes - Q3_SwapBlock( (int *)q3_dleafbrushes, q3_numleafbrushes * sizeof( q3_dleafbrushes[0] ) ); - - // brushes - Q3_SwapBlock( (int *)q3_dbrushes, q3_numbrushes * sizeof( q3_dbrushes[0] ) ); - - // brushsides - Q3_SwapBlock( (int *)q3_dbrushsides, q3_numbrushsides * sizeof( q3_dbrushsides[0] ) ); - - // vis - ((int *)&q3_visBytes)[0] = LittleLong( ((int *)&q3_visBytes)[0] ); - ((int *)&q3_visBytes)[1] = LittleLong( ((int *)&q3_visBytes)[1] ); - - // drawverts (don't swap colors ) - for ( i = 0 ; i < q3_numDrawVerts ; i++ ) { - q3_drawVerts[i].lightmap[0] = LittleFloat( q3_drawVerts[i].lightmap[0] ); - q3_drawVerts[i].lightmap[1] = LittleFloat( q3_drawVerts[i].lightmap[1] ); - q3_drawVerts[i].st[0] = LittleFloat( q3_drawVerts[i].st[0] ); - q3_drawVerts[i].st[1] = LittleFloat( q3_drawVerts[i].st[1] ); - q3_drawVerts[i].xyz[0] = LittleFloat( q3_drawVerts[i].xyz[0] ); - q3_drawVerts[i].xyz[1] = LittleFloat( q3_drawVerts[i].xyz[1] ); - q3_drawVerts[i].xyz[2] = LittleFloat( q3_drawVerts[i].xyz[2] ); - q3_drawVerts[i].normal[0] = LittleFloat( q3_drawVerts[i].normal[0] ); - q3_drawVerts[i].normal[1] = LittleFloat( q3_drawVerts[i].normal[1] ); - q3_drawVerts[i].normal[2] = LittleFloat( q3_drawVerts[i].normal[2] ); - } - - // drawindexes - Q3_SwapBlock( (int *)q3_drawIndexes, q3_numDrawIndexes * sizeof( q3_drawIndexes[0] ) ); - - // drawsurfs - Q3_SwapBlock( (int *)q3_drawSurfaces, q3_numDrawSurfaces * sizeof( q3_drawSurfaces[0] ) ); - - // fogs - for ( i = 0 ; i < q3_numFogs ; i++ ) { - q3_dfogs[i].brushNum = LittleLong( q3_dfogs[i].brushNum ); - } -} - - - -/* -============= -Q3_CopyLump -============= -*/ -int Q3_CopyLump( q3_dheader_t *header, int lump, void **dest, int size ) { - int length, ofs; - - length = header->lumps[lump].filelen; - ofs = header->lumps[lump].fileofs; - - if ( length % size ) { - Error ("Q3_LoadBSPFile: odd lump size"); - } - - *dest = GetMemory(length); - - memcpy( *dest, (byte *)header + ofs, length ); - - return length / size; -} - -/* -============= -CountTriangles -============= -*/ -void CountTriangles( void ) { - int i, numTris, numPatchTris; - q3_dsurface_t *surface; - - numTris = numPatchTris = 0; - for ( i = 0; i < q3_numDrawSurfaces; i++ ) { - surface = &q3_drawSurfaces[i]; - - numTris += surface->numIndexes / 3; - - if ( surface->patchWidth ) { - numPatchTris += surface->patchWidth * surface->patchHeight * 2; - } - } - - Log_Print( "%6d triangles\n", numTris ); - Log_Print( "%6d patch tris\n", numPatchTris ); -} - -/* -============= -Q3_LoadBSPFile -============= -*/ -void Q3_LoadBSPFile(struct quakefile_s *qf) -{ - q3_dheader_t *header; - - // load the file header - //LoadFile(filename, (void **)&header, offset, length); - // - LoadQuakeFile(qf, (void **)&header); - - // swap the header - Q3_SwapBlock( (int *)header, sizeof(*header) ); - - if ( header->ident != Q3_BSP_IDENT && header->ident != QL_BSP_IDENT ) { - Error( "%s is not a IBSP file", qf->filename ); - } - if ( header->version != Q3_BSP_VERSION && header->version != QL_BSP_VERSION ) { - Error( "%s is version %i, not %i", qf->filename, header->version, Q3_BSP_VERSION ); - } - - q3_numShaders = Q3_CopyLump( header, Q3_LUMP_SHADERS, (void *) &q3_dshaders, sizeof(q3_dshader_t) ); - q3_nummodels = Q3_CopyLump( header, Q3_LUMP_MODELS, (void *) &q3_dmodels, sizeof(q3_dmodel_t) ); - q3_numplanes = Q3_CopyLump( header, Q3_LUMP_PLANES, (void *) &q3_dplanes, sizeof(q3_dplane_t) ); - q3_numleafs = Q3_CopyLump( header, Q3_LUMP_LEAFS, (void *) &q3_dleafs, sizeof(q3_dleaf_t) ); - q3_numnodes = Q3_CopyLump( header, Q3_LUMP_NODES, (void *) &q3_dnodes, sizeof(q3_dnode_t) ); - q3_numleafsurfaces = Q3_CopyLump( header, Q3_LUMP_LEAFSURFACES, (void *) &q3_dleafsurfaces, sizeof(q3_dleafsurfaces[0]) ); - q3_numleafbrushes = Q3_CopyLump( header, Q3_LUMP_LEAFBRUSHES, (void *) &q3_dleafbrushes, sizeof(q3_dleafbrushes[0]) ); - q3_numbrushes = Q3_CopyLump( header, Q3_LUMP_BRUSHES, (void *) &q3_dbrushes, sizeof(q3_dbrush_t) ); - q3_numbrushsides = Q3_CopyLump( header, Q3_LUMP_BRUSHSIDES, (void *) &q3_dbrushsides, sizeof(q3_dbrushside_t) ); - q3_numDrawVerts = Q3_CopyLump( header, Q3_LUMP_DRAWVERTS, (void *) &q3_drawVerts, sizeof(q3_drawVert_t) ); - q3_numDrawSurfaces = Q3_CopyLump( header, Q3_LUMP_SURFACES, (void *) &q3_drawSurfaces, sizeof(q3_dsurface_t) ); - q3_numFogs = Q3_CopyLump( header, Q3_LUMP_FOGS, (void *) &q3_dfogs, sizeof(q3_dfog_t) ); - q3_numDrawIndexes = Q3_CopyLump( header, Q3_LUMP_DRAWINDEXES, (void *) &q3_drawIndexes, sizeof(q3_drawIndexes[0]) ); - - q3_numVisBytes = Q3_CopyLump( header, Q3_LUMP_VISIBILITY, (void *) &q3_visBytes, 1 ); - q3_numLightBytes = Q3_CopyLump( header, Q3_LUMP_LIGHTMAPS, (void *) &q3_lightBytes, 1 ); - q3_entdatasize = Q3_CopyLump( header, Q3_LUMP_ENTITIES, (void *) &q3_dentdata, 1); - - q3_numGridPoints = Q3_CopyLump( header, Q3_LUMP_LIGHTGRID, (void *) &q3_gridData, 8 ); - - CountTriangles(); - - FreeMemory( header ); // everything has been copied out - - // swap everything - Q3_SwapBSPFile(); - - Q3_FindVisibleBrushSides(); - - //Q3_PrintBSPFileSizes(); -} - - -//============================================================================ - -/* -============= -Q3_AddLump -============= -*/ -void Q3_AddLump( FILE *bspfile, q3_dheader_t *header, int lumpnum, void *data, int len ) { - q3_lump_t *lump; - - lump = &header->lumps[lumpnum]; - - lump->fileofs = LittleLong( ftell(bspfile) ); - lump->filelen = LittleLong( len ); - SafeWrite( bspfile, data, (len+3)&~3 ); -} - -/* -============= -Q3_WriteBSPFile - -Swaps the bsp file in place, so it should not be referenced again -============= -*/ -void Q3_WriteBSPFile( char *filename ) -{ - q3_dheader_t outheader, *header; - FILE *bspfile; - - header = &outheader; - memset( header, 0, sizeof(q3_dheader_t) ); - - Q3_SwapBSPFile(); - - header->ident = LittleLong( Q3_BSP_IDENT ); - header->version = LittleLong( Q3_BSP_VERSION ); - - bspfile = SafeOpenWrite( filename ); - SafeWrite( bspfile, header, sizeof(q3_dheader_t) ); // overwritten later - - Q3_AddLump( bspfile, header, Q3_LUMP_SHADERS, q3_dshaders, q3_numShaders*sizeof(q3_dshader_t) ); - Q3_AddLump( bspfile, header, Q3_LUMP_PLANES, q3_dplanes, q3_numplanes*sizeof(q3_dplane_t) ); - Q3_AddLump( bspfile, header, Q3_LUMP_LEAFS, q3_dleafs, q3_numleafs*sizeof(q3_dleaf_t) ); - Q3_AddLump( bspfile, header, Q3_LUMP_NODES, q3_dnodes, q3_numnodes*sizeof(q3_dnode_t) ); - Q3_AddLump( bspfile, header, Q3_LUMP_BRUSHES, q3_dbrushes, q3_numbrushes*sizeof(q3_dbrush_t) ); - Q3_AddLump( bspfile, header, Q3_LUMP_BRUSHSIDES, q3_dbrushsides, q3_numbrushsides*sizeof(q3_dbrushside_t) ); - Q3_AddLump( bspfile, header, Q3_LUMP_LEAFSURFACES, q3_dleafsurfaces, q3_numleafsurfaces*sizeof(q3_dleafsurfaces[0]) ); - Q3_AddLump( bspfile, header, Q3_LUMP_LEAFBRUSHES, q3_dleafbrushes, q3_numleafbrushes*sizeof(q3_dleafbrushes[0]) ); - Q3_AddLump( bspfile, header, Q3_LUMP_MODELS, q3_dmodels, q3_nummodels*sizeof(q3_dmodel_t) ); - Q3_AddLump( bspfile, header, Q3_LUMP_DRAWVERTS, q3_drawVerts, q3_numDrawVerts*sizeof(q3_drawVert_t) ); - Q3_AddLump( bspfile, header, Q3_LUMP_SURFACES, q3_drawSurfaces, q3_numDrawSurfaces*sizeof(q3_dsurface_t) ); - Q3_AddLump( bspfile, header, Q3_LUMP_VISIBILITY, q3_visBytes, q3_numVisBytes ); - Q3_AddLump( bspfile, header, Q3_LUMP_LIGHTMAPS, q3_lightBytes, q3_numLightBytes ); - Q3_AddLump( bspfile, header, Q3_LUMP_LIGHTGRID, q3_gridData, 8 * q3_numGridPoints ); - Q3_AddLump( bspfile, header, Q3_LUMP_ENTITIES, q3_dentdata, q3_entdatasize ); - Q3_AddLump( bspfile, header, Q3_LUMP_FOGS, q3_dfogs, q3_numFogs * sizeof(q3_dfog_t) ); - Q3_AddLump( bspfile, header, Q3_LUMP_DRAWINDEXES, q3_drawIndexes, q3_numDrawIndexes * sizeof(q3_drawIndexes[0]) ); - - fseek (bspfile, 0, SEEK_SET); - SafeWrite (bspfile, header, sizeof(q3_dheader_t)); - fclose (bspfile); -} - -//============================================================================ - -/* -============= -Q3_PrintBSPFileSizes - -Dumps info about current file -============= -*/ -void Q3_PrintBSPFileSizes( void ) -{ - if ( !num_entities ) - { - Q3_ParseEntities(); - } - - Log_Print ("%6i models %7i\n" - ,q3_nummodels, (int)(q3_nummodels*sizeof(q3_dmodel_t))); - Log_Print ("%6i shaders %7i\n" - ,q3_numShaders, (int)(q3_numShaders*sizeof(q3_dshader_t))); - Log_Print ("%6i brushes %7i\n" - ,q3_numbrushes, (int)(q3_numbrushes*sizeof(q3_dbrush_t))); - Log_Print ("%6i brushsides %7i\n" - ,q3_numbrushsides, (int)(q3_numbrushsides*sizeof(q3_dbrushside_t))); - Log_Print ("%6i fogs %7i\n" - ,q3_numFogs, (int)(q3_numFogs*sizeof(q3_dfog_t))); - Log_Print ("%6i planes %7i\n" - ,q3_numplanes, (int)(q3_numplanes*sizeof(q3_dplane_t))); - Log_Print ("%6i entdata %7i\n", num_entities, q3_entdatasize); - - Log_Print ("\n"); - - Log_Print ("%6i nodes %7i\n" - ,q3_numnodes, (int)(q3_numnodes*sizeof(q3_dnode_t))); - Log_Print ("%6i leafs %7i\n" - ,q3_numleafs, (int)(q3_numleafs*sizeof(q3_dleaf_t))); - Log_Print ("%6i leafsurfaces %7i\n" - ,q3_numleafsurfaces, (int)(q3_numleafsurfaces*sizeof(q3_dleafsurfaces[0]))); - Log_Print ("%6i leafbrushes %7i\n" - ,q3_numleafbrushes, (int)(q3_numleafbrushes*sizeof(q3_dleafbrushes[0]))); - Log_Print ("%6i drawverts %7i\n" - ,q3_numDrawVerts, (int)(q3_numDrawVerts*sizeof(q3_drawVerts[0]))); - Log_Print ("%6i drawindexes %7i\n" - ,q3_numDrawIndexes, (int)(q3_numDrawIndexes*sizeof(q3_drawIndexes[0]))); - Log_Print ("%6i drawsurfaces %7i\n" - ,q3_numDrawSurfaces, (int)(q3_numDrawSurfaces*sizeof(q3_drawSurfaces[0]))); - - Log_Print ("%6i lightmaps %7i\n" - ,q3_numLightBytes / (LIGHTMAP_WIDTH*LIGHTMAP_HEIGHT*3), q3_numLightBytes ); - Log_Print (" visibility %7i\n" - , q3_numVisBytes ); -} - -/* -================ -Q3_ParseEntities - -Parses the q3_dentdata string into entities -================ -*/ -void Q3_ParseEntities (void) -{ - script_t *script; - - num_entities = 0; - script = LoadScriptMemory(q3_dentdata, q3_entdatasize, "*Quake3 bsp file"); - SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES | - SCFL_NOSTRINGESCAPECHARS); - - while(ParseEntity(script)) - { - } //end while - - FreeScript(script); -} //end of the function Q3_ParseEntities - - -/* -================ -Q3_UnparseEntities - -Generates the q3_dentdata string from all the entities -================ -*/ -void Q3_UnparseEntities (void) -{ - char *buf, *end; - epair_t *ep; - char line[2048]; - int i; - - buf = q3_dentdata; - end = buf; - *end = 0; - - for (i=0 ; inext) - { - sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value); - strcat (end, line); - end += strlen(line); - } - strcat (end,"}\n"); - end += 2; - - if (end > buf + Q3_MAX_MAP_ENTSTRING) - Error ("Entity text too long"); - } - q3_entdatasize = end - buf + 1; -} //end of the function Q3_UnparseEntities - - diff --git a/tools/quake3/bspc/l_bsp_q3.h b/tools/quake3/bspc/l_bsp_q3.h deleted file mode 100644 index 3573c87c..00000000 --- a/tools/quake3/bspc/l_bsp_q3.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "q3files.h" -//#include "surfaceflags.h" - -extern int q3_nummodels; -extern q3_dmodel_t *q3_dmodels;//[MAX_MAP_MODELS]; - -extern int q3_numShaders; -extern q3_dshader_t *q3_dshaders;//[Q3_MAX_MAP_SHADERS]; - -extern int q3_entdatasize; -extern char *q3_dentdata;//[Q3_MAX_MAP_ENTSTRING]; - -extern int q3_numleafs; -extern q3_dleaf_t *q3_dleafs;//[Q3_MAX_MAP_LEAFS]; - -extern int q3_numplanes; -extern q3_dplane_t *q3_dplanes;//[Q3_MAX_MAP_PLANES]; - -extern int q3_numnodes; -extern q3_dnode_t *q3_dnodes;//[Q3_MAX_MAP_NODES]; - -extern int q3_numleafsurfaces; -extern int *q3_dleafsurfaces;//[Q3_MAX_MAP_LEAFFACES]; - -extern int q3_numleafbrushes; -extern int *q3_dleafbrushes;//[Q3_MAX_MAP_LEAFBRUSHES]; - -extern int q3_numbrushes; -extern q3_dbrush_t *q3_dbrushes;//[Q3_MAX_MAP_BRUSHES]; - -extern int q3_numbrushsides; -extern q3_dbrushside_t *q3_dbrushsides;//[Q3_MAX_MAP_BRUSHSIDES]; - -extern int q3_numLightBytes; -extern byte *q3_lightBytes;//[Q3_MAX_MAP_LIGHTING]; - -extern int q3_numGridPoints; -extern byte *q3_gridData;//[Q3_MAX_MAP_LIGHTGRID]; - -extern int q3_numVisBytes; -extern byte *q3_visBytes;//[Q3_MAX_MAP_VISIBILITY]; - -extern int q3_numDrawVerts; -extern q3_drawVert_t *q3_drawVerts;//[Q3_MAX_MAP_DRAW_VERTS]; - -extern int q3_numDrawIndexes; -extern int *q3_drawIndexes;//[Q3_MAX_MAP_DRAW_INDEXES]; - -extern int q3_numDrawSurfaces; -extern q3_dsurface_t *q3_drawSurfaces;//[Q3_MAX_MAP_DRAW_SURFS]; - -extern int q3_numFogs; -extern q3_dfog_t *q3_dfogs;//[Q3_MAX_MAP_FOGS]; - -extern char q3_dbrushsidetextured[Q3_MAX_MAP_BRUSHSIDES]; - -void Q3_LoadBSPFile(struct quakefile_s *qf); -void Q3_FreeMaxBSP(void); -void Q3_ParseEntities (void); diff --git a/tools/quake3/bspc/l_bsp_sin.c b/tools/quake3/bspc/l_bsp_sin.c deleted file mode 100644 index 3ae76770..00000000 --- a/tools/quake3/bspc/l_bsp_sin.c +++ /dev/null @@ -1,1186 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "l_cmd.h" -#include "l_math.h" -#include "l_mem.h" -#include "l_log.h" -#include "l_poly.h" -#include "botlib/l_script.h" -#include "l_bsp_ent.h" -#include "l_bsp_sin.h" - -void GetLeafNums (void); - -//============================================================================= - -int sin_nummodels; -sin_dmodel_t *sin_dmodels;//[SIN_MAX_MAP_MODELS]; - -int sin_visdatasize; -byte *sin_dvisdata;//[SIN_MAX_MAP_VISIBILITY]; -sin_dvis_t *sin_dvis;// = (sin_dvis_t *)sin_sin_dvisdata; - -int sin_lightdatasize; -byte *sin_dlightdata;//[SIN_MAX_MAP_LIGHTING]; - -int sin_entdatasize; -char *sin_dentdata;//[SIN_MAX_MAP_ENTSTRING]; - -int sin_numleafs; -sin_dleaf_t *sin_dleafs;//[SIN_MAX_MAP_LEAFS]; - -int sin_numplanes; -sin_dplane_t *sin_dplanes;//[SIN_MAX_MAP_PLANES]; - -int sin_numvertexes; -sin_dvertex_t *sin_dvertexes;//[SIN_MAX_MAP_VERTS]; - -int sin_numnodes; -sin_dnode_t *sin_dnodes;//[SIN_MAX_MAP_NODES]; - -int sin_numtexinfo; -sin_texinfo_t *sin_texinfo;//[SIN_MAX_MAP_sin_texinfo]; - -int sin_numfaces; -sin_dface_t *sin_dfaces;//[SIN_MAX_MAP_FACES]; - -int sin_numedges; -sin_dedge_t *sin_dedges;//[SIN_MAX_MAP_EDGES]; - -int sin_numleaffaces; -unsigned short *sin_dleaffaces;//[SIN_MAX_MAP_LEAFFACES]; - -int sin_numleafbrushes; -unsigned short *sin_dleafbrushes;//[SIN_MAX_MAP_LEAFBRUSHES]; - -int sin_numsurfedges; -int *sin_dsurfedges;//[SIN_MAX_MAP_SURFEDGES]; - -int sin_numbrushes; -sin_dbrush_t *sin_dbrushes;//[SIN_MAX_MAP_BRUSHES]; - -int sin_numbrushsides; -sin_dbrushside_t *sin_dbrushsides;//[SIN_MAX_MAP_BRUSHSIDES]; - -int sin_numareas; -sin_darea_t *sin_dareas;//[SIN_MAX_MAP_AREAS]; - -int sin_numareaportals; -sin_dareaportal_t *sin_dareaportals;//[SIN_MAX_MAP_AREAPORTALS]; - -int sin_numlightinfo; -sin_lightvalue_t *sin_lightinfo;//[SIN_MAX_MAP_LIGHTINFO]; - -byte sin_dpop[256]; - -char sin_dbrushsidetextured[SIN_MAX_MAP_BRUSHSIDES]; - -int sin_bspallocated = false; -int sin_allocatedbspmem = 0; - -void Sin_AllocMaxBSP(void) -{ - //models - sin_nummodels = 0; - sin_dmodels = (sin_dmodel_t *) GetClearedMemory(SIN_MAX_MAP_MODELS * sizeof(sin_dmodel_t)); - sin_allocatedbspmem += SIN_MAX_MAP_MODELS * sizeof(sin_dmodel_t); - //vis data - sin_visdatasize = 0; - sin_dvisdata = (byte *) GetClearedMemory(SIN_MAX_MAP_VISIBILITY * sizeof(byte)); - sin_dvis = (sin_dvis_t *) sin_dvisdata; - sin_allocatedbspmem += SIN_MAX_MAP_VISIBILITY * sizeof(byte); - //light data - sin_lightdatasize = 0; - sin_dlightdata = (byte *) GetClearedMemory(SIN_MAX_MAP_LIGHTING * sizeof(byte)); - sin_allocatedbspmem += SIN_MAX_MAP_LIGHTING * sizeof(byte); - //entity data - sin_entdatasize = 0; - sin_dentdata = (char *) GetClearedMemory(SIN_MAX_MAP_ENTSTRING * sizeof(char)); - sin_allocatedbspmem += SIN_MAX_MAP_ENTSTRING * sizeof(char); - //leafs - sin_numleafs = 0; - sin_dleafs = (sin_dleaf_t *) GetClearedMemory(SIN_MAX_MAP_LEAFS * sizeof(sin_dleaf_t)); - sin_allocatedbspmem += SIN_MAX_MAP_LEAFS * sizeof(sin_dleaf_t); - //planes - sin_numplanes = 0; - sin_dplanes = (sin_dplane_t *) GetClearedMemory(SIN_MAX_MAP_PLANES * sizeof(sin_dplane_t)); - sin_allocatedbspmem += SIN_MAX_MAP_PLANES * sizeof(sin_dplane_t); - //vertexes - sin_numvertexes = 0; - sin_dvertexes = (sin_dvertex_t *) GetClearedMemory(SIN_MAX_MAP_VERTS * sizeof(sin_dvertex_t)); - sin_allocatedbspmem += SIN_MAX_MAP_VERTS * sizeof(sin_dvertex_t); - //nodes - sin_numnodes = 0; - sin_dnodes = (sin_dnode_t *) GetClearedMemory(SIN_MAX_MAP_NODES * sizeof(sin_dnode_t)); - sin_allocatedbspmem += SIN_MAX_MAP_NODES * sizeof(sin_dnode_t); - //texture info - sin_numtexinfo = 0; - sin_texinfo = (sin_texinfo_t *) GetClearedMemory(SIN_MAX_MAP_TEXINFO * sizeof(sin_texinfo_t)); - sin_allocatedbspmem += SIN_MAX_MAP_TEXINFO * sizeof(sin_texinfo_t); - //faces - sin_numfaces = 0; - sin_dfaces = (sin_dface_t *) GetClearedMemory(SIN_MAX_MAP_FACES * sizeof(sin_dface_t)); - sin_allocatedbspmem += SIN_MAX_MAP_FACES * sizeof(sin_dface_t); - //edges - sin_numedges = 0; - sin_dedges = (sin_dedge_t *) GetClearedMemory(SIN_MAX_MAP_EDGES * sizeof(sin_dedge_t)); - sin_allocatedbspmem += SIN_MAX_MAP_EDGES * sizeof(sin_dedge_t); - //leaf faces - sin_numleaffaces = 0; - sin_dleaffaces = (unsigned short *) GetClearedMemory(SIN_MAX_MAP_LEAFFACES * sizeof(unsigned short)); - sin_allocatedbspmem += SIN_MAX_MAP_LEAFFACES * sizeof(unsigned short); - //leaf brushes - sin_numleafbrushes = 0; - sin_dleafbrushes = (unsigned short *) GetClearedMemory(SIN_MAX_MAP_LEAFBRUSHES * sizeof(unsigned short)); - sin_allocatedbspmem += SIN_MAX_MAP_LEAFBRUSHES * sizeof(unsigned short); - //surface edges - sin_numsurfedges = 0; - sin_dsurfedges = (int *) GetClearedMemory(SIN_MAX_MAP_SURFEDGES * sizeof(int)); - sin_allocatedbspmem += SIN_MAX_MAP_SURFEDGES * sizeof(int); - //brushes - sin_numbrushes = 0; - sin_dbrushes = (sin_dbrush_t *) GetClearedMemory(SIN_MAX_MAP_BRUSHES * sizeof(sin_dbrush_t)); - sin_allocatedbspmem += SIN_MAX_MAP_BRUSHES * sizeof(sin_dbrush_t); - //brushsides - sin_numbrushsides = 0; - sin_dbrushsides = (sin_dbrushside_t *) GetClearedMemory(SIN_MAX_MAP_BRUSHSIDES * sizeof(sin_dbrushside_t)); - sin_allocatedbspmem += SIN_MAX_MAP_BRUSHSIDES * sizeof(sin_dbrushside_t); - //areas - sin_numareas = 0; - sin_dareas = (sin_darea_t *) GetClearedMemory(SIN_MAX_MAP_AREAS * sizeof(sin_darea_t)); - sin_allocatedbspmem += SIN_MAX_MAP_AREAS * sizeof(sin_darea_t); - //area portals - sin_numareaportals = 0; - sin_dareaportals = (sin_dareaportal_t *) GetClearedMemory(SIN_MAX_MAP_AREAPORTALS * sizeof(sin_dareaportal_t)); - sin_allocatedbspmem += SIN_MAX_MAP_AREAPORTALS * sizeof(sin_dareaportal_t); - //light info - sin_numlightinfo = 0; - sin_lightinfo = (sin_lightvalue_t *) GetClearedMemory(SIN_MAX_MAP_LIGHTINFO * sizeof(sin_lightvalue_t)); - sin_allocatedbspmem += SIN_MAX_MAP_LIGHTINFO * sizeof(sin_lightvalue_t); - //print allocated memory - Log_Print("allocated "); - PrintMemorySize(sin_allocatedbspmem); - Log_Print(" of BSP memory\n"); -} //end of the function Sin_AllocMaxBSP - -void Sin_FreeMaxBSP(void) -{ - //models - sin_nummodels = 0; - FreeMemory(sin_dmodels); - sin_dmodels = NULL; - //vis data - sin_visdatasize = 0; - FreeMemory(sin_dvisdata); - sin_dvisdata = NULL; - sin_dvis = NULL; - //light data - sin_lightdatasize = 0; - FreeMemory(sin_dlightdata); - sin_dlightdata = NULL; - //entity data - sin_entdatasize = 0; - FreeMemory(sin_dentdata); - sin_dentdata = NULL; - //leafs - sin_numleafs = 0; - FreeMemory(sin_dleafs); - sin_dleafs = NULL; - //planes - sin_numplanes = 0; - FreeMemory(sin_dplanes); - sin_dplanes = NULL; - //vertexes - sin_numvertexes = 0; - FreeMemory(sin_dvertexes); - sin_dvertexes = NULL; - //nodes - sin_numnodes = 0; - FreeMemory(sin_dnodes); - sin_dnodes = NULL; - //texture info - sin_numtexinfo = 0; - FreeMemory(sin_texinfo); - sin_texinfo = NULL; - //faces - sin_numfaces = 0; - FreeMemory(sin_dfaces); - sin_dfaces = NULL; - //edges - sin_numedges = 0; - FreeMemory(sin_dedges); - sin_dedges = NULL; - //leaf faces - sin_numleaffaces = 0; - FreeMemory(sin_dleaffaces); - sin_dleaffaces = NULL; - //leaf brushes - sin_numleafbrushes = 0; - FreeMemory(sin_dleafbrushes); - sin_dleafbrushes = NULL; - //surface edges - sin_numsurfedges = 0; - FreeMemory(sin_dsurfedges); - sin_dsurfedges = NULL; - //brushes - sin_numbrushes = 0; - FreeMemory(sin_dbrushes); - sin_dbrushes = NULL; - //brushsides - sin_numbrushsides = 0; - FreeMemory(sin_dbrushsides); - sin_dbrushsides = NULL; - //areas - sin_numareas = 0; - FreeMemory(sin_dareas); - sin_dareas = NULL; - //area portals - sin_numareaportals = 0; - FreeMemory(sin_dareaportals); - sin_dareaportals = NULL; - //light info - sin_numlightinfo = 0; - FreeMemory(sin_lightinfo); - sin_lightinfo = NULL; - // - Log_Print("freed "); - PrintMemorySize(sin_allocatedbspmem); - Log_Print(" of BSP memory\n"); - sin_allocatedbspmem = 0; -} //end of the function Sin_FreeMaxBSP - -#define WCONVEX_EPSILON 0.5 - -//=========================================================================== -// returns the amount the face and the winding overlap -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float Sin_FaceOnWinding(sin_dface_t *face, winding_t *winding) -{ - int i, edgenum, side; - float dist, area; - sin_dplane_t plane; - vec_t *v1, *v2; - vec3_t normal, edgevec; - winding_t *w; - - // - w = CopyWinding(winding); - memcpy(&plane, &sin_dplanes[face->planenum], sizeof(sin_dplane_t)); - //check on which side of the plane the face is - if (face->side) - { - VectorNegate(plane.normal, plane.normal); - plane.dist = -plane.dist; - } //end if - for (i = 0; i < face->numedges && w; i++) - { - //get the first and second vertex of the edge - edgenum = sin_dsurfedges[face->firstedge + i]; - side = edgenum > 0; - //if the face plane is flipped - v1 = sin_dvertexes[sin_dedges[abs(edgenum)].v[side]].point; - v2 = sin_dvertexes[sin_dedges[abs(edgenum)].v[!side]].point; - //create a plane through the edge vector, orthogonal to the face plane - //and with the normal vector pointing out of the face - VectorSubtract(v1, v2, edgevec); - CrossProduct(edgevec, plane.normal, normal); - VectorNormalize(normal); - dist = DotProduct(normal, v1); - // - ChopWindingInPlace(&w, normal, dist, 0.9); //CLIP_EPSILON - } //end for - if (w) - { - area = WindingArea(w); - FreeWinding(w); - return area; - } //end if - return 0; -} //end of the function Sin_FaceOnWinding -//=========================================================================== -// creates a winding for the given brush side on the given brush -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -winding_t *Sin_BrushSideWinding(sin_dbrush_t *brush, sin_dbrushside_t *baseside) -{ - int i; - sin_dplane_t *baseplane, *plane; - sin_dbrushside_t *side; - winding_t *w; - - //create a winding for the brush side with the given planenumber - baseplane = &sin_dplanes[baseside->planenum]; - w = BaseWindingForPlane(baseplane->normal, baseplane->dist); - for (i = 0; i < brush->numsides && w; i++) - { - side = &sin_dbrushsides[brush->firstside + i]; - //don't chop with the base plane - if (side->planenum == baseside->planenum) continue; - //also don't use planes that are almost equal - plane = &sin_dplanes[side->planenum]; - if (DotProduct(baseplane->normal, plane->normal) > 0.999 - && fabs(baseplane->dist - plane->dist) < 0.01) continue; - // - plane = &sin_dplanes[side->planenum^1]; - ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); - } //end for - return w; -} //end of the function Sin_BrushSideWinding -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Sin_HintSkipBrush(sin_dbrush_t *brush) -{ - int j; - sin_dbrushside_t *brushside; - - for (j = 0; j < brush->numsides; j++) - { - brushside = &sin_dbrushsides[brush->firstside + j]; - if (brushside->texinfo > 0) - { - if (sin_texinfo[brushside->texinfo].flags & (SURF_SKIP|SURF_HINT)) - { - return true; - } //end if - } //end if - } //end for - return false; -} //end of the function Sin_HintSkipBrush -//=========================================================================== -// fix screwed brush texture references -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WindingIsTiny(winding_t *w); - -void Sin_FixTextureReferences(void) -{ - int i, j, k, we; - sin_dbrushside_t *brushside; - sin_dbrush_t *brush; - sin_dface_t *face; - winding_t *w; - - memset(sin_dbrushsidetextured, false, SIN_MAX_MAP_BRUSHSIDES); - //go over all the brushes - for (i = 0; i < sin_numbrushes; i++) - { - brush = &sin_dbrushes[i]; - //hint brushes are not textured - if (Sin_HintSkipBrush(brush)) continue; - //go over all the sides of the brush - for (j = 0; j < brush->numsides; j++) - { - brushside = &sin_dbrushsides[brush->firstside + j]; - // - w = Sin_BrushSideWinding(brush, brushside); - if (!w) - { - sin_dbrushsidetextured[brush->firstside + j] = true; - continue; - } //end if - else - { - //RemoveEqualPoints(w, 0.2); - if (WindingIsTiny(w)) - { - FreeWinding(w); - sin_dbrushsidetextured[brush->firstside + j] = true; - continue; - } //end if - else - { - we = WindingError(w); - if (we == WE_NOTENOUGHPOINTS - || we == WE_SMALLAREA - || we == WE_POINTBOGUSRANGE -// || we == WE_NONCONVEX - ) - { - FreeWinding(w); - sin_dbrushsidetextured[brush->firstside + j] = true; - continue; - } //end if - } //end else - } //end else - if (WindingArea(w) < 20) - { - sin_dbrushsidetextured[brush->firstside + j] = true; - } //end if - //find a face for texturing this brush - for (k = 0; k < sin_numfaces; k++) - { - face = &sin_dfaces[k]; - //if the face is in the same plane as the brush side - if ((face->planenum&~1) != (brushside->planenum&~1)) continue; - //if the face is partly or totally on the brush side - if (Sin_FaceOnWinding(face, w)) - { - brushside->texinfo = face->texinfo; - sin_dbrushsidetextured[brush->firstside + j] = true; - break; - } //end if - } //end for - FreeWinding(w); - } //end for - } //end for -} //end of the function Sin_FixTextureReferences*/ - -/* -=============== -CompressVis - -=============== -*/ -int Sin_CompressVis (byte *vis, byte *dest) -{ - int j; - int rep; - int visrow; - byte *dest_p; - - dest_p = dest; -// visrow = (r_numvisleafs + 7)>>3; - visrow = (sin_dvis->numclusters + 7)>>3; - - for (j=0 ; j>3; - row = (sin_dvis->numclusters+7)>>3; - out = decompressed; - - do - { - if (*in) - { - *out++ = *in++; - continue; - } - - c = in[1]; - if (!c) - Error ("DecompressVis: 0 repeat"); - in += 2; - while (c) - { - *out++ = 0; - c--; - } - } while (out - decompressed < row); -} //end of the function Sin_DecompressVis - -//============================================================================= - -/* -============= -Sin_SwapBSPFile - -Byte swaps all data in a bsp file. -============= -*/ -void Sin_SwapBSPFile (qboolean todisk) -{ - int i, j; - sin_dmodel_t *d; - - -// models - for (i=0 ; ifirstface = LittleLong (d->firstface); - d->numfaces = LittleLong (d->numfaces); - d->headnode = LittleLong (d->headnode); - - for (j=0 ; j<3 ; j++) - { - d->mins[j] = LittleFloat(d->mins[j]); - d->maxs[j] = LittleFloat(d->maxs[j]); - d->origin[j] = LittleFloat(d->origin[j]); - } - } - -// -// vertexes -// - for (i=0 ; inumclusters; - else - j = LittleLong(sin_dvis->numclusters); - sin_dvis->numclusters = LittleLong (sin_dvis->numclusters); - for (i=0 ; ibitofs[i][0] = LittleLong (sin_dvis->bitofs[i][0]); - sin_dvis->bitofs[i][1] = LittleLong (sin_dvis->bitofs[i][1]); - } -} //end of the function Sin_SwapBSPFile - - -sin_dheader_t *header; -#ifdef SIN -int Sin_CopyLump (int lump, void *dest, int size, int maxsize) -{ - int length, ofs; - - length = header->lumps[lump].filelen; - ofs = header->lumps[lump].fileofs; - - if (length % size) - Error ("Sin_LoadBSPFile: odd lump size"); - - if ((length/size) > maxsize) - Error ("Sin_LoadBSPFile: exceeded max size for lump %d size %d > maxsize %d\n", lump, (length/size), maxsize ); - - memcpy (dest, (byte *)header + ofs, length); - - return length / size; -} -#else -int Sin_CopyLump (int lump, void *dest, int size) -{ - int length, ofs; - - length = header->lumps[lump].filelen; - ofs = header->lumps[lump].fileofs; - - if (length % size) - Error ("Sin_LoadBSPFile: odd lump size"); - - memcpy (dest, (byte *)header + ofs, length); - - return length / size; -} -#endif - -/* -============= -Sin_LoadBSPFile -============= -*/ -void Sin_LoadBSPFile(char *filename, int offset, int length) -{ - int i; - -// -// load the file header -// - LoadFile (filename, (void **)&header, offset, length); - -// swap the header - for (i=0 ; i< sizeof(sin_dheader_t)/4 ; i++) - ((int *)header)[i] = LittleLong ( ((int *)header)[i]); - - if (header->ident != SIN_BSPHEADER && header->ident != SINGAME_BSPHEADER) - Error ("%s is not a IBSP file", filename); - if (header->version != SIN_BSPVERSION && header->version != SINGAME_BSPVERSION) - Error ("%s is version %i, not %i", filename, header->version, SIN_BSPVERSION); - -#ifdef SIN - sin_nummodels = Sin_CopyLump (SIN_LUMP_MODELS, sin_dmodels, sizeof(sin_dmodel_t), SIN_MAX_MAP_MODELS); - sin_numvertexes = Sin_CopyLump (SIN_LUMP_VERTEXES, sin_dvertexes, sizeof(sin_dvertex_t), SIN_MAX_MAP_VERTS); - sin_numplanes = Sin_CopyLump (SIN_LUMP_PLANES, sin_dplanes, sizeof(sin_dplane_t), SIN_MAX_MAP_PLANES); - sin_numleafs = Sin_CopyLump (SIN_LUMP_LEAFS, sin_dleafs, sizeof(sin_dleaf_t), SIN_MAX_MAP_LEAFS); - sin_numnodes = Sin_CopyLump (SIN_LUMP_NODES, sin_dnodes, sizeof(sin_dnode_t), SIN_MAX_MAP_NODES); - sin_numtexinfo = Sin_CopyLump (SIN_LUMP_TEXINFO, sin_texinfo, sizeof(sin_texinfo_t), SIN_MAX_MAP_TEXINFO); - sin_numfaces = Sin_CopyLump (SIN_LUMP_FACES, sin_dfaces, sizeof(sin_dface_t), SIN_MAX_MAP_FACES); - sin_numleaffaces = Sin_CopyLump (SIN_LUMP_LEAFFACES, sin_dleaffaces, sizeof(sin_dleaffaces[0]), SIN_MAX_MAP_LEAFFACES); - sin_numleafbrushes = Sin_CopyLump (SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sizeof(sin_dleafbrushes[0]), SIN_MAX_MAP_LEAFBRUSHES); - sin_numsurfedges = Sin_CopyLump (SIN_LUMP_SURFEDGES, sin_dsurfedges, sizeof(sin_dsurfedges[0]), SIN_MAX_MAP_SURFEDGES); - sin_numedges = Sin_CopyLump (SIN_LUMP_EDGES, sin_dedges, sizeof(sin_dedge_t), SIN_MAX_MAP_EDGES); - sin_numbrushes = Sin_CopyLump (SIN_LUMP_BRUSHES, sin_dbrushes, sizeof(sin_dbrush_t), SIN_MAX_MAP_BRUSHES); - sin_numbrushsides = Sin_CopyLump (SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sizeof(sin_dbrushside_t), SIN_MAX_MAP_BRUSHSIDES); - sin_numareas = Sin_CopyLump (SIN_LUMP_AREAS, sin_dareas, sizeof(sin_darea_t), SIN_MAX_MAP_AREAS); - sin_numareaportals = Sin_CopyLump (SIN_LUMP_AREAPORTALS, sin_dareaportals, sizeof(sin_dareaportal_t), SIN_MAX_MAP_AREAPORTALS); - sin_numlightinfo = Sin_CopyLump (SIN_LUMP_LIGHTINFO, sin_lightinfo, sizeof(sin_lightvalue_t), SIN_MAX_MAP_LIGHTINFO); - - sin_visdatasize = Sin_CopyLump (SIN_LUMP_VISIBILITY, sin_dvisdata, 1, SIN_MAX_MAP_VISIBILITY); - sin_lightdatasize = Sin_CopyLump (SIN_LUMP_LIGHTING, sin_dlightdata, 1, SIN_MAX_MAP_LIGHTING); - sin_entdatasize = Sin_CopyLump (SIN_LUMP_ENTITIES, sin_dentdata, 1, SIN_MAX_MAP_ENTSTRING); - - Sin_CopyLump (SIN_LUMP_POP, sin_dpop, 1, sizeof(sin_dpop)); -#else - sin_nummodels = Sin_CopyLump (SIN_LUMP_MODELS, sin_dmodels, sizeof(sin_dmodel_t)); - sin_numvertexes = Sin_CopyLump (SIN_LUMP_VERTEXES, sin_dvertexes, sizeof(sin_dvertex_t)); - sin_numplanes = Sin_CopyLump (SIN_LUMP_PLANES, sin_dplanes, sizeof(sin_dplane_t)); - sin_numleafs = Sin_CopyLump (SIN_LUMP_LEAFS, sin_dleafs, sizeof(sin_dleaf_t)); - sin_numnodes = Sin_CopyLump (SIN_LUMP_NODES, sin_dnodes, sizeof(sin_dnode_t)); - sin_numtexinfo = Sin_CopyLump (SIN_LUMP_TEXINFO, sin_texinfo, sizeof(sin_texinfo_t)); - sin_numfaces = Sin_CopyLump (SIN_LUMP_FACES, sin_dfaces, sizeof(sin_dface_t)); - sin_numleaffaces = Sin_CopyLump (SIN_LUMP_LEAFFACES, sin_dleaffaces, sizeof(sin_dleaffaces[0])); - sin_numleafbrushes = Sin_CopyLump (SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sizeof(sin_dleafbrushes[0])); - sin_numsurfedges = Sin_CopyLump (SIN_LUMP_SURFEDGES, sin_dsurfedges, sizeof(sin_dsurfedges[0])); - sin_numedges = Sin_CopyLump (SIN_LUMP_EDGES, sin_dedges, sizeof(sin_dedge_t)); - sin_numbrushes = Sin_CopyLump (SIN_LUMP_BRUSHES, sin_dbrushes, sizeof(sin_dbrush_t)); - sin_numbrushsides = Sin_CopyLump (SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sizeof(sin_dbrushside_t)); - sin_numareas = Sin_CopyLump (SIN_LUMP_AREAS, sin_dareas, sizeof(sin_darea_t)); - sin_numareaportals = Sin_CopyLump (SIN_LUMP_AREAPORTALS, sin_dareaportals, sizeof(sin_dareaportal_t)); - - sin_visdatasize = Sin_CopyLump (SIN_LUMP_VISIBILITY, sin_dvisdata, 1); - sin_lightdatasize = Sin_CopyLump (SIN_LUMP_LIGHTING, sin_dlightdata, 1); - sin_entdatasize = Sin_CopyLump (SIN_LUMP_ENTITIES, sin_dentdata, 1); - - Sin_CopyLump (SIN_LUMP_POP, sin_dpop, 1); -#endif - - FreeMemory(header); // everything has been copied out - -// -// swap everything -// - Sin_SwapBSPFile (false); -} //end of the function Sin_LoadBSPFile - -/* -============= -Sin_LoadBSPFilesTexinfo - -Only loads the sin_texinfo lump, so qdata can scan for textures -============= -*/ -void Sin_LoadBSPFileTexinfo (char *filename) -{ - int i; - FILE *f; - int length, ofs; - - header = GetMemory(sizeof(sin_dheader_t)); - - f = fopen (filename, "rb"); - fread (header, sizeof(sin_dheader_t), 1, f); - -// swap the header - for (i=0 ; i< sizeof(sin_dheader_t)/4 ; i++) - ((int *)header)[i] = LittleLong ( ((int *)header)[i]); - - if (header->ident != SIN_BSPHEADER && header->ident != SINGAME_BSPHEADER) - Error ("%s is not a IBSP file", filename); - if (header->version != SIN_BSPVERSION && header->version != SINGAME_BSPVERSION) - Error ("%s is version %i, not %i", filename, header->version, SIN_BSPVERSION); - - - length = header->lumps[SIN_LUMP_TEXINFO].filelen; - ofs = header->lumps[SIN_LUMP_TEXINFO].fileofs; - - fseek (f, ofs, SEEK_SET); - fread (sin_texinfo, length, 1, f); - fclose (f); - - sin_numtexinfo = length / sizeof(sin_texinfo_t); - - FreeMemory(header); // everything has been copied out - - Sin_SwapBSPFile (false); -} //end of the function Sin_LoadBSPFilesTexinfo - - -//============================================================================ - -FILE *wadfile; -sin_dheader_t outheader; - -#ifdef SIN -void Sin_AddLump (int lumpnum, void *data, int len, int size, int maxsize) -{ - sin_lump_t *lump; - int totallength; - - totallength = len*size; - - if (len > maxsize) - Error ("Sin_WriteBSPFile: exceeded max size for lump %d size %d > maxsize %d\n", lumpnum, len, maxsize ); - - lump = &header->lumps[lumpnum]; - - lump->fileofs = LittleLong( ftell(wadfile) ); - lump->filelen = LittleLong(totallength); - SafeWrite (wadfile, data, (totallength+3)&~3); -} -#else -void Sin_AddLump (int lumpnum, void *data, int len) -{ - sin_lump_t *lump; - - lump = &header->lumps[lumpnum]; - - lump->fileofs = LittleLong( ftell(wadfile) ); - lump->filelen = LittleLong(len); - SafeWrite (wadfile, data, (len+3)&~3); -} -#endif -/* -============= -Sin_WriteBSPFile - -Swaps the bsp file in place, so it should not be referenced again -============= -*/ -void Sin_WriteBSPFile (char *filename) -{ - header = &outheader; - memset (header, 0, sizeof(sin_dheader_t)); - - Sin_SwapBSPFile (true); - - header->ident = LittleLong (SIN_BSPHEADER); - header->version = LittleLong (SIN_BSPVERSION); - - wadfile = SafeOpenWrite (filename); - SafeWrite (wadfile, header, sizeof(sin_dheader_t)); // overwritten later - -#ifdef SIN - Sin_AddLump (SIN_LUMP_PLANES, sin_dplanes, sin_numplanes, sizeof(sin_dplane_t), SIN_MAX_MAP_PLANES); - Sin_AddLump (SIN_LUMP_LEAFS, sin_dleafs, sin_numleafs, sizeof(sin_dleaf_t), SIN_MAX_MAP_LEAFS); - Sin_AddLump (SIN_LUMP_VERTEXES, sin_dvertexes, sin_numvertexes, sizeof(sin_dvertex_t), SIN_MAX_MAP_VERTS); - Sin_AddLump (SIN_LUMP_NODES, sin_dnodes, sin_numnodes, sizeof(sin_dnode_t), SIN_MAX_MAP_NODES); - Sin_AddLump (SIN_LUMP_TEXINFO, sin_texinfo, sin_numtexinfo, sizeof(sin_texinfo_t), SIN_MAX_MAP_TEXINFO); - Sin_AddLump (SIN_LUMP_FACES, sin_dfaces, sin_numfaces, sizeof(sin_dface_t), SIN_MAX_MAP_FACES); - Sin_AddLump (SIN_LUMP_BRUSHES, sin_dbrushes, sin_numbrushes, sizeof(sin_dbrush_t), SIN_MAX_MAP_BRUSHES); - Sin_AddLump (SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sin_numbrushsides, sizeof(sin_dbrushside_t), SIN_MAX_MAP_BRUSHSIDES); - Sin_AddLump (SIN_LUMP_LEAFFACES, sin_dleaffaces, sin_numleaffaces, sizeof(sin_dleaffaces[0]), SIN_MAX_MAP_LEAFFACES); - Sin_AddLump (SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sin_numleafbrushes, sizeof(sin_dleafbrushes[0]), SIN_MAX_MAP_LEAFBRUSHES); - Sin_AddLump (SIN_LUMP_SURFEDGES, sin_dsurfedges, sin_numsurfedges, sizeof(sin_dsurfedges[0]), SIN_MAX_MAP_SURFEDGES); - Sin_AddLump (SIN_LUMP_EDGES, sin_dedges, sin_numedges, sizeof(sin_dedge_t), SIN_MAX_MAP_EDGES); - Sin_AddLump (SIN_LUMP_MODELS, sin_dmodels, sin_nummodels, sizeof(sin_dmodel_t), SIN_MAX_MAP_MODELS); - Sin_AddLump (SIN_LUMP_AREAS, sin_dareas, sin_numareas, sizeof(sin_darea_t), SIN_MAX_MAP_AREAS); - Sin_AddLump (SIN_LUMP_AREAPORTALS, sin_dareaportals, sin_numareaportals, sizeof(sin_dareaportal_t), SIN_MAX_MAP_AREAPORTALS); - Sin_AddLump (SIN_LUMP_LIGHTINFO, sin_lightinfo, sin_numlightinfo, sizeof(sin_lightvalue_t), SIN_MAX_MAP_LIGHTINFO); - - Sin_AddLump (SIN_LUMP_LIGHTING, sin_dlightdata, sin_lightdatasize, 1, SIN_MAX_MAP_LIGHTING); - Sin_AddLump (SIN_LUMP_VISIBILITY, sin_dvisdata, sin_visdatasize, 1, SIN_MAX_MAP_VISIBILITY); - Sin_AddLump (SIN_LUMP_ENTITIES, sin_dentdata, sin_entdatasize, 1, SIN_MAX_MAP_ENTSTRING); - Sin_AddLump (SIN_LUMP_POP, sin_dpop, sizeof(sin_dpop), 1, sizeof(sin_dpop)); -#else - Sin_AddLump (SIN_LUMP_PLANES, sin_dplanes, sin_numplanes*sizeof(sin_dplane_t)); - Sin_AddLump (SIN_LUMP_LEAFS, sin_dleafs, sin_numleafs*sizeof(sin_dleaf_t)); - Sin_AddLump (SIN_LUMP_VERTEXES, sin_dvertexes, sin_numvertexes*sizeof(sin_dvertex_t)); - Sin_AddLump (SIN_LUMP_NODES, sin_dnodes, sin_numnodes*sizeof(sin_dnode_t)); - Sin_AddLump (SIN_LUMP_TEXINFO, sin_texinfo, sin_numtexinfo*sizeof(sin_texinfo_t)); - Sin_AddLump (SIN_LUMP_FACES, sin_dfaces, sin_numfaces*sizeof(sin_dface_t)); - Sin_AddLump (SIN_LUMP_BRUSHES, sin_dbrushes, sin_numbrushes*sizeof(sin_dbrush_t)); - Sin_AddLump (SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sin_numbrushsides*sizeof(sin_dbrushside_t)); - Sin_AddLump (SIN_LUMP_LEAFFACES, sin_dleaffaces, sin_numleaffaces*sizeof(sin_dleaffaces[0])); - Sin_AddLump (SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sin_numleafbrushes*sizeof(sin_dleafbrushes[0])); - Sin_AddLump (SIN_LUMP_SURFEDGES, sin_dsurfedges, sin_numsurfedges*sizeof(sin_dsurfedges[0])); - Sin_AddLump (SIN_LUMP_EDGES, sin_dedges, sin_numedges*sizeof(sin_dedge_t)); - Sin_AddLump (SIN_LUMP_MODELS, sin_dmodels, sin_nummodels*sizeof(sin_dmodel_t)); - Sin_AddLump (SIN_LUMP_AREAS, sin_dareas, sin_numareas*sizeof(sin_darea_t)); - Sin_AddLump (SIN_LUMP_AREAPORTALS, sin_dareaportals, sin_numareaportals*sizeof(sin_dareaportal_t)); - - Sin_AddLump (SIN_LUMP_LIGHTING, sin_dlightdata, sin_lightdatasize); - Sin_AddLump (SIN_LUMP_VISIBILITY, sin_dvisdata, sin_visdatasize); - Sin_AddLump (SIN_LUMP_ENTITIES, sin_dentdata, sin_entdatasize); - Sin_AddLump (SIN_LUMP_POP, sin_dpop, sizeof(sin_dpop)); -#endif - - fseek (wadfile, 0, SEEK_SET); - SafeWrite (wadfile, header, sizeof(sin_dheader_t)); - fclose (wadfile); -} - -//============================================================================ - - -//============================================ - -/* -================ -ParseEntities - -Parses the sin_dentdata string into entities -================ -*/ -void Sin_ParseEntities (void) -{ - script_t *script; - - num_entities = 0; - script = LoadScriptMemory(sin_dentdata, sin_entdatasize, "*sin bsp file"); - SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES | - SCFL_NOSTRINGESCAPECHARS); - - while(ParseEntity(script)) - { - } //end while - - FreeScript(script); -} //end of the function Sin_ParseEntities - - -/* -================ -UnparseEntities - -Generates the sin_dentdata string from all the entities -================ -*/ -void Sin_UnparseEntities (void) -{ - char *buf, *end; - epair_t *ep; - char line[2048]; - int i; - char key[1024], value[1024]; - - buf = sin_dentdata; - end = buf; - *end = 0; - - for (i=0 ; inext) - { - strcpy (key, ep->key); - StripTrailing (key); - strcpy (value, ep->value); - StripTrailing (value); - - sprintf (line, "\"%s\" \"%s\"\n", key, value); - strcat (end, line); - end += strlen(line); - } - strcat (end,"}\n"); - end += 2; - - if (end > buf + SIN_MAX_MAP_ENTSTRING) - Error ("Entity text too long"); - } - sin_entdatasize = end - buf + 1; -} //end of the function Sin_UnparseEntities - -#ifdef SIN -void FreeValueKeys(entity_t *ent) -{ - epair_t *ep,*next; - - for (ep=ent->epairs ; ep ; ep=next) - { - next = ep->next; - FreeMemory(ep->value); - FreeMemory(ep->key); - FreeMemory(ep); - } - ent->epairs = NULL; -} -#endif - -/* -============= -Sin_PrintBSPFileSizes - -Dumps info about current file -============= -*/ -void Sin_PrintBSPFileSizes (void) -{ - if (!num_entities) - Sin_ParseEntities (); - - Log_Print("%6i models %7i\n" - ,sin_nummodels, (int)(sin_nummodels*sizeof(sin_dmodel_t))); - Log_Print("%6i brushes %7i\n" - ,sin_numbrushes, (int)(sin_numbrushes*sizeof(sin_dbrush_t))); - Log_Print("%6i brushsides %7i\n" - ,sin_numbrushsides, (int)(sin_numbrushsides*sizeof(sin_dbrushside_t))); - Log_Print("%6i planes %7i\n" - ,sin_numplanes, (int)(sin_numplanes*sizeof(sin_dplane_t))); - Log_Print("%6i texinfo %7i\n" - ,sin_numtexinfo, (int)(sin_numtexinfo*sizeof(sin_texinfo_t))); -#ifdef SIN - Log_Print("%6i lightinfo %7i\n" - ,sin_numlightinfo, (int)(sin_numlightinfo*sizeof(sin_lightvalue_t))); -#endif - Log_Print("%6i entdata %7i\n", num_entities, sin_entdatasize); - - Log_Print("\n"); - - Log_Print("%6i vertexes %7i\n" - ,sin_numvertexes, (int)(sin_numvertexes*sizeof(sin_dvertex_t))); - Log_Print("%6i nodes %7i\n" - ,sin_numnodes, (int)(sin_numnodes*sizeof(sin_dnode_t))); - Log_Print("%6i faces %7i\n" - ,sin_numfaces, (int)(sin_numfaces*sizeof(sin_dface_t))); - Log_Print("%6i leafs %7i\n" - ,sin_numleafs, (int)(sin_numleafs*sizeof(sin_dleaf_t))); - Log_Print("%6i leaffaces %7i\n" - ,sin_numleaffaces, (int)(sin_numleaffaces*sizeof(sin_dleaffaces[0]))); - Log_Print("%6i leafbrushes %7i\n" - ,sin_numleafbrushes, (int)(sin_numleafbrushes*sizeof(sin_dleafbrushes[0]))); - Log_Print("%6i surfedges %7i\n" - ,sin_numsurfedges, (int)(sin_numsurfedges*sizeof(sin_dsurfedges[0]))); - Log_Print("%6i edges %7i\n" - ,sin_numedges, (int)(sin_numedges*sizeof(sin_dedge_t))); - Log_Print(" lightdata %7i\n", sin_lightdatasize); - Log_Print(" visdata %7i\n", sin_visdatasize); -} diff --git a/tools/quake3/bspc/l_bsp_sin.h b/tools/quake3/bspc/l_bsp_sin.h deleted file mode 100644 index 862674b1..00000000 --- a/tools/quake3/bspc/l_bsp_sin.h +++ /dev/null @@ -1,106 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "sinfiles.h" - -#define SINGAME_BSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'R') //RBSP -#define SINGAME_BSPVERSION 1 - -#define SIN_BSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I') //IBSP -#define SIN_BSPVERSION 41 - - -extern int sin_nummodels; -extern sin_dmodel_t *sin_dmodels;//[MAX_MAP_MODELS]; - -extern int sin_visdatasize; -extern byte *sin_dvisdata;//[MAX_MAP_VISIBILITY]; -extern sin_dvis_t *sin_dvis;// = (dvis_t *)sin_sin_dvisdata; - -extern int sin_lightdatasize; -extern byte *sin_dlightdata;//[MAX_MAP_LIGHTING]; - -extern int sin_entdatasize; -extern char *sin_dentdata;//[MAX_MAP_ENTSTRING]; - -extern int sin_numleafs; -extern sin_dleaf_t *sin_dleafs;//[MAX_MAP_LEAFS]; - -extern int sin_numplanes; -extern sin_dplane_t *sin_dplanes;//[MAX_MAP_PLANES]; - -extern int sin_numvertexes; -extern sin_dvertex_t *sin_dvertexes;//[MAX_MAP_VERTS]; - -extern int sin_numnodes; -extern sin_dnode_t *sin_dnodes;//[MAX_MAP_NODES]; - -extern int sin_numtexinfo; -extern sin_texinfo_t *sin_texinfo;//[MAX_MAP_sin_texinfo]; - -extern int sin_numfaces; -extern sin_dface_t *sin_dfaces;//[MAX_MAP_FACES]; - -extern int sin_numedges; -extern sin_dedge_t *sin_dedges;//[MAX_MAP_EDGES]; - -extern int sin_numleaffaces; -extern unsigned short *sin_dleaffaces;//[MAX_MAP_LEAFFACES]; - -extern int sin_numleafbrushes; -extern unsigned short *sin_dleafbrushes;//[MAX_MAP_LEAFBRUSHES]; - -extern int sin_numsurfedges; -extern int *sin_dsurfedges;//[MAX_MAP_SURFEDGES]; - -extern int sin_numbrushes; -extern sin_dbrush_t *sin_dbrushes;//[MAX_MAP_BRUSHES]; - -extern int sin_numbrushsides; -extern sin_dbrushside_t *sin_dbrushsides;//[MAX_MAP_BRUSHSIDES]; - -extern int sin_numareas; -extern sin_darea_t *sin_dareas;//[MAX_MAP_AREAS]; - -extern int sin_numareaportals; -extern sin_dareaportal_t *sin_dareaportals;//[MAX_MAP_AREAPORTALS]; - -extern int sin_numlightinfo; -extern sin_lightvalue_t *sin_lightinfo;//[MAX_MAP_LIGHTINFO]; - -extern byte sin_dpop[256]; - -extern char sin_dbrushsidetextured[SIN_MAX_MAP_BRUSHSIDES]; - -void Sin_AllocMaxBSP(void); -void Sin_FreeMaxBSP(void); - -void Sin_DecompressVis(byte *in, byte *decompressed); -int Sin_CompressVis(byte *vis, byte *dest); - -void Sin_LoadBSPFile (char *filename, int offset, int length); -void Sin_LoadBSPFileTexinfo (char *filename); // just for qdata -void Sin_WriteBSPFile (char *filename); -void Sin_PrintBSPFileSizes (void); -void Sin_ParseEntities(void); -void Sin_UnparseEntities(void); - diff --git a/tools/quake3/bspc/l_cmd.c b/tools/quake3/bspc/l_cmd.c deleted file mode 100644 index 3a8ab63c..00000000 --- a/tools/quake3/bspc/l_cmd.c +++ /dev/null @@ -1,1234 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -// cmdlib.c - -#include "l_cmd.h" -#include "l_log.h" -#include "l_mem.h" -#include -#include -#include - -#ifndef SIN -#define SIN -#endif //SIN - -#if defined(WIN32) || defined(_WIN32) -#include -#else -#include -#endif - -#ifdef NeXT -#include -#endif - -#define BASEDIRNAME "quake2" -#define PATHSEPERATOR '/' - -// set these before calling CheckParm -int myargc; -char **myargv; - -char com_token[1024]; -qboolean com_eof; - -qboolean archive; -char archivedir[1024]; - - -/* -=================== -ExpandWildcards - -Mimic unix command line expansion -=================== -*/ -#define MAX_EX_ARGC 1024 -int ex_argc; -char *ex_argv[MAX_EX_ARGC]; -#ifdef _WIN32 -#include "io.h" -void ExpandWildcards (int *argc, char ***argv) -{ - struct _finddata_t fileinfo; - int handle; - int i; - char filename[1024]; - char filebase[1024]; - char *path; - - ex_argc = 0; - for (i=0 ; i<*argc ; i++) - { - path = (*argv)[i]; - if ( path[0] == '-' - || ( !strstr(path, "*") && !strstr(path, "?") ) ) - { - ex_argv[ex_argc++] = path; - continue; - } - - handle = _findfirst (path, &fileinfo); - if (handle == -1) - return; - - ExtractFilePath (path, filebase); - - do - { - sprintf (filename, "%s%s", filebase, fileinfo.name); - ex_argv[ex_argc++] = copystring (filename); - } while (_findnext( handle, &fileinfo ) != -1); - - _findclose (handle); - } - - *argc = ex_argc; - *argv = ex_argv; -} -#else -void ExpandWildcards (int *argc, char ***argv) -{ -} -#endif - -#ifdef WINBSPC - -#include - -HWND program_hwnd; - -void SetProgramHandle(HWND hwnd) -{ - program_hwnd = hwnd; -} //end of the function SetProgramHandle - -/* -================= -Error - -For abnormal program terminations in windowed apps -================= -*/ -void Error (char *error, ...) -{ - va_list argptr; - char text[1024]; - char text2[1024]; - int err; - - err = GetLastError (); - - va_start(argptr, error); - vsprintf(text, error, argptr); - va_end(argptr); - - sprintf(text2, "%s\nGetLastError() = %i", text, err); - MessageBox(program_hwnd, text2, "Error", 0 /* MB_OK */ ); - - Log_Write(text); - Log_Close(); - - exit(1); -} //end of the function Error - -void Warning(char *szFormat, ...) -{ - char szBuffer[256]; - va_list argptr; - - va_start (argptr, szFormat); - vsprintf(szBuffer, szFormat, argptr); - va_end (argptr); - - MessageBox(program_hwnd, szBuffer, "Warning", MB_OK); - - Log_Write(szBuffer); -} //end of the function Warning - - -#else -/* -================= -Error - -For abnormal program terminations in console apps -================= -*/ -void Error (char *error, ...) -{ - va_list argptr; - char text[1024]; - - va_start(argptr, error); - vsprintf(text, error, argptr); - va_end(argptr); - printf("ERROR: %s\n", text); - - Log_Write(text); - Log_Close(); - - exit (1); -} //end of the function Error - -void Warning(char *warning, ...) -{ - va_list argptr; - char text[1024]; - - va_start(argptr, warning); - vsprintf(text, warning, argptr); - va_end(argptr); - printf("WARNING: %s\n", text); - - Log_Write(text); -} //end of the function Warning - -#endif - -//only printf if in verbose mode -qboolean verbose = true; - -void qprintf(char *format, ...) -{ - va_list argptr; -#ifdef WINBSPC - char buf[2048]; -#endif //WINBSPC - - if (!verbose) - return; - - va_start(argptr,format); -#ifdef WINBSPC - vsprintf(buf, format, argptr); - WinBSPCPrint(buf); -#else - vprintf(format, argptr); -#endif //WINBSPC - va_end(argptr); -} //end of the function qprintf - -void Com_Error(int level, char *error, ...) -{ - va_list argptr; - char text[1024]; - - va_start(argptr, error); - vsprintf(text, error, argptr); - va_end(argptr); - Error(text); -} //end of the funcion Com_Error - -void Com_Printf( const char *fmt, ... ) -{ - va_list argptr; - char text[1024]; - - va_start(argptr, fmt); - vsprintf(text, fmt, argptr); - va_end(argptr); - Log_Print(text); -} //end of the funcion Com_Printf - -/* - -qdir will hold the path up to the quake directory, including the slash - - f:\quake\ - /raid/quake/ - -gamedir will hold qdir + the game directory (id1, id2, etc) - - */ - -char qdir[1024]; -char gamedir[1024]; - -void SetQdirFromPath (char *path) -{ - char temp[1024]; - char *c; - int len; - - if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) - { // path is partial - Q_getwd (temp, sizeof(temp)); - strcat (temp, path); - path = temp; - } - - // search for "quake2" in path - - len = strlen(BASEDIRNAME); - for (c=path+strlen(path)-1 ; c != path ; c--) - if (!Q_strncasecmp (c, BASEDIRNAME, len)) - { - strncpy (qdir, path, c+len+1-path); - qprintf ("qdir: %s\n", qdir); - c += len+1; - while (*c) - { - if (*c == '/' || *c == '\\') - { - strncpy (gamedir, path, c+1-path); - qprintf ("gamedir: %s\n", gamedir); - return; - } - c++; - } - Error ("No gamedir in %s", path); - return; - } - Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path); -} - -char *ExpandArg (char *path) -{ - static char full[1024]; - - if (path[0] != '/' && path[0] != '\\' && path[1] != ':') - { - Q_getwd (full, sizeof(full)); - strcat (full, path); - } - else - strcpy (full, path); - return full; -} - -char *ExpandPath (char *path) -{ - static char full[1024]; - if (!qdir[0]) - Error ("ExpandPath called without qdir set"); - if (path[0] == '/' || path[0] == '\\' || path[1] == ':') - return path; - sprintf (full, "%s%s", qdir, path); - return full; -} - -char *ExpandPathAndArchive (char *path) -{ - char *expanded; - char archivename[1024]; - - expanded = ExpandPath (path); - - if (archive) - { - sprintf (archivename, "%s/%s", archivedir, path); - QCopyFile (expanded, archivename); - } - return expanded; -} - - -char *copystring(char *s) -{ - char *b; - b = GetMemory(strlen(s)+1); - strcpy (b, s); - return b; -} - - - -/* -================ -I_FloatTime -================ -*/ -double I_FloatTime (void) -{ - time_t t; - - time (&t); - - return t; -#if 0 -// more precise, less portable - struct timeval tp; - struct timezone tzp; - static int secbase; - - gettimeofday(&tp, &tzp); - - if (!secbase) - { - secbase = tp.tv_sec; - return tp.tv_usec/1000000.0; - } - - return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; -#endif -} - -void Q_getwd (char *out, size_t size) -{ - assert(size >= 2); - if (NULL == getcwd(out, size - 2)) { - perror("getcwd"); - exit(1); - } -#if defined(WIN32) || defined(_WIN32) - strcat(out, "\\"); -#else - strcat(out, "/"); -#endif -} - - -void Q_mkdir (char *path) -{ -#ifdef WIN32 - if (_mkdir (path) != -1) - return; -#else - if (mkdir (path, 0777) != -1) - return; -#endif - if (errno != EEXIST) - Error ("mkdir %s: %s",path, strerror(errno)); -} - -/* -============ -FileTime - -returns -1 if not present -============ -*/ -int FileTime (char *path) -{ - struct stat buf; - - if (stat (path,&buf) == -1) - return -1; - - return buf.st_mtime; -} - - - -/* -============== -COM_Parse - -Parse a token out of a string -============== -*/ -char *COM_Parse (char *data) -{ - int c; - int len; - - len = 0; - com_token[0] = 0; - - if (!data) - return NULL; - -// skip whitespace -skipwhite: - while ( (c = *data) <= ' ') - { - if (c == 0) - { - com_eof = true; - return NULL; // end of file; - } - data++; - } - -// skip // comments - if (c=='/' && data[1] == '/') - { - while (*data && *data != '\n') - data++; - goto skipwhite; - } - - -// handle quoted strings specially - if (c == '\"') - { - data++; - do - { - c = *data++; - if (c=='\"') - { - com_token[len] = 0; - return data; - } - com_token[len] = c; - len++; - } while (1); - } - -// parse single characters - if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') - { - com_token[len] = c; - len++; - com_token[len] = 0; - return data+1; - } - -// parse a regular word - do - { - com_token[len] = c; - data++; - len++; - c = *data; - if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') - break; - } while (c>32); - - com_token[len] = 0; - return data; -} - - -int Q_strncasecmp (char *s1, char *s2, int n) -{ - int c1, c2; - - do - { - c1 = *s1++; - c2 = *s2++; - - if (!n--) - return 0; // strings are equal until end point - - if (c1 != c2) - { - if (c1 >= 'a' && c1 <= 'z') - c1 -= ('a' - 'A'); - if (c2 >= 'a' && c2 <= 'z') - c2 -= ('a' - 'A'); - if (c1 != c2) - return -1; // strings not equal - } - } while (c1); - - return 0; // strings are equal -} - -int Q_strcasecmp (char *s1, char *s2) -{ - return Q_strncasecmp (s1, s2, 99999); -} - -int Q_stricmp (char *s1, char *s2) -{ - return Q_strncasecmp (s1, s2, 99999); -} - -void Q_strncpyz( char *dest, const char *src, int destsize ) { - strncpy( dest, src, destsize-1 ); - dest[destsize-1] = 0; -} - -char *strupr (char *start) -{ - char *in; - in = start; - while (*in) - { - *in = toupper(*in); - in++; - } - return start; -} - -char *strlower (char *start) -{ - char *in; - in = start; - while (*in) - { - *in = tolower(*in); - in++; - } - return start; -} - - -/* -============================================================================= - - MISC FUNCTIONS - -============================================================================= -*/ - - -/* -================= -CheckParm - -Checks for the given parameter in the program's command line arguments -Returns the argument number (1 to argc-1) or 0 if not present -================= -*/ -int CheckParm (char *check) -{ - int i; - - for (i = 1;i 0 && path[length] != PATHSEPERATOR) - length--; - path[length] = 0; -} - -void StripExtension (char *path) -{ - int length; - - length = strlen(path)-1; - while (length > 0 && path[length] != '.') - { - length--; - if (path[length] == '/') - return; // no extension - } - if (length) - path[length] = 0; -} - - -/* -==================== -Extract file parts -==================== -*/ -// FIXME: should include the slash, otherwise -// backing to an empty path will be wrong when appending a slash -void ExtractFilePath (char *path, char *dest) -{ - char *src; - - src = path + strlen(path) - 1; - -// -// back up until a \ or the start -// - while (src != path && *(src-1) != '\\' && *(src-1) != '/') - src--; - - memcpy (dest, path, src-path); - dest[src-path] = 0; -} - -void ExtractFileBase (char *path, char *dest) -{ - char *src; - - src = path + strlen(path) - 1; - -// -// back up until a \ or the start -// - while (src != path && *(src-1) != '\\' && *(src-1) != '/') - src--; - - while (*src && *src != '.') - { - *dest++ = *src++; - } - *dest = 0; -} - -void ExtractFileExtension (char *path, char *dest) -{ - char *src; - - src = path + strlen(path) - 1; - -// -// back up until a . or the start -// - while (src != path && *(src-1) != '.') - src--; - if (src == path) - { - *dest = 0; // no extension - return; - } - - strcpy (dest,src); -} - - -/* -============== -ParseNum / ParseHex -============== -*/ -int ParseHex (char *hex) -{ - char *str; - int num; - - num = 0; - str = hex; - - while (*str) - { - num <<= 4; - if (*str >= '0' && *str <= '9') - num += *str-'0'; - else if (*str >= 'a' && *str <= 'f') - num += 10 + *str-'a'; - else if (*str >= 'A' && *str <= 'F') - num += 10 + *str-'A'; - else - Error ("Bad hex number: %s",hex); - str++; - } - - return num; -} - - -int ParseNum (char *str) -{ - if (str[0] == '$') - return ParseHex (str+1); - if (str[0] == '0' && str[1] == 'x') - return ParseHex (str+2); - return atol (str); -} - - - -/* -============================================================================ - - BYTE ORDER FUNCTIONS - -============================================================================ -*/ - -#ifdef _SGI_SOURCE -#define __BIG_ENDIAN__ -#endif - -#ifdef __BIG_ENDIAN__ - -short LittleShort (short l) -{ - byte b1,b2; - - b1 = l&255; - b2 = (l>>8)&255; - - return (b1<<8) + b2; -} - -short BigShort (short l) -{ - return l; -} - - -int LittleLong (int l) -{ - byte b1,b2,b3,b4; - - b1 = l&255; - b2 = (l>>8)&255; - b3 = (l>>16)&255; - b4 = (l>>24)&255; - - return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; -} - -int BigLong (int l) -{ - return l; -} - - -float LittleFloat (float l) -{ - union {byte b[4]; float f;} in, out; - - in.f = l; - out.b[0] = in.b[3]; - out.b[1] = in.b[2]; - out.b[2] = in.b[1]; - out.b[3] = in.b[0]; - - return out.f; -} - -float BigFloat (float l) -{ - return l; -} - -#ifdef SIN -unsigned short LittleUnsignedShort (unsigned short l) -{ - byte b1,b2; - - b1 = l&255; - b2 = (l>>8)&255; - - return (b1<<8) + b2; -} - -unsigned short BigUnsignedShort (unsigned short l) -{ - return l; -} - -unsigned LittleUnsigned (unsigned l) -{ - byte b1,b2,b3,b4; - - b1 = l&255; - b2 = (l>>8)&255; - b3 = (l>>16)&255; - b4 = (l>>24)&255; - - return ((unsigned)b1<<24) + ((unsigned)b2<<16) + ((unsigned)b3<<8) + b4; -} - -unsigned BigUnsigned (unsigned l) -{ - return l; -} -#endif - - -#else - - -short BigShort (short l) -{ - byte b1,b2; - - b1 = l&255; - b2 = (l>>8)&255; - - return (b1<<8) + b2; -} - -short LittleShort (short l) -{ - return l; -} - - -int BigLong (int l) -{ - byte b1,b2,b3,b4; - - b1 = l&255; - b2 = (l>>8)&255; - b3 = (l>>16)&255; - b4 = (l>>24)&255; - - return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; -} - -int LittleLong (int l) -{ - return l; -} - -float BigFloat (float l) -{ - union {byte b[4]; float f;} in, out; - - in.f = l; - out.b[0] = in.b[3]; - out.b[1] = in.b[2]; - out.b[2] = in.b[1]; - out.b[3] = in.b[0]; - - return out.f; -} - -float LittleFloat (float l) -{ - return l; -} - -#ifdef SIN -unsigned short BigUnsignedShort (unsigned short l) -{ - byte b1,b2; - - b1 = l&255; - b2 = (l>>8)&255; - - return (b1<<8) + b2; -} - -unsigned short LittleUnsignedShort (unsigned short l) -{ - return l; -} - - -unsigned BigUnsigned (unsigned l) -{ - byte b1,b2,b3,b4; - - b1 = l&255; - b2 = (l>>8)&255; - b3 = (l>>16)&255; - b4 = (l>>24)&255; - - return ((unsigned)b1<<24) + ((unsigned)b2<<16) + ((unsigned)b3<<8) + b4; -} - -unsigned LittleUnsigned (unsigned l) -{ - return l; -} -#endif - - -#endif - - -//======================================================= - - -// FIXME: byte swap? - -// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 -// and the initial and final xor values shown below... in other words, the -// CCITT standard CRC used by XMODEM - -#define CRC_INIT_VALUE 0xffff -#define CRC_XOR_VALUE 0x0000 - -static unsigned short crctable[256] = -{ - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 -}; - -void CRC_Init(unsigned short *crcvalue) -{ - *crcvalue = CRC_INIT_VALUE; -} - -void CRC_ProcessByte(unsigned short *crcvalue, byte data) -{ - *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; -} - -unsigned short CRC_Value(unsigned short crcvalue) -{ - return crcvalue ^ CRC_XOR_VALUE; -} -//============================================================================= - -/* -============ -CreatePath -============ -*/ -void CreatePath (char *path) -{ - char *ofs, c; - - if (path[1] == ':') - path += 2; - - for (ofs = path+1 ; *ofs ; ofs++) - { - c = *ofs; - if (c == '/' || c == '\\') - { // create the directory - *ofs = 0; - Q_mkdir (path); - *ofs = c; - } - } -} - - -/* -============ -QCopyFile - - Used to archive source files -============ -*/ -void QCopyFile (char *from, char *to) -{ - void *buffer; - int length; - - length = LoadFile (from, &buffer, 0, 0); - CreatePath (to); - SaveFile (to, buffer, length); - FreeMemory(buffer); -} - -void FS_FreeFile(void *buf) -{ - FreeMemory(buf); -} //end of the function FS_FreeFile - -int FS_ReadFileAndCache(const char *qpath, void **buffer) -{ - return LoadFile((char *) qpath, buffer, 0, 0); -} //end of the function FS_ReadFileAndCache - -int FS_FOpenFileRead( const char *filename, FILE **file, qboolean uniqueFILE ) -{ - *file = fopen(filename, "rb"); - return (*file != NULL); -} //end of the function FS_FOpenFileRead diff --git a/tools/quake3/bspc/l_cmd.h b/tools/quake3/bspc/l_cmd.h deleted file mode 100644 index 71235bfc..00000000 --- a/tools/quake3/bspc/l_cmd.h +++ /dev/null @@ -1,158 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// cmdlib.h - -#ifndef SIN -#define SIN -#endif //SIN - -#ifndef __CMDLIB__ -#define __CMDLIB__ - -#ifdef _WIN32 -#pragma warning(disable : 4244) // MIPS -#pragma warning(disable : 4136) // X86 -#pragma warning(disable : 4051) // ALPHA - -#pragma warning(disable : 4018) // signed/unsigned mismatch -#pragma warning(disable : 4305) // truncate from double to float -#endif - -#include -#include -#include -#include -#include -#include -#include - -#ifndef __BYTEBOOL__ -#define __BYTEBOOL__ -typedef enum {false, true} qboolean; -typedef unsigned char byte; -#endif - -// the dec offsetof macro doesnt work very well... -#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier) - - -// set these before calling CheckParm -extern int myargc; -extern char **myargv; - -char *strupr (char *in); -char *strlower (char *in); -int Q_strncasecmp (char *s1, char *s2, int n); -int Q_strcasecmp (char *s1, char *s2); -void Q_strncpyz( char *dest, const char *src, int destsize ); -void Q_getwd (char *out, size_t size); - -int Q_filelength (FILE *f); -int FileTime (char *path); - -void Q_mkdir (char *path); - -extern char qdir[1024]; -extern char gamedir[1024]; -void SetQdirFromPath (char *path); -char *ExpandArg (char *path); // from cmd line -char *ExpandPath (char *path); // from scripts -char *ExpandPathAndArchive (char *path); - - -double I_FloatTime (void); - -void Error(char *error, ...); -void Warning(char *warning, ...); - -int CheckParm (char *check); - -FILE *SafeOpenWrite (char *filename); -FILE *SafeOpenRead (char *filename); -void SafeRead (FILE *f, void *buffer, int count); -void SafeWrite (FILE *f, void *buffer, int count); - -int LoadFile (char *filename, void **bufferptr, int offset, int length); -int TryLoadFile (char *filename, void **bufferptr); -void SaveFile (char *filename, void *buffer, int count); -qboolean FileExists (char *filename); - -void DefaultExtension (char *path, char *extension); -void DefaultPath (char *path, char *basepath); -void StripFilename (char *path); -void StripExtension (char *path); - -void ExtractFilePath (char *path, char *dest); -void ExtractFileBase (char *path, char *dest); -void ExtractFileExtension (char *path, char *dest); - -int ParseNum (char *str); - -short BigShort (short l); -short LittleShort (short l); -int BigLong (int l); -int LittleLong (int l); -float BigFloat (float l); -float LittleFloat (float l); - -#ifdef SIN -unsigned short BigUnsignedShort (unsigned short l); -unsigned short LittleUnsignedShort (unsigned short l); -unsigned BigUnsigned (unsigned l); -unsigned LittleUnsigned (unsigned l); -#endif - - -char *COM_Parse (char *data); - -extern char com_token[1024]; -extern qboolean com_eof; - -char *copystring(char *s); - - -void CRC_Init(unsigned short *crcvalue); -void CRC_ProcessByte(unsigned short *crcvalue, byte data); -unsigned short CRC_Value(unsigned short crcvalue); - -void CreatePath (char *path); -void QCopyFile (char *from, char *to); - -extern qboolean archive; -extern char archivedir[1024]; - - -extern qboolean verbose; -void qprintf (char *format, ...); - -void ExpandWildcards (int *argc, char ***argv); - - -// for compression routines -typedef struct -{ - byte *data; - int count; -} cblock_t; - -#endif - diff --git a/tools/quake3/bspc/l_log.c b/tools/quake3/bspc/l_log.c deleted file mode 100644 index 6a69c2b6..00000000 --- a/tools/quake3/bspc/l_log.c +++ /dev/null @@ -1,215 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include -#include -#include - -#include "qbsp.h" - -#define MAX_LOGFILENAMESIZE 1024 - -typedef struct logfile_s -{ - char filename[MAX_LOGFILENAMESIZE]; - FILE *fp; - int numwrites; -} logfile_t; - -logfile_t logfile; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_Open(char *filename) -{ - if (!filename || !strlen(filename)) - { - printf("openlog \n"); - return; - } //end if - if (logfile.fp) - { - printf("log file %s is already opened\n", logfile.filename); - return; - } //end if - logfile.fp = fopen(filename, "wb"); - if (!logfile.fp) - { - printf("can't open the log file %s\n", filename); - return; - } //end if - strncpy(logfile.filename, filename, MAX_LOGFILENAMESIZE); - printf("Opened log %s\n", logfile.filename); -} //end of the function Log_Create -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_Close(void) -{ - if (!logfile.fp) - { - printf("no log file to close\n"); - return; - } //end if - if (fclose(logfile.fp)) - { - printf("can't close log file %s\n", logfile.filename); - return; - } //end if - logfile.fp = NULL; - printf("Closed log %s\n", logfile.filename); -} //end of the function Log_Close -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_Shutdown(void) -{ - if (logfile.fp) Log_Close(); -} //end of the function Log_Shutdown -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_UnifyEndOfLine(char *buf) -{ - int i; - - for (i = 0; buf[i]; i++) - { - if (buf[i] == '\n') - { - if (i <= 0 || buf[i-1] != '\r') - { - memmove(&buf[i+1], &buf[i], strlen(&buf[i])+1); - buf[i] = '\r'; - i++; - } //end if - } //end if - } //end for -} //end of the function Log_UnifyEndOfLine -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_Print(char *fmt, ...) -{ - va_list ap; - char buf[2048]; - - va_start(ap, fmt); - vsprintf(buf, fmt, ap); - va_end(ap); - - if (verbose) - { -#ifdef WINBSPC - WinBSPCPrint(buf); -#else - printf("%s", buf); -#endif //WINBSPS - } //end if - - if (logfile.fp) - { - Log_UnifyEndOfLine(buf); - fprintf(logfile.fp, "%s", buf); - fflush(logfile.fp); - } //end if -} //end of the function Log_Print -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_Write(char *fmt, ...) -{ - va_list ap; - char buf[2048]; - - if (!logfile.fp) return; - va_start(ap, fmt); - vsprintf(buf, fmt, ap); - va_end(ap); - Log_UnifyEndOfLine(buf); - fprintf(logfile.fp, "%s", buf); - fflush(logfile.fp); -} //end of the function Log_Write -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_WriteTimeStamped(char *fmt, ...) -{ - va_list ap; - - if (!logfile.fp) return; -/* fprintf(logfile.fp, "%d %02d:%02d:%02d:%02d ", - logfile.numwrites, - (int) (botlibglobals.time / 60 / 60), - (int) (botlibglobals.time / 60), - (int) (botlibglobals.time), - (int) ((int) (botlibglobals.time * 100)) - - ((int) botlibglobals.time) * 100);*/ - va_start(ap, fmt); - vfprintf(logfile.fp, fmt, ap); - va_end(ap); - logfile.numwrites++; - fflush(logfile.fp); -} //end of the function Log_Write -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -FILE *Log_FileStruct(void) -{ - return logfile.fp; -} //end of the function Log_FileStruct -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Log_Flush(void) -{ - if (logfile.fp) fflush(logfile.fp); -} //end of the function Log_Flush - diff --git a/tools/quake3/bspc/l_log.h b/tools/quake3/bspc/l_log.h deleted file mode 100644 index b9f1be46..00000000 --- a/tools/quake3/bspc/l_log.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -//open a log file -void Log_Open(char *filename); -//close the current log file -void Log_Close(void); -//close log file if present -void Log_Shutdown(void); -//print on stdout and write to the current opened log file -void Log_Print(char *fmt, ...); -//write to the current opened log file -void Log_Write(char *fmt, ...); -//write to the current opened log file with a time stamp -void Log_WriteTimeStamped(char *fmt, ...); -//returns the log file structure -FILE *Log_FileStruct(void); -//flush log file -void Log_Flush(void); - -#ifdef WINBSPC -void WinBSPCPrint(char *str); -#endif //WINBSPC diff --git a/tools/quake3/bspc/l_math.c b/tools/quake3/bspc/l_math.c deleted file mode 100644 index 8d26a05d..00000000 --- a/tools/quake3/bspc/l_math.c +++ /dev/null @@ -1,289 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// mathlib.c -- math primitives - -#include "l_cmd.h" -#include "l_math.h" - -vec3_t vec3_origin = {0,0,0}; - -void AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) -{ - float angle; - static float sr, sp, sy, cr, cp, cy; - // static to help MS compiler fp bugs - - angle = angles[YAW] * (M_PI*2 / 360); - sy = sin(angle); - cy = cos(angle); - angle = angles[PITCH] * (M_PI*2 / 360); - sp = sin(angle); - cp = cos(angle); - angle = angles[ROLL] * (M_PI*2 / 360); - sr = sin(angle); - cr = cos(angle); - - if (forward) - { - forward[0] = cp*cy; - forward[1] = cp*sy; - forward[2] = -sp; - } - if (right) - { - right[0] = (-1*sr*sp*cy+-1*cr*-sy); - right[1] = (-1*sr*sp*sy+-1*cr*cy); - right[2] = -1*sr*cp; - } - if (up) - { - up[0] = (cr*sp*cy+-sr*-sy); - up[1] = (cr*sp*sy+-sr*cy); - up[2] = cr*cp; - } -} - -/* -================= -RadiusFromBounds -================= -*/ -float RadiusFromBounds( const vec3_t mins, const vec3_t maxs ) { - int i; - vec3_t corner; - float a, b; - - for (i=0 ; i<3 ; i++) { - a = fabs( mins[i] ); - b = fabs( maxs[i] ); - corner[i] = a > b ? a : b; - } - - return VectorLength (corner); -} - -/* -================ -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]; -} - -void AxisClear( vec3_t axis[3] ) { - axis[0][0] = 1; - axis[0][1] = 0; - axis[0][2] = 0; - axis[1][0] = 0; - axis[1][1] = 1; - axis[1][2] = 0; - axis[2][0] = 0; - axis[2][1] = 0; - axis[2][2] = 1; -} - -float VectorLengthSquared(vec3_t v) { - return DotProduct(v, v); -} - -double VectorLength(vec3_t v) -{ - int i; - double length; - - length = 0; - for (i=0 ; i< 3 ; i++) - length += v[i]*v[i]; - length = sqrt (length); // FIXME - - return length; -} - -qboolean VectorCompare (vec3_t v1, vec3_t v2) -{ - int i; - - for (i=0 ; i<3 ; i++) - if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON) - return false; - - return true; -} - -vec_t Q_rint (vec_t in) -{ - return floor(in + 0.5); -} - -void CrossProduct (const vec3_t v1, const 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]; -} - -void _VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc) -{ - vc[0] = va[0] + scale*vb[0]; - vc[1] = va[1] + scale*vb[1]; - vc[2] = va[2] + scale*vb[2]; -} - -vec_t _DotProduct (vec3_t v1, vec3_t v2) -{ - return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; -} - -void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out) -{ - out[0] = va[0]-vb[0]; - out[1] = va[1]-vb[1]; - out[2] = va[2]-vb[2]; -} - -void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out) -{ - out[0] = va[0]+vb[0]; - out[1] = va[1]+vb[1]; - out[2] = va[2]+vb[2]; -} - -void _VectorCopy (vec3_t in, vec3_t out) -{ - out[0] = in[0]; - out[1] = in[1]; - out[2] = in[2]; -} - -void _VectorScale (vec3_t v, vec_t scale, vec3_t out) -{ - out[0] = v[0] * scale; - out[1] = v[1] * scale; - out[2] = v[2] * scale; -} - -vec_t VectorNormalize(vec3_t inout) -{ - vec_t length, ilength; - - length = sqrt (inout[0]*inout[0] + inout[1]*inout[1] + inout[2]*inout[2]); - if (length == 0) - { - VectorClear (inout); - return 0; - } - - ilength = 1.0/length; - inout[0] = inout[0]*ilength; - inout[1] = inout[1]*ilength; - inout[2] = inout[2]*ilength; - - return length; -} - -vec_t VectorNormalize2(const vec3_t in, vec3_t out) -{ - vec_t length, ilength; - - length = sqrt (in[0]*in[0] + in[1]*in[1] + in[2]*in[2]); - if (length == 0) - { - VectorClear (out); - return 0; - } - - ilength = 1.0/length; - out[0] = in[0]*ilength; - out[1] = in[1]*ilength; - out[2] = in[2]*ilength; - - return length; -} - -vec_t ColorNormalize (vec3_t in, vec3_t out) -{ - float max, scale; - - max = in[0]; - if (in[1] > max) - max = in[1]; - if (in[2] > max) - max = in[2]; - - if (max == 0) - return 0; - - scale = 1.0 / max; - - VectorScale (in, scale, out); - - return max; -} - - - -void VectorInverse (vec3_t v) -{ - v[0] = -v[0]; - v[1] = -v[1]; - v[2] = -v[2]; -} - -void ClearBounds(vec3_t mins, vec3_t maxs) -{ - mins[0] = mins[1] = mins[2] = 99999; - maxs[0] = maxs[1] = maxs[2] = -99999; -} - -void AddPointToBounds(const vec3_t v, vec3_t mins, vec3_t maxs) -{ - int i; - vec_t val; - - for (i=0 ; i<3 ; i++) - { - val = v[i]; - if (val < mins[i]) - mins[i] = val; - if (val > maxs[i]) - maxs[i] = val; - } -} diff --git a/tools/quake3/bspc/l_math.h b/tools/quake3/bspc/l_math.h deleted file mode 100644 index 4cd4a6c5..00000000 --- a/tools/quake3/bspc/l_math.h +++ /dev/null @@ -1,93 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#ifndef __MATHLIB__ -#define __MATHLIB__ - -// mathlib.h - -#include - -#ifdef DOUBLEVEC_T -typedef double vec_t; -#else -typedef float vec_t; -#endif -typedef vec_t vec3_t[3]; -typedef vec_t vec4_t[4]; - -#define SIDE_FRONT 0 -#define SIDE_ON 2 -#define SIDE_BACK 1 -#define SIDE_CROSS -2 - -#define PITCH 0 -#define YAW 1 -#define ROLL 2 - -#define Q_PI 3.14159265358979323846 - -#define DEG2RAD( a ) ( a * M_PI ) / 180.0F - -#ifndef M_PI -#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h -#endif - -extern vec3_t vec3_origin; - -#define EQUAL_EPSILON 0.001 - -qboolean VectorCompare (vec3_t v1, vec3_t v2); - -#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) -#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];} -#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];} -#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];} -#define Vector4Copy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];} -#define VectorScale(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s)) -#define VectorClear(x) {x[0] = x[1] = x[2] = 0;} -#define VectorNegate(x, y) {y[0]=-x[0];y[1]=-x[1];y[2]=-x[2];} -#define VectorMA(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s)) - -vec_t Q_rint (vec_t in); -vec_t _DotProduct (vec3_t v1, vec3_t v2); -void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out); -void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out); -void _VectorCopy (vec3_t in, vec3_t out); -void _VectorScale (vec3_t v, vec_t scale, vec3_t out); -void _VectorMA(vec3_t va, double scale, vec3_t vb, vec3_t vc); - -double VectorLength(vec3_t v); -void CrossProduct(const vec3_t v1, const vec3_t v2, vec3_t cross); -vec_t VectorNormalize(vec3_t inout); -vec_t ColorNormalize(vec3_t in, vec3_t out); -vec_t VectorNormalize2(const vec3_t v, vec3_t out); -void VectorInverse (vec3_t v); - -void ClearBounds (vec3_t mins, vec3_t maxs); -void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs); - -void AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); -void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]); -void RotatePoint(vec3_t point, float matrix[3][3]); -void CreateRotationMatrix(vec3_t angles, float matrix[3][3]); - -#endif diff --git a/tools/quake3/bspc/l_mem.c b/tools/quake3/bspc/l_mem.c deleted file mode 100644 index 2636b68a..00000000 --- a/tools/quake3/bspc/l_mem.c +++ /dev/null @@ -1,441 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "l_log.h" - -int allocedmemory; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintMemorySize(unsigned long size) -{ - unsigned long number1, number2, number3; - number1 = size >> 20; - number2 = (size & 0xFFFFF) >> 10; - number3 = (size & 0x3FF); - if (number1) Log_Print("%ld MB", number1); - if (number1 && number2) Log_Print(" and "); - if (number2) Log_Print("%ld KB", number2); - if (number2 && number3) Log_Print(" and "); - if (number3) Log_Print("%ld bytes", number3); -} //end of the function PrintFileSize - -#ifndef MEMDEBUG -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int MemorySize(void *ptr) -{ -#if defined(WIN32) || defined(_WIN32) - #ifdef __WATCOMC__ - //Intel 32 bits memory addressing, 16 bytes aligned - return (_msize(ptr) + 15) >> 4 << 4; - #else - return _msize(ptr); - #endif -#else - return 0; -#endif -} //end of the function MemorySize -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void *GetClearedMemory(int size) -{ - void *ptr; - - ptr = (void *) malloc(size); - if (!ptr) Error("out of memory"); - memset(ptr, 0, size); - allocedmemory += MemorySize(ptr); - return ptr; -} //end of the function GetClearedMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void *GetMemory(unsigned long size) -{ - void *ptr; - ptr = malloc(size); - if (!ptr) Error("out of memory"); - allocedmemory += MemorySize(ptr); - return ptr; -} //end of the function GetMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeMemory(void *ptr) -{ - allocedmemory -= MemorySize(ptr); - free(ptr); -} //end of the function FreeMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TotalAllocatedMemory(void) -{ - return allocedmemory; -} //end of the function TotalAllocatedMemory - -#else - -#define MEM_ID 0x12345678l - -int totalmemorysize; -int numblocks; - -typedef struct memoryblock_s -{ - unsigned long int id; - void *ptr; - int size; -#ifdef MEMDEBUG - char *label; - char *file; - int line; -#endif //MEMDEBUG - struct memoryblock_s *prev, *next; -} memoryblock_t; - -memoryblock_t *memory; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void LinkMemoryBlock(memoryblock_t *block) -{ - block->prev = NULL; - block->next = memory; - if (memory) memory->prev = block; - memory = block; -} //end of the function LinkMemoryBlock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void UnlinkMemoryBlock(memoryblock_t *block) -{ - if (block->prev) block->prev->next = block->next; - else memory = block->next; - if (block->next) block->next->prev = block->prev; -} //end of the function UnlinkMemoryBlock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef MEMDEBUG -void *GetMemoryDebug(unsigned long size, char *label, char *file, int line) -#else -void *GetMemory(unsigned long size) -#endif //MEMDEBUG -{ - void *ptr; - memoryblock_t *block; - - ptr = malloc(size + sizeof(memoryblock_t)); - block = (memoryblock_t *) ptr; - block->id = MEM_ID; - block->ptr = (char *) ptr + sizeof(memoryblock_t); - block->size = size + sizeof(memoryblock_t); -#ifdef MEMDEBUG - block->label = label; - block->file = file; - block->line = line; -#endif //MEMDEBUG - LinkMemoryBlock(block); - totalmemorysize += block->size; - numblocks++; - return block->ptr; -} //end of the function GetMemoryDebug -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef MEMDEBUG -void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line) -#else -void *GetClearedMemory(unsigned long size) -#endif //MEMDEBUG -{ - void *ptr; -#ifdef MEMDEBUG - ptr = GetMemoryDebug(size, label, file, line); -#else - ptr = GetMemory(size); -#endif //MEMDEBUG - memset(ptr, 0, size); - return ptr; -} //end of the function GetClearedMemoryLabelled -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void *GetClearedHunkMemory(unsigned long size) -{ - return GetClearedMemory(size); -} //end of the function GetClearedHunkMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void *GetHunkMemory(unsigned long size) -{ - return GetMemory(size); -} //end of the function GetHunkMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -memoryblock_t *BlockFromPointer(void *ptr, char *str) -{ - memoryblock_t *block; - - if (!ptr) - { -#ifdef MEMDEBUG - //char *crash = (char *) NULL; - //crash[0] = 1; - Error("%s: NULL pointer\n", str); -#endif - return NULL; - } //end if - block = (memoryblock_t *) ((char *) ptr - sizeof(memoryblock_t)); - if (block->id != MEM_ID) - { - Error("%s: invalid memory block\n", str); - } //end if - if (block->ptr != ptr) - { - - Error("%s: memory block pointer invalid\n", str); - } //end if - return block; -} //end of the function BlockFromPointer -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeMemory(void *ptr) -{ - memoryblock_t *block; - - block = BlockFromPointer(ptr, "FreeMemory"); - if (!block) return; - UnlinkMemoryBlock(block); - totalmemorysize -= block->size; - numblocks--; - // - free(block); -} //end of the function FreeMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int MemoryByteSize(void *ptr) -{ - memoryblock_t *block; - - block = BlockFromPointer(ptr, "MemoryByteSize"); - if (!block) return 0; - return block->size; -} //end of the function MemoryByteSize -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int MemorySize(void *ptr) -{ - return MemoryByteSize(ptr); -} //end of the function MemorySize -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintUsedMemorySize(void) -{ - printf("total botlib memory: %d KB\n", totalmemorysize >> 10); - printf("total memory blocks: %d\n", numblocks); -} //end of the function PrintUsedMemorySize -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintMemoryLabels(void) -{ - memoryblock_t *block; - int i; - - PrintUsedMemorySize(); - i = 0; - for (block = memory; block; block = block->next) - { -#ifdef MEMDEBUG - Log_Write("%6d, %p, %8d: %24s line %6d: %s", i, block->ptr, block->size, block->file, block->line, block->label); -#endif //MEMDEBUG - i++; - } //end for -} //end of the function PrintMemoryLabels -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void DumpMemory(void) -{ - memoryblock_t *block; - - for (block = memory; block; block = memory) - { - FreeMemory(block->ptr); - } //end for - totalmemorysize = 0; -} //end of the function DumpMemory -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TotalAllocatedMemory(void) -{ - return totalmemorysize; -} //end of the function TotalAllocatedMemory -#endif - -//=========================================================================== -// Q3 Hunk and Z_ memory management -//=========================================================================== - -typedef struct memhunk_s -{ - void *ptr; - struct memhunk_s *next; -} memhunk_t; - -memhunk_t *memhunk_high; -memhunk_t *memhunk_low; -int memhunk_high_size = 16 * 1024 * 1024; -int memhunk_low_size = 0; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Hunk_ClearHigh(void) -{ - memhunk_t *h, *nexth; - - for (h = memhunk_high; h; h = nexth) - { - nexth = h->next; - FreeMemory(h); - } //end for - memhunk_high = NULL; - memhunk_high_size = 16 * 1024 * 1024; -} //end of the function Hunk_ClearHigh -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void *Hunk_Alloc(int size) -{ - memhunk_t *h; - - if (!size) return (void *) memhunk_high_size; - // - h = GetClearedMemory(size + sizeof(memhunk_t)); - h->ptr = (char *) h + sizeof(memhunk_t); - h->next = memhunk_high; - memhunk_high = h; - memhunk_high_size -= size; - return h->ptr; -} //end of the function Hunk_Alloc -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void *Z_Malloc(int size) -{ - return GetClearedMemory(size); -} //end of the function Z_Malloc -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Z_Free (void *ptr) -{ - FreeMemory(ptr); -} //end of the function Z_Free diff --git a/tools/quake3/bspc/l_mem.h b/tools/quake3/bspc/l_mem.h deleted file mode 100644 index ba3b0d38..00000000 --- a/tools/quake3/bspc/l_mem.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - - -//============================================================================= - -// memory.h -//#define MEMDEBUG -#undef MEMDEBUG - -#ifndef MEMDEBUG - -void *GetClearedMemory(int size); -void *GetMemory(unsigned long size); - -#else - -#define GetMemory(size) GetMemoryDebug(size, #size, __FILE__, __LINE__); -#define GetClearedMemory(size) GetClearedMemoryDebug(size, #size, __FILE__, __LINE__); -//allocate a memory block of the given size -void *GetMemoryDebug(unsigned long size, char *label, char *file, int line); -//allocate a memory block of the given size and clear it -void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line); -// -void PrintMemoryLabels(void); -#endif //MEMDEBUG - -void FreeMemory(void *ptr); -int MemorySize(void *ptr); -void PrintMemorySize(unsigned long size); -int TotalAllocatedMemory(void); - diff --git a/tools/quake3/bspc/l_poly.c b/tools/quake3/bspc/l_poly.c deleted file mode 100644 index 39e89186..00000000 --- a/tools/quake3/bspc/l_poly.c +++ /dev/null @@ -1,1411 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include -#include "l_cmd.h" -#include "l_math.h" -#include "l_poly.h" -#include "l_log.h" -#include "l_mem.h" - -#define BOGUS_RANGE 65535 - -extern int numthreads; - -// counters are only bumped when running single threaded, -// because they are an awefull coherence problem -int c_active_windings; -int c_peak_windings; -int c_winding_allocs; -int c_winding_points; -int c_windingmemory; -int c_peak_windingmemory; - -char windingerror[1024]; - -void pw(winding_t *w) -{ - int i; - for (i=0 ; inumpoints ; i++) - printf ("(%5.3f, %5.3f, %5.3f)\n",w->p[i][0], w->p[i][1],w->p[i][2]); -} - - -void ResetWindings(void) -{ - c_active_windings = 0; - c_peak_windings = 0; - c_winding_allocs = 0; - c_winding_points = 0; - c_windingmemory = 0; - c_peak_windingmemory = 0; - - strcpy(windingerror, ""); -} //end of the function ResetWindings -/* -============= -AllocWinding -============= -*/ -winding_t *AllocWinding (int points) -{ - winding_t *w; - int s; - - s = sizeof(vec_t)*3*points + sizeof(int); - w = GetMemory(s); - memset(w, 0, s); - - if (numthreads == 1) - { - c_winding_allocs++; - c_winding_points += points; - c_active_windings++; - if (c_active_windings > c_peak_windings) - c_peak_windings = c_active_windings; - c_windingmemory += MemorySize(w); - if (c_windingmemory > c_peak_windingmemory) - c_peak_windingmemory = c_windingmemory; - } //end if - return w; -} //end of the function AllocWinding - -void FreeWinding (winding_t *w) -{ - if (*(unsigned *)w == 0xdeaddead) - Error ("FreeWinding: freed a freed winding"); - - if (numthreads == 1) - { - c_active_windings--; - c_windingmemory -= MemorySize(w); - } //end if - - *(unsigned *)w = 0xdeaddead; - - FreeMemory(w); -} //end of the function FreeWinding - -int WindingMemory(void) -{ - return c_windingmemory; -} //end of the function WindingMemory - -int WindingPeakMemory(void) -{ - return c_peak_windingmemory; -} //end of the function WindingPeakMemory - -int ActiveWindings(void) -{ - return c_active_windings; -} //end of the function ActiveWindings -/* -============ -RemoveColinearPoints -============ -*/ -int c_removed; - -void RemoveColinearPoints (winding_t *w) -{ - int i, j, k; - vec3_t v1, v2; - int nump; - vec3_t p[MAX_POINTS_ON_WINDING]; - - nump = 0; - for (i=0 ; inumpoints ; i++) - { - j = (i+1)%w->numpoints; - k = (i+w->numpoints-1)%w->numpoints; - VectorSubtract (w->p[j], w->p[i], v1); - VectorSubtract (w->p[i], w->p[k], v2); - VectorNormalize(v1); - VectorNormalize(v2); - if (DotProduct(v1, v2) < 0.999) - { - if (nump >= MAX_POINTS_ON_WINDING) - Error("RemoveColinearPoints: MAX_POINTS_ON_WINDING"); - VectorCopy (w->p[i], p[nump]); - nump++; - } - } - - if (nump == w->numpoints) - return; - - if (numthreads == 1) - c_removed += w->numpoints - nump; - w->numpoints = nump; - memcpy (w->p, p, nump*sizeof(p[0])); -} - -/* -============ -WindingPlane -============ -*/ -void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist) -{ - vec3_t v1, v2; - int i; - - //find two vectors each longer than 0.5 units - for (i = 0; i < w->numpoints; i++) - { - VectorSubtract(w->p[(i+1) % w->numpoints], w->p[i], v1); - VectorSubtract(w->p[(i+2) % w->numpoints], w->p[i], v2); - if (VectorLength(v1) > 0.5 && VectorLength(v2) > 0.5) break; - } //end for - CrossProduct(v2, v1, normal); - VectorNormalize(normal); - *dist = DotProduct(w->p[0], normal); -} //end of the function WindingPlane - -/* -============= -WindingArea -============= -*/ -vec_t WindingArea (winding_t *w) -{ - int i; - vec3_t d1, d2, cross; - vec_t total; - - total = 0; - for (i=2 ; inumpoints ; i++) - { - VectorSubtract (w->p[i-1], w->p[0], d1); - VectorSubtract (w->p[i], w->p[0], d2); - CrossProduct (d1, d2, cross); - total += 0.5 * VectorLength ( cross ); - } - return total; -} - -void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs) -{ - vec_t v; - int i,j; - - mins[0] = mins[1] = mins[2] = 99999; - maxs[0] = maxs[1] = maxs[2] = -99999; - - for (i=0 ; inumpoints ; i++) - { - for (j=0 ; j<3 ; j++) - { - v = w->p[i][j]; - if (v < mins[j]) - mins[j] = v; - if (v > maxs[j]) - maxs[j] = v; - } - } -} - -/* -============= -WindingCenter -============= -*/ -void WindingCenter (winding_t *w, vec3_t center) -{ - int i; - float scale; - - VectorCopy (vec3_origin, center); - for (i=0 ; inumpoints ; i++) - VectorAdd (w->p[i], center, center); - - scale = 1.0/w->numpoints; - VectorScale (center, scale, center); -} - -/* -================= -BaseWindingForPlane -================= -*/ -winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist) -{ - int i, x; - vec_t max, v; - vec3_t org, vright, vup; - winding_t *w; - -// find the major axis - - max = -BOGUS_RANGE; - x = -1; - for (i=0 ; i<3; i++) - { - v = fabs(normal[i]); - if (v > max) - { - x = i; - max = v; - } - } - if (x==-1) - Error ("BaseWindingForPlane: no axis found"); - - VectorCopy (vec3_origin, vup); - switch (x) - { - case 0: - case 1: - vup[2] = 1; - break; - case 2: - vup[0] = 1; - break; - } - - v = DotProduct (vup, normal); - VectorMA (vup, -v, normal, vup); - VectorNormalize (vup); - - VectorScale (normal, dist, org); - - CrossProduct (vup, normal, vright); - - VectorScale (vup, BOGUS_RANGE, vup); - VectorScale (vright, BOGUS_RANGE, vright); - -// project a really big axis aligned box onto the plane - w = AllocWinding (4); - - VectorSubtract (org, vright, w->p[0]); - VectorAdd (w->p[0], vup, w->p[0]); - - VectorAdd (org, vright, w->p[1]); - VectorAdd (w->p[1], vup, w->p[1]); - - VectorAdd (org, vright, w->p[2]); - VectorSubtract (w->p[2], vup, w->p[2]); - - VectorSubtract (org, vright, w->p[3]); - VectorSubtract (w->p[3], vup, w->p[3]); - - w->numpoints = 4; - - return w; -} - -/* -================== -CopyWinding -================== -*/ -winding_t *CopyWinding (winding_t *w) -{ - int size; - winding_t *c; - - c = AllocWinding (w->numpoints); - size = (int)((winding_t *)0)->p[w->numpoints]; - memcpy (c, w, size); - return c; -} - -/* -================== -ReverseWinding -================== -*/ -winding_t *ReverseWinding (winding_t *w) -{ - int i; - winding_t *c; - - c = AllocWinding (w->numpoints); - for (i=0 ; inumpoints ; i++) - { - VectorCopy (w->p[w->numpoints-1-i], c->p[i]); - } - c->numpoints = w->numpoints; - return c; -} - - -/* -============= -ClipWindingEpsilon -============= -*/ -void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, - vec_t epsilon, winding_t **front, winding_t **back) -{ - vec_t dists[MAX_POINTS_ON_WINDING+4]; - int sides[MAX_POINTS_ON_WINDING+4]; - int counts[3]; - //MrElusive: DOH can't use statics when unsing multithreading!!! - vec_t dot; // VC 4.2 optimizer bug if not static - int i, j; - vec_t *p1, *p2; - vec3_t mid; - winding_t *f, *b; - int maxpts; - - counts[0] = counts[1] = counts[2] = 0; - -// determine sides for each point - for (i=0 ; inumpoints ; i++) - { - dot = DotProduct (in->p[i], normal); - dot -= dist; - dists[i] = dot; - if (dot > epsilon) - sides[i] = SIDE_FRONT; - else if (dot < -epsilon) - sides[i] = SIDE_BACK; - else - { - sides[i] = SIDE_ON; - } - counts[sides[i]]++; - } - sides[i] = sides[0]; - dists[i] = dists[0]; - - *front = *back = NULL; - - if (!counts[0]) - { - *back = CopyWinding (in); - return; - } - if (!counts[1]) - { - *front = CopyWinding (in); - return; - } - - maxpts = in->numpoints+4; // cant use counts[0]+2 because - // of fp grouping errors - - *front = f = AllocWinding (maxpts); - *back = b = AllocWinding (maxpts); - - for (i=0 ; inumpoints ; i++) - { - p1 = in->p[i]; - - if (sides[i] == SIDE_ON) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - VectorCopy (p1, b->p[b->numpoints]); - b->numpoints++; - continue; - } - - if (sides[i] == SIDE_FRONT) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - } - if (sides[i] == SIDE_BACK) - { - VectorCopy (p1, b->p[b->numpoints]); - b->numpoints++; - } - - if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) - continue; - - // generate a split point - p2 = in->p[(i+1)%in->numpoints]; - - dot = dists[i] / (dists[i]-dists[i+1]); - for (j=0 ; j<3 ; j++) - { // avoid round off error when possible - if (normal[j] == 1) - mid[j] = dist; - else if (normal[j] == -1) - mid[j] = -dist; - else - mid[j] = p1[j] + dot*(p2[j]-p1[j]); - } - - VectorCopy (mid, f->p[f->numpoints]); - f->numpoints++; - VectorCopy (mid, b->p[b->numpoints]); - b->numpoints++; - } - - if (f->numpoints > maxpts || b->numpoints > maxpts) - Error ("ClipWinding: points exceeded estimate"); - if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING) - Error ("ClipWinding: MAX_POINTS_ON_WINDING"); -} - - -/* -============= -ChopWindingInPlace -============= -*/ -void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon) -{ - winding_t *in; - vec_t dists[MAX_POINTS_ON_WINDING+4]; - int sides[MAX_POINTS_ON_WINDING+4]; - int counts[3]; - //MrElusive: DOH can't use statics when unsing multithreading!!! - vec_t dot; // VC 4.2 optimizer bug if not static - int i, j; - vec_t *p1, *p2; - vec3_t mid; - winding_t *f; - int maxpts; - - in = *inout; - counts[0] = counts[1] = counts[2] = 0; - -// determine sides for each point - for (i=0 ; inumpoints ; i++) - { - dot = DotProduct (in->p[i], normal); - dot -= dist; - dists[i] = dot; - if (dot > epsilon) - sides[i] = SIDE_FRONT; - else if (dot < -epsilon) - sides[i] = SIDE_BACK; - else - { - sides[i] = SIDE_ON; - } - counts[sides[i]]++; - } - sides[i] = sides[0]; - dists[i] = dists[0]; - - if (!counts[0]) - { - FreeWinding (in); - *inout = NULL; - return; - } - if (!counts[1]) - return; // inout stays the same - - maxpts = in->numpoints+4; // cant use counts[0]+2 because - // of fp grouping errors - - f = AllocWinding (maxpts); - - for (i=0 ; inumpoints ; i++) - { - p1 = in->p[i]; - - if (sides[i] == SIDE_ON) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - continue; - } - - if (sides[i] == SIDE_FRONT) - { - VectorCopy (p1, f->p[f->numpoints]); - f->numpoints++; - } - - if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) - continue; - - // generate a split point - p2 = in->p[(i+1)%in->numpoints]; - - dot = dists[i] / (dists[i]-dists[i+1]); - for (j=0 ; j<3 ; j++) - { // avoid round off error when possible - if (normal[j] == 1) - mid[j] = dist; - else if (normal[j] == -1) - mid[j] = -dist; - else - mid[j] = p1[j] + dot*(p2[j]-p1[j]); - } - - VectorCopy (mid, f->p[f->numpoints]); - f->numpoints++; - } - - if (f->numpoints > maxpts) - Error ("ClipWinding: points exceeded estimate"); - if (f->numpoints > MAX_POINTS_ON_WINDING) - Error ("ClipWinding: MAX_POINTS_ON_WINDING"); - - FreeWinding (in); - *inout = f; -} - - -/* -================= -ChopWinding - -Returns the fragment of in that is on the front side -of the cliping plane. The original is freed. -================= -*/ -winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist) -{ - winding_t *f, *b; - - ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b); - FreeWinding (in); - if (b) - FreeWinding (b); - return f; -} - - -/* -================= -CheckWinding - -================= -*/ -void CheckWinding (winding_t *w) -{ - int i, j; - vec_t *p1, *p2; - vec_t d, edgedist; - vec3_t dir, edgenormal, facenormal; - vec_t area; - vec_t facedist; - - if (w->numpoints < 3) - Error ("CheckWinding: %i points",w->numpoints); - - area = WindingArea(w); - if (area < 1) - Error ("CheckWinding: %f area", area); - - WindingPlane (w, facenormal, &facedist); - - for (i=0 ; inumpoints ; i++) - { - p1 = w->p[i]; - - for (j=0 ; j<3 ; j++) - if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE) - Error ("CheckWinding: BUGUS_RANGE: %f",p1[j]); - - j = i+1 == w->numpoints ? 0 : i+1; - - // check the point is on the face plane - d = DotProduct (p1, facenormal) - facedist; - if (d < -ON_EPSILON || d > ON_EPSILON) - Error ("CheckWinding: point off plane"); - - // check the edge isnt degenerate - p2 = w->p[j]; - VectorSubtract (p2, p1, dir); - - if (VectorLength (dir) < ON_EPSILON) - Error ("CheckWinding: degenerate edge"); - - CrossProduct (facenormal, dir, edgenormal); - VectorNormalize (edgenormal); - edgedist = DotProduct (p1, edgenormal); - edgedist += ON_EPSILON; - - // all other points must be on front side - for (j=0 ; jnumpoints ; j++) - { - if (j == i) - continue; - d = DotProduct (w->p[j], edgenormal); - if (d > edgedist) - Error ("CheckWinding: non-convex"); - } - } -} - - -/* -============ -WindingOnPlaneSide -============ -*/ -int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist) -{ - qboolean front, back; - int i; - vec_t d; - - front = false; - back = false; - for (i=0 ; inumpoints ; i++) - { - d = DotProduct (w->p[i], normal) - dist; - if (d < -ON_EPSILON) - { - if (front) - return SIDE_CROSS; - back = true; - continue; - } - if (d > ON_EPSILON) - { - if (back) - return SIDE_CROSS; - front = true; - continue; - } - } - - if (back) - return SIDE_BACK; - if (front) - return SIDE_FRONT; - return SIDE_ON; -} - -//#ifdef ME - #define CONTINUOUS_EPSILON 0.005 -//#else -// #define CONTINUOUS_EPSILON 0.001 -//#endif - -/* -============= -TryMergeWinding - -If two polygons share a common edge and the edges that meet at the -common points are both inside the other polygons, merge them - -Returns NULL if the faces couldn't be merged, or the new face. -The originals will NOT be freed. -============= -*/ - -winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, vec3_t planenormal) -{ - vec_t *p1, *p2, *p3, *p4, *back; - winding_t *newf; - int i, j, k, l; - vec3_t normal, delta; - vec_t dot; - qboolean keep1, keep2; - - - // - // find a common edge - // - p1 = p2 = NULL; // stop compiler warning - j = 0; // - - for (i = 0; i < f1->numpoints; i++) - { - p1 = f1->p[i]; - p2 = f1->p[(i+1) % f1->numpoints]; - for (j = 0; j < f2->numpoints; j++) - { - p3 = f2->p[j]; - p4 = f2->p[(j+1) % f2->numpoints]; - for (k = 0; k < 3; k++) - { - if (fabs(p1[k] - p4[k]) > 0.1)//EQUAL_EPSILON) //ME - break; - if (fabs(p2[k] - p3[k]) > 0.1)//EQUAL_EPSILON) //ME - break; - } //end for - if (k==3) - break; - } //end for - if (j < f2->numpoints) - break; - } //end for - - if (i == f1->numpoints) - return NULL; // no matching edges - - // - // check slope of connected lines - // if the slopes are colinear, the point can be removed - // - back = f1->p[(i+f1->numpoints-1)%f1->numpoints]; - VectorSubtract (p1, back, delta); - CrossProduct (planenormal, delta, normal); - VectorNormalize (normal); - - back = f2->p[(j+2)%f2->numpoints]; - VectorSubtract (back, p1, delta); - dot = DotProduct (delta, normal); - if (dot > CONTINUOUS_EPSILON) - return NULL; // not a convex polygon - keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON); - - back = f1->p[(i+2)%f1->numpoints]; - VectorSubtract (back, p2, delta); - CrossProduct (planenormal, delta, normal); - VectorNormalize (normal); - - back = f2->p[(j+f2->numpoints-1)%f2->numpoints]; - VectorSubtract (back, p2, delta); - dot = DotProduct (delta, normal); - if (dot > CONTINUOUS_EPSILON) - return NULL; // not a convex polygon - keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON); - - // - // build the new polygon - // - newf = AllocWinding (f1->numpoints + f2->numpoints); - - // copy first polygon - for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints) - { - if (k==(i+1)%f1->numpoints && !keep2) - continue; - - VectorCopy (f1->p[k], newf->p[newf->numpoints]); - newf->numpoints++; - } - - // copy second polygon - for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints) - { - if (l==(j+1)%f2->numpoints && !keep1) - continue; - VectorCopy (f2->p[l], newf->p[newf->numpoints]); - newf->numpoints++; - } - - return newf; -} - -//#ifdef ME -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -winding_t *MergeWindings(winding_t *w1, winding_t *w2, vec3_t planenormal) -{ - winding_t *neww; - float dist; - int i, j, n, found, insertafter; - int sides[MAX_POINTS_ON_WINDING+4]; - vec3_t newp[MAX_POINTS_ON_WINDING+4]; - int numpoints; - vec3_t edgevec, sepnormal, v; - - RemoveEqualPoints(w1, 0.2); - numpoints = w1->numpoints; - memcpy(newp, w1->p, w1->numpoints * sizeof(vec3_t)); - // - for (i = 0; i < w2->numpoints; i++) - { - VectorCopy(w2->p[i], v); - for (j = 0; j < numpoints; j++) - { - VectorSubtract(newp[(j+1)%numpoints], - newp[(j)%numpoints], edgevec); - CrossProduct(edgevec, planenormal, sepnormal); - VectorNormalize(sepnormal); - if (VectorLength(sepnormal) < 0.9) - { - //remove the point from the new winding - for (n = j; n < numpoints-1; n++) - { - VectorCopy(newp[n+1], newp[n]); - sides[n] = sides[n+1]; - } //end for - numpoints--; - j--; - Log_Print("MergeWindings: degenerate edge on winding %f %f %f\n", sepnormal[0], - sepnormal[1], - sepnormal[2]); - continue; - } //end if - dist = DotProduct(newp[(j)%numpoints], sepnormal); - if (DotProduct(v, sepnormal) - dist < -0.1) sides[j] = SIDE_BACK; - else sides[j] = SIDE_FRONT; - } //end for - //remove all unnecesary points - for (j = 0; j < numpoints;) - { - if (sides[j] == SIDE_BACK - && sides[(j+1)%numpoints] == SIDE_BACK) - { - //remove the point from the new winding - for (n = (j+1)%numpoints; n < numpoints-1; n++) - { - VectorCopy(newp[n+1], newp[n]); - sides[n] = sides[n+1]; - } //end for - numpoints--; - } //end if - else - { - j++; - } //end else - } //end for - // - found = false; - for (j = 0; j < numpoints; j++) - { - if (sides[j] == SIDE_FRONT - && sides[(j+1)%numpoints] == SIDE_BACK) - { - if (found) Log_Print("Warning: MergeWindings: front to back found twice\n"); - found = true; - } //end if - } //end for - // - for (j = 0; j < numpoints; j++) - { - if (sides[j] == SIDE_FRONT - && sides[(j+1)%numpoints] == SIDE_BACK) - { - insertafter = (j+1)%numpoints; - //insert the new point after j+1 - for (n = numpoints-1; n > insertafter; n--) - { - VectorCopy(newp[n], newp[n+1]); - } //end for - numpoints++; - VectorCopy(v, newp[(insertafter+1)%numpoints]); - break; - } //end if - } //end for - } //end for - neww = AllocWinding(numpoints); - neww->numpoints = numpoints; - memcpy(neww->p, newp, numpoints * sizeof(vec3_t)); - RemoveColinearPoints(neww); - return neww; -} //end of the function MergeWindings -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *WindingErrorString(void) -{ - return windingerror; -} //end of the function WindingErrorString -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int WindingError(winding_t *w) -{ - int i, j; - vec_t *p1, *p2; - vec_t d, edgedist; - vec3_t dir, edgenormal, facenormal; - vec_t area; - vec_t facedist; - - if (w->numpoints < 3) - { - sprintf(windingerror, "winding %i points", w->numpoints); - return WE_NOTENOUGHPOINTS; - } //end if - - area = WindingArea(w); - if (area < 1) - { - sprintf(windingerror, "winding %f area", area); - return WE_SMALLAREA; - } //end if - - WindingPlane (w, facenormal, &facedist); - - for (i=0 ; inumpoints ; i++) - { - p1 = w->p[i]; - - for (j=0 ; j<3 ; j++) - { - if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE) - { - sprintf(windingerror, "winding point %d BUGUS_RANGE \'%f %f %f\'", j, p1[0], p1[1], p1[2]); - return WE_POINTBOGUSRANGE; - } //end if - } //end for - - j = i+1 == w->numpoints ? 0 : i+1; - - // check the point is on the face plane - d = DotProduct (p1, facenormal) - facedist; - if (d < -ON_EPSILON || d > ON_EPSILON) - { - sprintf(windingerror, "winding point %d off plane", i); - return WE_POINTOFFPLANE; - } //end if - - // check the edge isnt degenerate - p2 = w->p[j]; - VectorSubtract (p2, p1, dir); - - if (VectorLength (dir) < ON_EPSILON) - { - sprintf(windingerror, "winding degenerate edge %d-%d", i, j); - return WE_DEGENERATEEDGE; - } //end if - - CrossProduct (facenormal, dir, edgenormal); - VectorNormalize (edgenormal); - edgedist = DotProduct (p1, edgenormal); - edgedist += ON_EPSILON; - - // all other points must be on front side - for (j=0 ; jnumpoints ; j++) - { - if (j == i) - continue; - d = DotProduct (w->p[j], edgenormal); - if (d > edgedist) - { - sprintf(windingerror, "winding non-convex"); - return WE_NONCONVEX; - } //end if - } //end for - } //end for - return WE_NONE; -} //end of the function WindingError -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RemoveEqualPoints(winding_t *w, float epsilon) -{ - int i, nump; - vec3_t v; - vec3_t p[MAX_POINTS_ON_WINDING]; - - VectorCopy(w->p[0], p[0]); - nump = 1; - for (i = 1; i < w->numpoints; i++) - { - VectorSubtract(w->p[i], p[nump-1], v); - if (VectorLength(v) > epsilon) - { - if (nump >= MAX_POINTS_ON_WINDING) - Error("RemoveColinearPoints: MAX_POINTS_ON_WINDING"); - VectorCopy (w->p[i], p[nump]); - nump++; - } //end if - } //end for - - if (nump == w->numpoints) - return; - - w->numpoints = nump; - memcpy(w->p, p, nump * sizeof(p[0])); -} //end of the function RemoveEqualPoints -//=========================================================================== -// adds the given point to a winding at the given spot -// (for instance when spot is zero then the point is added at position zero) -// the original winding is NOT freed -// -// Parameter: - -// Returns: the new winding with the added point -// Changes Globals: - -//=========================================================================== -winding_t *AddWindingPoint(winding_t *w, vec3_t point, int spot) -{ - int i, j; - winding_t *neww; - - if (spot > w->numpoints) - { - Error("AddWindingPoint: num > w->numpoints"); - } //end if - if (spot < 0) - { - Error("AddWindingPoint: num < 0"); - } //end if - neww = AllocWinding(w->numpoints + 1); - neww->numpoints = w->numpoints + 1; - for (i = 0, j = 0; i < neww->numpoints; i++) - { - if (i == spot) - { - VectorCopy(point, neww->p[i]); - } //end if - else - { - VectorCopy(w->p[j], neww->p[i]); - j++; - } //end else - } //end for - return neww; -} //end of the function AddWindingPoint -//=========================================================================== -// the position where the new point should be added in the winding is -// stored in *spot -// -// Parameter: - -// Returns: true if the point is on the winding -// Changes Globals: - -//=========================================================================== -#define MELT_ON_EPSILON 0.2 - -int PointOnWinding(winding_t *w, vec3_t normal, float dist, vec3_t point, int *spot) -{ - int i, j; - vec3_t v1, v2; - vec3_t edgenormal, edgevec; - float edgedist, dot; - - *spot = 0; - //the point must be on the winding plane - dot = DotProduct(point, normal) - dist; - if (dot < -MELT_ON_EPSILON || dot > MELT_ON_EPSILON) return false; - // - for (i = 0; i < w->numpoints; i++) - { - j = (i+1) % w->numpoints; - //get a plane orthogonal to the winding plane through the edge - VectorSubtract(w->p[j], w->p[i], edgevec); - CrossProduct(normal, edgevec, edgenormal); - VectorNormalize(edgenormal); - edgedist = DotProduct(edgenormal, w->p[i]); - //point must be not too far from the plane - dot = DotProduct(point, edgenormal) - edgedist; - if (dot < -MELT_ON_EPSILON || dot > MELT_ON_EPSILON) continue; - //vector from first point of winding to the point to test - VectorSubtract(point, w->p[i], v1); - //vector from second point of winding to the point to test - VectorSubtract(point, w->p[j], v2); - //if the length of the vector is not larger than 0.5 units then - //the point is assumend to be the same as one of the winding points - if (VectorNormalize(v1) < 0.5) return false; - if (VectorNormalize(v2) < 0.5) return false; - //point must be between the two winding points - //(the two vectors must be directed towards each other, and on the - //same straight line) - if (DotProduct(v1, v2) < -0.99) - { - *spot = i + 1; - return true; - } //end if - } //end for - return false; -} //end of the function PointOnWinding -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int FindPlaneSeperatingWindings(winding_t *w1, winding_t *w2, vec3_t dir, - vec3_t normal, float *dist) -{ - int i, i2, j, j2, n; - int sides1[3], sides2[3]; - float dist1, dist2, dot, diff; - vec3_t normal1, normal2; - vec3_t v1, v2; - - for (i = 0; i < w1->numpoints; i++) - { - i2 = (i+1) % w1->numpoints; - // - VectorSubtract(w1->p[i2], w1->p[i], v1); - if (VectorLength(v1) < 0.1) - { - //Log_Write("FindPlaneSeperatingWindings: winding1 with degenerate edge\r\n"); - continue; - } //end if - CrossProduct(v1, dir, normal1); - VectorNormalize(normal1); - dist1 = DotProduct(normal1, w1->p[i]); - // - for (j = 0; j < w2->numpoints; j++) - { - j2 = (j+1) % w2->numpoints; - // - VectorSubtract(w2->p[j2], w2->p[j], v2); - if (VectorLength(v2) < 0.1) - { - //Log_Write("FindPlaneSeperatingWindings: winding2 with degenerate edge\r\n"); - continue; - } //end if - CrossProduct(v2, dir, normal2); - VectorNormalize(normal2); - dist2 = DotProduct(normal2, w2->p[j]); - // - diff = dist1 - dist2; - if (diff < -0.1 || diff > 0.1) - { - dist2 = -dist2; - VectorNegate(normal2, normal2); - diff = dist1 - dist2; - if (diff < -0.1 || diff > 0.1) continue; - } //end if - //check if the normal vectors are equal - for (n = 0; n < 3; n++) - { - diff = normal1[n] - normal2[n]; - if (diff < -0.0001 || diff > 0.0001) break; - } //end for - if (n != 3) continue; - //check on which side of the seperating plane the points of - //the first winding are - sides1[0] = sides1[1] = sides1[2] = 0; - for (n = 0; n < w1->numpoints; n++) - { - dot = DotProduct(w1->p[n], normal1) - dist1; - if (dot > 0.1) sides1[0]++; - else if (dot < -0.1) sides1[1]++; - else sides1[2]++; - } //end for - //check on which side of the seperating plane the points of - //the second winding are - sides2[0] = sides2[1] = sides2[2] = 0; - for (n = 0; n < w2->numpoints; n++) - { - //used normal1 and dist1 (they are equal to normal2 and dist2) - dot = DotProduct(w2->p[n], normal1) - dist1; - if (dot > 0.1) sides2[0]++; - else if (dot < -0.1) sides2[1]++; - else sides2[2]++; - } //end for - //if the first winding has points at both sides - if (sides1[0] && sides1[1]) - { - Log_Write("FindPlaneSeperatingWindings: winding1 non-convex\r\n"); - continue; - } //end if - //if the second winding has points at both sides - if (sides2[0] && sides2[1]) - { - Log_Write("FindPlaneSeperatingWindings: winding2 non-convex\r\n"); - continue; - } //end if - // - if ((!sides1[0] && !sides1[1]) || (!sides2[0] && !sides2[1])) - { - //don't use one of the winding planes as the seperating plane - continue; - } //end if - //the windings must be at different sides of the seperating plane - if ((!sides1[0] && !sides2[1]) || (!sides1[1] && !sides2[0])) - { - VectorCopy(normal1, normal); - *dist = dist1; - return true; - } //end if - } //end for - } //end for - return false; -} //end of the function FindPlaneSeperatingWindings -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#define WCONVEX_EPSILON 0.2 - -int WindingsNonConvex(winding_t *w1, winding_t *w2, - vec3_t normal1, vec3_t normal2, - float dist1, float dist2) -{ - int i; - - if (!w1 || !w2) return false; - - //check if one of the points of face1 is at the back of the plane of face2 - for (i = 0; i < w1->numpoints; i++) - { - if (DotProduct(normal2, w1->p[i]) - dist2 > WCONVEX_EPSILON) return true; - } //end for - //check if one of the points of face2 is at the back of the plane of face1 - for (i = 0; i < w2->numpoints; i++) - { - if (DotProduct(normal1, w2->p[i]) - dist1 > WCONVEX_EPSILON) return true; - } //end for - - return false; -} //end of the function WindingsNonConvex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -/* -#define VERTEX_EPSILON 0.5 - -qboolean EqualVertexes(vec3_t v1, vec3_t v2) -{ - float diff; - - diff = v1[0] - v2[0]; - if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON) - { - diff = v1[1] - v2[1]; - if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON) - { - diff = v1[2] - v2[2]; - if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON) - { - return true; - } //end if - } //end if - } //end if - return false; -} //end of the function EqualVertexes - -#define CONTINUOUS_EPSILON 0.001 - -winding_t *AAS_MergeWindings(winding_t *w1, winding_t *w2, vec3_t windingnormal) -{ - int n, i, k; - vec3_t normal, delta; - winding_t *winding, *neww; - float dist, dot; - int p1, p2; - int points[2][64]; - int numpoints[2] = {0, 0}; - int newnumpoints; - int keep[2]; - - if (!FindPlaneSeperatingWindings(w1, w2, windingnormal, normal, &dist)) return NULL; - - //for both windings - for (n = 0; n < 2; n++) - { - if (n == 0) winding = w1; - else winding = w2; - //get the points of the winding which are on the seperating plane - for (i = 0; i < winding->numpoints; i++) - { - dot = DotProduct(winding->p[i], normal) - dist; - if (dot > -ON_EPSILON && dot < ON_EPSILON) - { - //don't allow more than 64 points on the seperating plane - if (numpoints[n] >= 64) Error("AAS_MergeWindings: more than 64 points on seperating plane\n"); - points[n][numpoints[n]++] = i; - } //end if - } //end for - //there must be at least two points of each winding on the seperating plane - if (numpoints[n] < 2) return NULL; - } //end for - - //if the first point of winding1 (which is on the seperating plane) is unequal - //to the last point of winding2 (which is on the seperating plane) - if (!EqualVertexes(w1->p[points[0][0]], w2->p[points[1][numpoints[1]-1]])) - { - return NULL; - } //end if - //if the last point of winding1 (which is on the seperating plane) is unequal - //to the first point of winding2 (which is on the seperating plane) - if (!EqualVertexes(w1->p[points[0][numpoints[0]-1]], w2->p[points[1][0]])) - { - return NULL; - } //end if - // - // check slope of connected lines - // if the slopes are colinear, the point can be removed - // - //first point of winding1 which is on the seperating plane - p1 = points[0][0]; - //point before p1 - p2 = (p1 + w1->numpoints - 1) % w1->numpoints; - VectorSubtract(w1->p[p1], w1->p[p2], delta); - CrossProduct(windingnormal, delta, normal); - VectorNormalize(normal, normal); - - //last point of winding2 which is on the seperating plane - p1 = points[1][numpoints[1]-1]; - //point after p1 - p2 = (p1 + 1) % w2->numpoints; - VectorSubtract(w2->p[p2], w2->p[p1], delta); - dot = DotProduct(delta, normal); - if (dot > CONTINUOUS_EPSILON) return NULL; //merging would create a non-convex polygon - keep[0] = (qboolean)(dot < -CONTINUOUS_EPSILON); - - //first point of winding2 which is on the seperating plane - p1 = points[1][0]; - //point before p1 - p2 = (p1 + w2->numpoints - 1) % w2->numpoints; - VectorSubtract(w2->p[p1], w2->p[p2], delta); - CrossProduct(windingnormal, delta, normal); - VectorNormalize(normal, normal); - - //last point of winding1 which is on the seperating plane - p1 = points[0][numpoints[0]-1]; - //point after p1 - p2 = (p1 + 1) % w1->numpoints; - VectorSubtract(w1->p[p2], w1->p[p1], delta); - dot = DotProduct(delta, normal); - if (dot > CONTINUOUS_EPSILON) return NULL; //merging would create a non-convex polygon - keep[1] = (qboolean)(dot < -CONTINUOUS_EPSILON); - - //number of points on the new winding - newnumpoints = w1->numpoints - numpoints[0] + w2->numpoints - numpoints[1] + 2; - //allocate the winding - neww = AllocWinding(newnumpoints); - neww->numpoints = newnumpoints; - //copy all the points - k = 0; - //for both windings - for (n = 0; n < 2; n++) - { - if (n == 0) winding = w1; - else winding = w2; - //copy the points of the winding starting with the last point on the - //seperating plane and ending before the first point on the seperating plane - for (i = points[n][numpoints[n]-1]; i != points[n][0]; i = (i+1)%winding->numpoints) - { - if (k >= newnumpoints) - { - Log_Print("numpoints[0] = %d\n", numpoints[0]); - Log_Print("numpoints[1] = %d\n", numpoints[1]); - Error("AAS_MergeWindings: k = %d >= newnumpoints = %d\n", k, newnumpoints); - } //end if - VectorCopy(winding->p[i], neww->p[k]); - k++; - } //end for - } //end for - RemoveEqualPoints(neww); - if (!WindingIsOk(neww, 1)) - { - Log_Print("AAS_MergeWindings: winding not ok after merging\n"); - FreeWinding(neww); - return NULL; - } //end if - return neww; -} //end of the function AAS_MergeWindings*/ -//#endif //ME diff --git a/tools/quake3/bspc/l_poly.h b/tools/quake3/bspc/l_poly.h deleted file mode 100644 index f3191eb3..00000000 --- a/tools/quake3/bspc/l_poly.h +++ /dev/null @@ -1,120 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -//a winding gives the bounding points of a convex polygon -typedef struct -{ - int numpoints; - vec3_t p[4]; //variable sized -} winding_t; - -#define MAX_POINTS_ON_WINDING 96 - -//you can define on_epsilon in the makefile as tighter -#ifndef ON_EPSILON -#define ON_EPSILON 0.1 -#endif -//winding errors -#define WE_NONE 0 -#define WE_NOTENOUGHPOINTS 1 -#define WE_SMALLAREA 2 -#define WE_POINTBOGUSRANGE 3 -#define WE_POINTOFFPLANE 4 -#define WE_DEGENERATEEDGE 5 -#define WE_NONCONVEX 6 - -//allocates a winding -winding_t *AllocWinding (int points); -//returns the area of the winding -vec_t WindingArea (winding_t *w); -//gives the center of the winding -void WindingCenter (winding_t *w, vec3_t center); -//clips the given winding to the given plane and gives the front -//and back part of the clipped winding -void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, - vec_t epsilon, winding_t **front, winding_t **back); -//returns the fragment of the given winding that is on the front -//side of the cliping plane. The original is freed. -winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist); -//returns a copy of the given winding -winding_t *CopyWinding (winding_t *w); -//returns the reversed winding of the given one -winding_t *ReverseWinding (winding_t *w); -//returns a base winding for the given plane -winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist); -//checks the winding for errors -void CheckWinding (winding_t *w); -//returns the plane normal and dist the winding is in -void WindingPlane(winding_t *w, vec3_t normal, vec_t *dist); -//removes colinear points from the winding -void RemoveColinearPoints(winding_t *w); -//returns on which side of the plane the winding is situated -int WindingOnPlaneSide(winding_t *w, vec3_t normal, vec_t dist); -//frees the winding -void FreeWinding(winding_t *w); -//gets the bounds of the winding -void WindingBounds(winding_t *w, vec3_t mins, vec3_t maxs); -//chops the winding with the given plane, the original winding is freed if clipped -void ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon); -//prints the winding points on STDOUT -void pw(winding_t *w); -//try to merge the two windings which are in the given plane -//the original windings are undisturbed -//the merged winding is returned when merging was possible -//NULL is returned otherwise -winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, vec3_t planenormal); -//brute force winding merging... creates a convex winding out of -//the two whatsoever -winding_t *MergeWindings(winding_t *w1, winding_t *w2, vec3_t planenormal); - -//#ifdef ME -void ResetWindings(void); -//returns the amount of winding memory -int WindingMemory(void); -int WindingPeakMemory(void); -int ActiveWindings(void); -//returns the winding error string -char *WindingErrorString(void); -//returns one of the WE_ flags when the winding has errors -int WindingError(winding_t *w); -//removes equal points from the winding -void RemoveEqualPoints(winding_t *w, float epsilon); -//returns a winding with a point added at the given spot to the -//given winding, original winding is NOT freed -winding_t *AddWindingPoint(winding_t *w, vec3_t point, int spot); -//returns true if the point is on one of the winding 'edges' -//when the point is on one of the edged the number of the first -//point of the edge is stored in 'spot' -int PointOnWinding(winding_t *w, vec3_t normal, float dist, vec3_t point, int *spot); -//find a plane seperating the two windings -//true is returned when the windings area adjacent -//the seperating plane normal and distance area stored in 'normal' and 'dist' -//this plane will contain both the piece of common edge of the two windings -//and the vector 'dir' -int FindPlaneSeperatingWindings(winding_t *w1, winding_t *w2, vec3_t dir, - vec3_t normal, float *dist); -// -int WindingsNonConvex(winding_t *w1, winding_t *w2, - vec3_t normal1, vec3_t normal2, - float dist1, float dist2); -//#endif //ME - diff --git a/tools/quake3/bspc/l_qfiles.c b/tools/quake3/bspc/l_qfiles.c deleted file mode 100644 index e2e8d104..00000000 --- a/tools/quake3/bspc/l_qfiles.c +++ /dev/null @@ -1,663 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#if defined(WIN32)|defined(_WIN32) -#include -#include -#include -#else -#include -#include -#include -#endif - -#include "qbsp.h" - -//file extensions with their type -typedef struct qfile_exttype_s -{ - char *extension; - int type; -} qfile_exttyp_t; - -qfile_exttyp_t quakefiletypes[] = -{ - {QFILEEXT_UNKNOWN, QFILETYPE_UNKNOWN}, - {QFILEEXT_PAK, QFILETYPE_PAK}, - {QFILEEXT_PK3, QFILETYPE_PK3}, - {QFILEEXT_SIN, QFILETYPE_PAK}, - {QFILEEXT_BSP, QFILETYPE_BSP}, - {QFILEEXT_MAP, QFILETYPE_MAP}, - {QFILEEXT_MDL, QFILETYPE_MDL}, - {QFILEEXT_MD2, QFILETYPE_MD2}, - {QFILEEXT_MD3, QFILETYPE_MD3}, - {QFILEEXT_WAL, QFILETYPE_WAL}, - {QFILEEXT_WAV, QFILETYPE_WAV}, - {QFILEEXT_AAS, QFILETYPE_AAS}, - {NULL, 0} -}; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int QuakeFileExtensionType(char *extension) -{ - int i; - - for (i = 0; quakefiletypes[i].extension; i++) - { - if (!stricmp(extension, quakefiletypes[i].extension)) - { - return quakefiletypes[i].type; - } //end if - } //end for - return QFILETYPE_UNKNOWN; -} //end of the function QuakeFileExtensionType -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *QuakeFileTypeExtension(int type) -{ - int i; - - for (i = 0; quakefiletypes[i].extension; i++) - { - if (quakefiletypes[i].type == type) - { - return quakefiletypes[i].extension; - } //end if - } //end for - return QFILEEXT_UNKNOWN; -} //end of the function QuakeFileExtension -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int QuakeFileType(char *filename) -{ - char ext[_MAX_PATH] = "."; - - ExtractFileExtension(filename, ext+1); - return QuakeFileExtensionType(ext); -} //end of the function QuakeFileTypeFromFileName -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -char *StringContains(char *str1, char *str2, int casesensitive) -{ - int len, i, j; - - len = strlen(str1) - strlen(str2); - for (i = 0; i <= len; i++, str1++) - { - for (j = 0; str2[j]; j++) - { - if (casesensitive) - { - if (str1[j] != str2[j]) break; - } //end if - else - { - if (toupper(str1[j]) != toupper(str2[j])) break; - } //end else - } //end for - if (!str2[j]) return str1; - } //end for - return NULL; -} //end of the function StringContains -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int FileFilter(char *filter, char *filename, int casesensitive) -{ - char buf[1024]; - char *ptr; - int i, found; - - while(*filter) - { - if (*filter == '*') - { - filter++; - for (i = 0; *filter; i++) - { - if (*filter == '*' || *filter == '?') break; - buf[i] = *filter; - filter++; - } //end for - buf[i] = '\0'; - if (strlen(buf)) - { - ptr = StringContains(filename, buf, casesensitive); - if (!ptr) return false; - filename = ptr + strlen(buf); - } //end if - } //end if - else if (*filter == '?') - { - filter++; - filename++; - } //end else if - else if (*filter == '[' && *(filter+1) == '[') - { - filter++; - } //end if - else if (*filter == '[') - { - filter++; - found = false; - while(*filter && !found) - { - if (*filter == ']' && *(filter+1) != ']') break; - if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) - { - if (casesensitive) - { - if (*filename >= *filter && *filename <= *(filter+2)) found = true; - } //end if - else - { - if (toupper(*filename) >= toupper(*filter) && - toupper(*filename) <= toupper(*(filter+2))) found = true; - } //end else - filter += 3; - } //end if - else - { - if (casesensitive) - { - if (*filter == *filename) found = true; - } //end if - else - { - if (toupper(*filter) == toupper(*filename)) found = true; - } //end else - filter++; - } //end else - } //end while - if (!found) return false; - while(*filter) - { - if (*filter == ']' && *(filter+1) != ']') break; - filter++; - } //end while - filter++; - filename++; - } //end else if - else - { - if (casesensitive) - { - if (*filter != *filename) return false; - } //end if - else - { - if (toupper(*filter) != toupper(*filename)) return false; - } //end else - filter++; - filename++; - } //end else - } //end while - return true; -} //end of the function FileFilter -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -quakefile_t *FindQuakeFilesInZip(char *zipfile, char *filter) -{ - unzFile uf; - int err; - unz_global_info gi; - char filename_inzip[MAX_PATH]; - unz_file_info file_info; - int i; - quakefile_t *qfiles, *lastqf, *qf; - - uf = unzOpen(zipfile); - err = unzGetGlobalInfo(uf, &gi); - - if (err != UNZ_OK) return NULL; - - unzGoToFirstFile(uf); - - qfiles = NULL; - lastqf = NULL; - for (i = 0; i < gi.number_entry; i++) - { - err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL,0,NULL,0); - if (err != UNZ_OK) break; - - ConvertPath(filename_inzip); - if (FileFilter(filter, filename_inzip, false)) - { - qf = malloc(sizeof(quakefile_t)); - if (!qf) Error("out of memory"); - memset(qf, 0, sizeof(quakefile_t)); - strcpy(qf->pakfile, zipfile); - strcpy(qf->filename, zipfile); - strcpy(qf->origname, filename_inzip); - qf->zipfile = true; - //memcpy( &buildBuffer[i].zipfileinfo, (unz_s*)uf, sizeof(unz_s)); - memcpy(&qf->zipinfo, (unz_s*)uf, sizeof(unz_s)); - qf->offset = 0; - qf->length = file_info.uncompressed_size; - qf->type = QuakeFileType(filename_inzip); - //add the file ot the list - qf->next = NULL; - if (lastqf) lastqf->next = qf; - else qfiles = qf; - lastqf = qf; - } //end if - unzGoToNextFile(uf); - } //end for - - unzClose(uf); - - return qfiles; -} //end of the function FindQuakeFilesInZip -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -quakefile_t *FindQuakeFilesInPak(char *pakfile, char *filter) -{ - FILE *fp; - dpackheader_t packheader; - dsinpackfile_t *packfiles; - dpackfile_t *idpackfiles; - quakefile_t *qfiles, *lastqf, *qf; - int numpackdirs, i; - - qfiles = NULL; - lastqf = NULL; - //open the pak file - fp = fopen(pakfile, "rb"); - if (!fp) - { - Warning("can't open pak file %s", pakfile); - return NULL; - } //end if - //read pak header, check for valid pak id and seek to the dir entries - if ((fread(&packheader, 1, sizeof(dpackheader_t), fp) != sizeof(dpackheader_t)) - || (packheader.ident != IDPAKHEADER && packheader.ident != SINPAKHEADER) - || (fseek(fp, LittleLong(packheader.dirofs), SEEK_SET)) - ) - { - fclose(fp); - Warning("invalid pak file %s", pakfile); - return NULL; - } //end if - //if it is a pak file from id software - if (packheader.ident == IDPAKHEADER) - { - //number of dir entries in the pak file - numpackdirs = LittleLong(packheader.dirlen) / sizeof(dpackfile_t); - idpackfiles = (dpackfile_t *) malloc(numpackdirs * sizeof(dpackfile_t)); - if (!idpackfiles) Error("out of memory"); - //read the dir entry - if (fread(idpackfiles, sizeof(dpackfile_t), numpackdirs, fp) != numpackdirs) - { - fclose(fp); - free(idpackfiles); - Warning("can't read the Quake pak file dir entries from %s", pakfile); - return NULL; - } //end if - fclose(fp); - //convert to sin pack files - packfiles = (dsinpackfile_t *) malloc(numpackdirs * sizeof(dsinpackfile_t)); - if (!packfiles) Error("out of memory"); - for (i = 0; i < numpackdirs; i++) - { - strcpy(packfiles[i].name, idpackfiles[i].name); - packfiles[i].filepos = LittleLong(idpackfiles[i].filepos); - packfiles[i].filelen = LittleLong(idpackfiles[i].filelen); - } //end for - free(idpackfiles); - } //end if - else //its a Sin pack file - { - //number of dir entries in the pak file - numpackdirs = LittleLong(packheader.dirlen) / sizeof(dsinpackfile_t); - packfiles = (dsinpackfile_t *) malloc(numpackdirs * sizeof(dsinpackfile_t)); - if (!packfiles) Error("out of memory"); - //read the dir entry - if (fread(packfiles, sizeof(dsinpackfile_t), numpackdirs, fp) != numpackdirs) - { - fclose(fp); - free(packfiles); - Warning("can't read the Sin pak file dir entries from %s", pakfile); - return NULL; - } //end if - fclose(fp); - for (i = 0; i < numpackdirs; i++) - { - packfiles[i].filepos = LittleLong(packfiles[i].filepos); - packfiles[i].filelen = LittleLong(packfiles[i].filelen); - } //end for - } //end else - // - for (i = 0; i < numpackdirs; i++) - { - ConvertPath(packfiles[i].name); - if (FileFilter(filter, packfiles[i].name, false)) - { - qf = malloc(sizeof(quakefile_t)); - if (!qf) Error("out of memory"); - memset(qf, 0, sizeof(quakefile_t)); - strcpy(qf->pakfile, pakfile); - strcpy(qf->filename, pakfile); - strcpy(qf->origname, packfiles[i].name); - qf->zipfile = false; - qf->offset = packfiles[i].filepos; - qf->length = packfiles[i].filelen; - qf->type = QuakeFileType(packfiles[i].name); - //add the file ot the list - qf->next = NULL; - if (lastqf) lastqf->next = qf; - else qfiles = qf; - lastqf = qf; - } //end if - } //end for - free(packfiles); - return qfiles; -} //end of the function FindQuakeFilesInPak -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -quakefile_t *FindQuakeFilesWithPakFilter(char *pakfilter, char *filter) -{ -#if defined(WIN32)|defined(_WIN32) - WIN32_FIND_DATA filedata; - HWND handle; - struct _stat statbuf; -#else - glob_t globbuf; - struct stat statbuf; - int j; -#endif - quakefile_t *qfiles, *lastqf, *qf; - char pakfile[_MAX_PATH], filename[_MAX_PATH], *str; - int done; - - qfiles = NULL; - lastqf = NULL; - if (pakfilter && strlen(pakfilter)) - { -#if defined(WIN32)|defined(_WIN32) - handle = FindFirstFile(pakfilter, &filedata); - done = (handle == INVALID_HANDLE_VALUE); - while(!done) - { - _splitpath(pakfilter, pakfile, NULL, NULL, NULL); - _splitpath(pakfilter, NULL, &pakfile[strlen(pakfile)], NULL, NULL); - AppendPathSeperator(pakfile, _MAX_PATH); - strcat(pakfile, filedata.cFileName); - _stat(pakfile, &statbuf); -#else - glob(pakfilter, 0, NULL, &globbuf); - for (j = 0; j < globbuf.gl_pathc; j++) - { - strcpy(pakfile, globbuf.gl_pathv[j]); - stat(pakfile, &statbuf); -#endif - //if the file with .pak or .pk3 is a folder - if (statbuf.st_mode & S_IFDIR) - { - strcpy(filename, pakfilter); - AppendPathSeperator(filename, _MAX_PATH); - strcat(filename, filter); - qf = FindQuakeFilesWithPakFilter(NULL, filename); - if (lastqf) lastqf->next = qf; - else qfiles = qf; - lastqf = qf; - while(lastqf->next) lastqf = lastqf->next; - } //end if - else - { -#if defined(WIN32)|defined(_WIN32) - str = StringContains(pakfile, ".pk3", false); -#else - str = StringContains(pakfile, ".pk3", true); -#endif - if (str && str == pakfile + strlen(pakfile) - strlen(".pk3")) - { - qf = FindQuakeFilesInZip(pakfile, filter); - } //end if - else - { - qf = FindQuakeFilesInPak(pakfile, filter); - } //end else - // - if (qf) - { - if (lastqf) lastqf->next = qf; - else qfiles = qf; - lastqf = qf; - while(lastqf->next) lastqf = lastqf->next; - } //end if - } //end else - // -#if defined(WIN32)|defined(_WIN32) - //find the next file - done = !FindNextFile(handle, &filedata); - } //end while -#else - } //end for - globfree(&globbuf); -#endif - } //end if - else - { -#if defined(WIN32)|defined(_WIN32) - handle = FindFirstFile(filter, &filedata); - done = (handle == INVALID_HANDLE_VALUE); - while(!done) - { - _splitpath(filter, filename, NULL, NULL, NULL); - _splitpath(filter, NULL, &filename[strlen(filename)], NULL, NULL); - AppendPathSeperator(filename, _MAX_PATH); - strcat(filename, filedata.cFileName); -#else - glob(filter, 0, NULL, &globbuf); - for (j = 0; j < globbuf.gl_pathc; j++) - { - strcpy(filename, globbuf.gl_pathv[j]); -#endif - // - qf = malloc(sizeof(quakefile_t)); - if (!qf) Error("out of memory"); - memset(qf, 0, sizeof(quakefile_t)); - strcpy(qf->pakfile, ""); - strcpy(qf->filename, filename); - strcpy(qf->origname, filename); - qf->offset = 0; - qf->length = 0; - qf->type = QuakeFileType(filename); - //add the file ot the list - qf->next = NULL; - if (lastqf) lastqf->next = qf; - else qfiles = qf; - lastqf = qf; -#if defined(WIN32)|defined(_WIN32) - //find the next file - done = !FindNextFile(handle, &filedata); - } //end while -#else - } //end for - globfree(&globbuf); -#endif - } //end else - return qfiles; -} //end of the function FindQuakeFilesWithPakFilter -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -quakefile_t *FindQuakeFiles(char *filter) -{ - char *str; - char newfilter[_MAX_PATH]; - char pakfilter[_MAX_PATH]; - char filefilter[_MAX_PATH]; - - strcpy(newfilter, filter); - ConvertPath(newfilter); - strcpy(pakfilter, newfilter); - - str = StringContains(pakfilter, ".pak", false); - if (!str) str = StringContains(pakfilter, ".pk3", false); - - if (str) - { - str += strlen(".pak"); - if (*str) - { - *str++ = '\0'; - while(*str == '\\' || *str == '/') str++; - strcpy(filefilter, str); - return FindQuakeFilesWithPakFilter(pakfilter, filefilter); - } //end if - } //end else - return FindQuakeFilesWithPakFilter(NULL, newfilter); -} //end of the function FindQuakeFiles -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int LoadQuakeFile(quakefile_t *qf, void **bufferptr) -{ - FILE *fp; - void *buffer; - int length; - unzFile zf; - - if (qf->zipfile) - { - //open the zip file - zf = unzOpen(qf->pakfile); - //set the file pointer - qf->zipinfo.file = ((unz_s *) zf)->file; - //open the Quake file in the zip file - unzOpenCurrentFile(&qf->zipinfo); - //allocate memory for the buffer - length = qf->length; - buffer = GetMemory(length+1); - //read the Quake file from the zip file - length = unzReadCurrentFile(&qf->zipinfo, buffer, length); - //close the Quake file in the zip file - unzCloseCurrentFile(&qf->zipinfo); - //close the zip file - unzClose(zf); - - *bufferptr = buffer; - return length; - } //end if - else - { - fp = SafeOpenRead(qf->filename); - if (qf->offset) fseek(fp, qf->offset, SEEK_SET); - length = qf->length; - if (!length) length = Q_filelength(fp); - buffer = GetMemory(length+1); - ((char *)buffer)[length] = 0; - SafeRead(fp, buffer, length); - fclose(fp); - - *bufferptr = buffer; - return length; - } //end else -} //end of the function LoadQuakeFile -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int ReadQuakeFile(quakefile_t *qf, void *buffer, int offset, int length) -{ - FILE *fp; - int read; - unzFile zf; - char tmpbuf[1024]; - - if (qf->zipfile) - { - //open the zip file - zf = unzOpen(qf->pakfile); - //set the file pointer - qf->zipinfo.file = ((unz_s *) zf)->file; - //open the Quake file in the zip file - unzOpenCurrentFile(&qf->zipinfo); - // - while(offset > 0) - { - read = offset; - if (read > sizeof(tmpbuf)) read = sizeof(tmpbuf); - unzReadCurrentFile(&qf->zipinfo, tmpbuf, read); - offset -= read; - } //end while - //read the Quake file from the zip file - length = unzReadCurrentFile(&qf->zipinfo, buffer, length); - //close the Quake file in the zip file - unzCloseCurrentFile(&qf->zipinfo); - //close the zip file - unzClose(zf); - - return length; - } //end if - else - { - fp = SafeOpenRead(qf->filename); - if (qf->offset) fseek(fp, qf->offset, SEEK_SET); - if (offset) fseek(fp, offset, SEEK_CUR); - SafeRead(fp, buffer, length); - fclose(fp); - - return length; - } //end else -} //end of the function ReadQuakeFile diff --git a/tools/quake3/bspc/l_qfiles.h b/tools/quake3/bspc/l_qfiles.h deleted file mode 100644 index 819fb359..00000000 --- a/tools/quake3/bspc/l_qfiles.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "deps/qcommon/unzip.h" - -#define QFILETYPE_UNKNOWN 0x8000 -#define QFILETYPE_PAK 0x0001 -#define QFILETYPE_PK3 0x0002 -#define QFILETYPE_BSP 0x0004 -#define QFILETYPE_MAP 0x0008 -#define QFILETYPE_MDL 0x0010 -#define QFILETYPE_MD2 0x0020 -#define QFILETYPE_MD3 0x0040 -#define QFILETYPE_WAL 0x0080 -#define QFILETYPE_WAV 0x0100 -#define QFILETYPE_AAS 0x4000 - -#define QFILEEXT_UNKNOWN "" -#define QFILEEXT_PAK ".PAK" -#define QFILEEXT_PK3 ".PK3" -#define QFILEEXT_SIN ".SIN" -#define QFILEEXT_BSP ".BSP" -#define QFILEEXT_MAP ".MAP" -#define QFILEEXT_MDL ".MDL" -#define QFILEEXT_MD2 ".MD2" -#define QFILEEXT_MD3 ".MD3" -#define QFILEEXT_WAL ".WAL" -#define QFILEEXT_WAV ".WAV" -#define QFILEEXT_AAS ".AAS" - -//maximum path length -#ifndef _MAX_PATH - #define _MAX_PATH 1024 -#endif - -//for Sin packs -#define MAX_PAK_FILENAME_LENGTH 120 -#define SINPAKHEADER (('K'<<24)+('A'<<16)+('P'<<8)+'S') - -typedef struct -{ - char name[MAX_PAK_FILENAME_LENGTH]; - int filepos, filelen; -} dsinpackfile_t; - -typedef struct quakefile_s -{ - char pakfile[_MAX_PATH]; - char filename[_MAX_PATH]; - char origname[_MAX_PATH]; - int zipfile; - int type; - int offset; - int length; - unz_s zipinfo; - struct quakefile_s *next; -} quakefile_t; - -//returns the file extension for the given type -char *QuakeFileTypeExtension(int type); -//returns the file type for the given extension -int QuakeFileExtensionType(char *extension); -//return the Quake file type for the given file -int QuakeFileType(char *filename); -//returns true if the filename complies to the filter -int FileFilter(char *filter, char *filename, int casesensitive); -//find Quake files using the given filter -quakefile_t *FindQuakeFiles(char *filter); -//load the given Quake file, returns the length of the file -int LoadQuakeFile(quakefile_t *qf, void **bufferptr); -//read part of a Quake file into the buffer -int ReadQuakeFile(quakefile_t *qf, void *buffer, int offset, int length); diff --git a/tools/quake3/bspc/l_threads.c b/tools/quake3/bspc/l_threads.c deleted file mode 100644 index 8c0dbc1e..00000000 --- a/tools/quake3/bspc/l_threads.c +++ /dev/null @@ -1,1505 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "l_cmd.h" -#include "l_threads.h" -#include "l_log.h" -#include "l_mem.h" - -#define MAX_THREADS 64 - -//#define THREAD_DEBUG - -int dispatch; -int workcount; -int oldf; -qboolean pacifier; -qboolean threaded; -void (*workfunction) (int); - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int GetThreadWork(void) -{ - int r; - int f; - - ThreadLock(); - - if (dispatch == workcount) - { - ThreadUnlock (); - return -1; - } - - f = 10*dispatch / workcount; - if (f != oldf) - { - oldf = f; - if (pacifier) - printf ("%i...", f); - } //end if - - r = dispatch; - dispatch++; - ThreadUnlock (); - - return r; -} //end of the function GetThreadWork -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadWorkerFunction(int threadnum) -{ - int work; - - while(1) - { - work = GetThreadWork (); - if (work == -1) - break; -//printf ("thread %i, work %i\n", threadnum, work); - workfunction(work); - } //end while -} //end of the function ThreadWorkerFunction -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int)) -{ - if (numthreads == -1) - ThreadSetDefault (); - workfunction = func; - RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction); -} //end of the function RunThreadsOnIndividual - - -//=================================================================== -// -// WIN32 -// -//=================================================================== - -#if defined(WIN32) || defined(_WIN32) - -#define USED - -#include - -typedef struct thread_s -{ - HANDLE handle; - int threadid; - int id; - struct thread_s *next; -} thread_t; - -thread_t *firstthread; -thread_t *lastthread; -int currentnumthreads; -int currentthreadid; - -int numthreads = 1; -CRITICAL_SECTION crit; -HANDLE semaphore; -static int enter; -static int numwaitingthreads = 0; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetDefault(void) -{ - SYSTEM_INFO info; - - if (numthreads == -1) // not set manually - { - GetSystemInfo (&info); - numthreads = info.dwNumberOfProcessors; - if (numthreads < 1 || numthreads > 32) - numthreads = 1; - } //end if - qprintf ("%i threads\n", numthreads); -} //end of the function ThreadSetDefault -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadLock(void) -{ - if (!threaded) - { - Error("ThreadLock: !threaded"); - return; - } //end if - EnterCriticalSection(&crit); - if (enter) - Error("Recursive ThreadLock\n"); - enter = 1; -} //end of the function ThreadLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadUnlock (void) -{ - if (!threaded) - { - Error("ThreadUnlock: !threaded"); - return; - } //end if - if (!enter) - Error("ThreadUnlock without lock\n"); - enter = 0; - LeaveCriticalSection(&crit); -} //end of the function ThreadUnlock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetupLock(void) -{ - Log_Print("Win32 multi-threading\n"); - InitializeCriticalSection(&crit); - threaded = true; //Stupid me... forgot this!!! - currentnumthreads = 0; - currentthreadid = 0; -} //end of the function ThreadInitLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadShutdownLock(void) -{ - DeleteCriticalSection(&crit); - threaded = false; //Stupid me... forgot this!!! -} //end of the function ThreadShutdownLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetupSemaphore(void) -{ - semaphore = CreateSemaphore(NULL, 0, 99999999, "bspc"); -} //end of the function ThreadSetupSemaphore -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadShutdownSemaphore(void) -{ -} //end of the function ThreadShutdownSemaphore -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSemaphoreWait(void) -{ - WaitForSingleObject(semaphore, INFINITE); -} //end of the function ThreadSemaphoreWait -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSemaphoreIncrease(int count) -{ - ReleaseSemaphore(semaphore, count, NULL); -} //end of the function ThreadSemaphoreIncrease -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RunThreadsOn(int workcnt, qboolean showpacifier, void(*func)(int)) -{ - int threadid[MAX_THREADS]; - HANDLE threadhandle[MAX_THREADS]; - int i; - int start, end; - - Log_Print("Win32 multi-threading\n"); - start = I_FloatTime (); - dispatch = 0; - workcount = workcnt; - oldf = -1; - pacifier = showpacifier; - threaded = true; - - if (numthreads == -1) - ThreadSetDefault (); - - if (numthreads < 1 || numthreads > MAX_THREADS) numthreads = 1; - // - // run threads in parallel - // - InitializeCriticalSection (&crit); - - numwaitingthreads = 0; - - if (numthreads == 1) - { // use same thread - func (0); - } //end if - else - { -// printf("starting %d threads\n", numthreads); - for (i = 0; i < numthreads; i++) - { - threadhandle[i] = CreateThread( - NULL, // LPSECURITY_ATTRIBUTES lpsa, - 0, // DWORD cbStack, - (LPTHREAD_START_ROUTINE)func, // LPTHREAD_START_ROUTINE lpStartAddr, - (LPVOID)i, // LPVOID lpvThreadParm, - 0, // DWORD fdwCreate, - &threadid[i]); -// printf("started thread %d\n", i); - } //end for - - for (i = 0; i < numthreads; i++) - WaitForSingleObject (threadhandle[i], INFINITE); - } //end else - DeleteCriticalSection (&crit); - - threaded = false; - end = I_FloatTime (); - if (pacifier) printf (" (%i)\n", end-start); -} //end of the function RunThreadsOn -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AddThread(void (*func)(int)) -{ - thread_t *thread; - - if (numthreads == 1) - { - if (currentnumthreads >= numthreads) return; - currentnumthreads++; - func(-1); - currentnumthreads--; - } //end if - else - { - ThreadLock(); - if (currentnumthreads >= numthreads) - { - ThreadUnlock(); - return; - } //end if - //allocate new thread - thread = GetMemory(sizeof(thread_t)); - if (!thread) Error("can't allocate memory for thread\n"); - - // - thread->threadid = currentthreadid; - thread->handle = CreateThread( - NULL, // LPSECURITY_ATTRIBUTES lpsa, - 0, // DWORD cbStack, - (LPTHREAD_START_ROUTINE)func, // LPTHREAD_START_ROUTINE lpStartAddr, - (LPVOID) thread->threadid, // LPVOID lpvThreadParm, - 0, // DWORD fdwCreate, - &thread->id); - - //add the thread to the end of the list - thread->next = NULL; - if (lastthread) lastthread->next = thread; - else firstthread = thread; - lastthread = thread; - // -#ifdef THREAD_DEBUG - qprintf("added thread with id %d\n", thread->threadid); -#endif //THREAD_DEBUG - // - currentnumthreads++; - currentthreadid++; - // - ThreadUnlock(); - } //end else -} //end of the function AddThread -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RemoveThread(int threadid) -{ - thread_t *thread, *last; - - //if a single thread - if (threadid == -1) return; - // - ThreadLock(); - last = NULL; - for (thread = firstthread; thread; thread = thread->next) - { - if (thread->threadid == threadid) - { - if (last) last->next = thread->next; - else firstthread = thread->next; - if (!thread->next) lastthread = last; - // - FreeMemory(thread); - currentnumthreads--; -#ifdef THREAD_DEBUG - qprintf("removed thread with id %d\n", threadid); -#endif //THREAD_DEBUG - break; - } //end if - last = thread; - } //end if - if (!thread) Error("couldn't find thread with id %d", threadid); - ThreadUnlock(); -} //end of the function RemoveThread -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void WaitForAllThreadsFinished(void) -{ - HANDLE handle; - - ThreadLock(); - while(firstthread) - { - handle = firstthread->handle; - ThreadUnlock(); - - WaitForSingleObject(handle, INFINITE); - - ThreadLock(); - } //end while - ThreadUnlock(); -} //end of the function WaitForAllThreadsFinished -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int GetNumThreads(void) -{ - return currentnumthreads; -} //end of the function GetNumThreads - -#endif - - -//=================================================================== -// -// OSF1 -// -//=================================================================== - -#if defined(__osf__) - -#define USED - -#include - -typedef struct thread_s -{ - pthread_t thread; - int threadid; - int id; - struct thread_s *next; -} thread_t; - -thread_t *firstthread; -thread_t *lastthread; -int currentnumthreads; -int currentthreadid; - -int numthreads = 1; -pthread_mutex_t my_mutex; -pthread_attr_t attrib; -static int enter; -static int numwaitingthreads = 0; - - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetDefault(void) -{ - if (numthreads == -1) // not set manually - { - numthreads = 1; - } //end if - qprintf("%i threads\n", numthreads); -} //end of the function ThreadSetDefault -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadLock(void) -{ - if (!threaded) - { - Error("ThreadLock: !threaded"); - return; - } //end if - if (my_mutex) - { - pthread_mutex_lock(my_mutex); - } //end if - if (enter) - Error("Recursive ThreadLock\n"); - enter = 1; -} //end of the function ThreadLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadUnlock(void) -{ - if (!threaded) - { - Error("ThreadUnlock: !threaded"); - return; - } //end if - if (!enter) - Error("ThreadUnlock without lock\n"); - enter = 0; - if (my_mutex) - { - pthread_mutex_unlock(my_mutex); - } //end if -} //end of the function ThreadUnlock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetupLock(void) -{ - pthread_mutexattr_t mattrib; - - Log_Print("pthread multi-threading\n"); - - if (!my_mutex) - { - my_mutex = GetMemory(sizeof(*my_mutex)); - if (pthread_mutexattr_create (&mattrib) == -1) - Error ("pthread_mutex_attr_create failed"); - if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1) - Error ("pthread_mutexattr_setkind_np failed"); - if (pthread_mutex_init (my_mutex, mattrib) == -1) - Error ("pthread_mutex_init failed"); - } - - if (pthread_attr_create (&attrib) == -1) - Error ("pthread_attr_create failed"); - if (pthread_attr_setstacksize (&attrib, 0x100000) == -1) - Error ("pthread_attr_setstacksize failed"); - - threaded = true; - currentnumthreads = 0; - currentthreadid = 0; -} //end of the function ThreadInitLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadShutdownLock(void) -{ - threaded = false; -} //end of the function ThreadShutdownLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RunThreadsOn(int workcnt, qboolean showpacifier, void(*func)(int)) -{ - int i; - pthread_t work_threads[MAX_THREADS]; - pthread_addr_t status; - pthread_attr_t attrib; - pthread_mutexattr_t mattrib; - int start, end; - - Log_Print("pthread multi-threading\n"); - - start = I_FloatTime (); - dispatch = 0; - workcount = workcnt; - oldf = -1; - pacifier = showpacifier; - threaded = true; - - if (numthreads < 1 || numthreads > MAX_THREADS) numthreads = 1; - - if (pacifier) - setbuf (stdout, NULL); - - if (!my_mutex) - { - my_mutex = GetMemory(sizeof(*my_mutex)); - if (pthread_mutexattr_create (&mattrib) == -1) - Error ("pthread_mutex_attr_create failed"); - if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1) - Error ("pthread_mutexattr_setkind_np failed"); - if (pthread_mutex_init (my_mutex, mattrib) == -1) - Error ("pthread_mutex_init failed"); - } - - if (pthread_attr_create (&attrib) == -1) - Error ("pthread_attr_create failed"); - if (pthread_attr_setstacksize (&attrib, 0x100000) == -1) - Error ("pthread_attr_setstacksize failed"); - - for (i=0 ; i= numthreads) return; - currentnumthreads++; - func(-1); - currentnumthreads--; - } //end if - else - { - ThreadLock(); - if (currentnumthreads >= numthreads) - { - ThreadUnlock(); - return; - } //end if - //allocate new thread - thread = GetMemory(sizeof(thread_t)); - if (!thread) Error("can't allocate memory for thread\n"); - // - thread->threadid = currentthreadid; - - if (pthread_create(&thread->thread, attrib, (pthread_startroutine_t)func, (pthread_addr_t)thread->threadid) == -1) - Error ("pthread_create failed"); - - //add the thread to the end of the list - thread->next = NULL; - if (lastthread) lastthread->next = thread; - else firstthread = thread; - lastthread = thread; - // -#ifdef THREAD_DEBUG - qprintf("added thread with id %d\n", thread->threadid); -#endif //THREAD_DEBUG - // - currentnumthreads++; - currentthreadid++; - // - ThreadUnlock(); - } //end else -} //end of the function AddThread -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RemoveThread(int threadid) -{ - thread_t *thread, *last; - - //if a single thread - if (threadid == -1) return; - // - ThreadLock(); - last = NULL; - for (thread = firstthread; thread; thread = thread->next) - { - if (thread->threadid == threadid) - { - if (last) last->next = thread->next; - else firstthread = thread->next; - if (!thread->next) lastthread = last; - // - FreeMemory(thread); - currentnumthreads--; -#ifdef THREAD_DEBUG - qprintf("removed thread with id %d\n", threadid); -#endif //THREAD_DEBUG - break; - } //end if - last = thread; - } //end if - if (!thread) Error("couldn't find thread with id %d", threadid); - ThreadUnlock(); -} //end of the function RemoveThread -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void WaitForAllThreadsFinished(void) -{ - pthread_t *thread; - pthread_addr_t status; - - ThreadLock(); - while(firstthread) - { - thread = &firstthread->thread; - ThreadUnlock(); - - if (pthread_join(*thread, &status) == -1) - Error("pthread_join failed"); - - ThreadLock(); - } //end while - ThreadUnlock(); -} //end of the function WaitForAllThreadsFinished -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int GetNumThreads(void) -{ - return currentnumthreads; -} //end of the function GetNumThreads - -#endif - -//=================================================================== -// -// LINUX -// -//=================================================================== - -#if defined(LINUX) - -#define USED - -#include -#include - -typedef struct thread_s -{ - pthread_t thread; - int threadid; - int id; - struct thread_s *next; -} thread_t; - -thread_t *firstthread; -thread_t *lastthread; -int currentnumthreads; -int currentthreadid; - -int numthreads = 1; -pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER; -pthread_attr_t attrib; -sem_t semaphore; -static int enter; - - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetDefault(void) -{ - if (numthreads == -1) // not set manually - { - numthreads = 1; - } //end if - qprintf("%i threads\n", numthreads); -} //end of the function ThreadSetDefault -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadLock(void) -{ - if (!threaded) - { - Error("ThreadLock: !threaded"); - return; - } //end if - pthread_mutex_lock(&my_mutex); - if (enter) - Error("Recursive ThreadLock\n"); - enter = 1; -} //end of the function ThreadLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadUnlock(void) -{ - if (!threaded) - { - Error("ThreadUnlock: !threaded"); - return; - } //end if - if (!enter) - Error("ThreadUnlock without lock\n"); - enter = 0; - pthread_mutex_unlock(&my_mutex); -} //end of the function ThreadUnlock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetupLock(void) -{ - Log_Print("pthread multi-threading\n"); - - threaded = true; - currentnumthreads = 0; - currentthreadid = 0; -} //end of the function ThreadInitLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadShutdownLock(void) -{ - threaded = false; -} //end of the function ThreadShutdownLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetupSemaphore(void) -{ - sem_init(&semaphore, 0, 0); -} //end of the function ThreadSetupSemaphore -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadShutdownSemaphore(void) -{ - sem_destroy(&semaphore); -} //end of the function ThreadShutdownSemaphore -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSemaphoreWait(void) -{ - sem_wait(&semaphore); -} //end of the function ThreadSemaphoreWait -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSemaphoreIncrease(int count) -{ - int i; - - for (i = 0; i < count; i++) - { - sem_post(&semaphore); - } //end for -} //end of the function ThreadSemaphoreIncrease -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RunThreadsOn(int workcnt, qboolean showpacifier, void(*func)(int)) -{ - int i; - pthread_t work_threads[MAX_THREADS]; - void *pthread_return; - int start, end; - - Log_Print("pthread multi-threading\n"); - - start = I_FloatTime (); - dispatch = 0; - workcount = workcnt; - oldf = -1; - pacifier = showpacifier; - threaded = true; - - if (numthreads < 1 || numthreads > MAX_THREADS) numthreads = 1; - - if (pacifier) - setbuf (stdout, NULL); - - for (i=0 ; i= numthreads) return; - currentnumthreads++; - func(-1); - currentnumthreads--; - } //end if - else - { - ThreadLock(); - if (currentnumthreads >= numthreads) - { - ThreadUnlock(); - return; - } //end if - //allocate new thread - thread = GetMemory(sizeof(thread_t)); - if (!thread) Error("can't allocate memory for thread\n"); - // - thread->threadid = currentthreadid; - - if (pthread_create(&thread->thread, NULL, (void *)func, (void *)thread->threadid) == -1) - Error ("pthread_create failed"); - - //add the thread to the end of the list - thread->next = NULL; - if (lastthread) lastthread->next = thread; - else firstthread = thread; - lastthread = thread; - // -#ifdef THREAD_DEBUG - qprintf("added thread with id %d\n", thread->threadid); -#endif //THREAD_DEBUG - // - currentnumthreads++; - currentthreadid++; - // - ThreadUnlock(); - } //end else -} //end of the function AddThread -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RemoveThread(int threadid) -{ - thread_t *thread, *last; - - //if a single thread - if (threadid == -1) return; - // - ThreadLock(); - last = NULL; - for (thread = firstthread; thread; thread = thread->next) - { - if (thread->threadid == threadid) - { - if (last) last->next = thread->next; - else firstthread = thread->next; - if (!thread->next) lastthread = last; - // - FreeMemory(thread); - currentnumthreads--; -#ifdef THREAD_DEBUG - qprintf("removed thread with id %d\n", threadid); -#endif //THREAD_DEBUG - break; - } //end if - last = thread; - } //end if - if (!thread) Error("couldn't find thread with id %d", threadid); - ThreadUnlock(); -} //end of the function RemoveThread -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void WaitForAllThreadsFinished(void) -{ - pthread_t *thread; - void *pthread_return; - - ThreadLock(); - while(firstthread) - { - thread = &firstthread->thread; - ThreadUnlock(); - - if (pthread_join(*thread, &pthread_return) == -1) - Error("pthread_join failed"); - - ThreadLock(); - } //end while - ThreadUnlock(); -} //end of the function WaitForAllThreadsFinished -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int GetNumThreads(void) -{ - return currentnumthreads; -} //end of the function GetNumThreads - -#endif //LINUX - - -//=================================================================== -// -// IRIX -// -//=================================================================== - -#ifdef _MIPS_ISA - -#define USED - -#include -#include -#include -#include - -typedef struct thread_s -{ - int threadid; - int id; - struct thread_s *next; -} thread_t; - -thread_t *firstthread; -thread_t *lastthread; -int currentnumthreads; -int currentthreadid; - -int numthreads = 1; -static int enter; -static int numwaitingthreads = 0; - -abilock_t lck; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetDefault (void) -{ - if (numthreads == -1) - numthreads = prctl(PR_MAXPPROCS); - printf ("%i threads\n", numthreads); -//@@ - usconfig (CONF_INITUSERS, numthreads); -} //end of the function ThreadSetDefault -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadLock (void) -{ - spin_lock (&lck); -} //end of the function ThreadLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadUnlock (void) -{ - release_lock(&lck); -} //end of the function ThreadUnlock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetupLock(void) -{ - init_lock (&lck); - - Log_Print("IRIX multi-threading\n"); - - threaded = true; - currentnumthreads = 0; - currentthreadid = 0; -} //end of the function ThreadInitLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadShutdownLock(void) -{ - threaded = false; -} //end of the function ThreadShutdownLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int)) -{ - int i; - int pid[MAX_THREADS]; - int start, end; - - start = I_FloatTime (); - dispatch = 0; - workcount = workcnt; - oldf = -1; - pacifier = showpacifier; - threaded = true; - - if (numthreads < 1 || numthreads > MAX_THREADS) numthreads = 1; - - if (pacifier) - setbuf (stdout, NULL); - - init_lock (&lck); - - for (i=0 ; i= numthreads) return; - currentnumthreads++; - func(-1); - currentnumthreads--; - } //end if - else - { - ThreadLock(); - if (currentnumthreads >= numthreads) - { - ThreadUnlock(); - return; - } //end if - //allocate new thread - thread = GetMemory(sizeof(thread_t)); - if (!thread) Error("can't allocate memory for thread\n"); - // - thread->threadid = currentthreadid; - - thread->id = sprocsp ( (void (*)(void *, size_t))func, PR_SALL, (void *)thread->threadid, NULL, 0x100000); - if (thread->id == -1) - { - perror ("sproc"); - Error ("sproc failed"); - } - - //add the thread to the end of the list - thread->next = NULL; - if (lastthread) lastthread->next = thread; - else firstthread = thread; - lastthread = thread; - // -#ifdef THREAD_DEBUG - qprintf("added thread with id %d\n", thread->threadid); -#endif //THREAD_DEBUG - // - currentnumthreads++; - currentthreadid++; - // - ThreadUnlock(); - } //end else -} //end of the function AddThread -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RemoveThread(int threadid) -{ - thread_t *thread, *last; - - //if a single thread - if (threadid == -1) return; - // - ThreadLock(); - last = NULL; - for (thread = firstthread; thread; thread = thread->next) - { - if (thread->threadid == threadid) - { - if (last) last->next = thread->next; - else firstthread = thread->next; - if (!thread->next) lastthread = last; - // - FreeMemory(thread); - currentnumthreads--; -#ifdef THREAD_DEBUG - qprintf("removed thread with id %d\n", threadid); -#endif //THREAD_DEBUG - break; - } //end if - last = thread; - } //end if - if (!thread) Error("couldn't find thread with id %d", threadid); - ThreadUnlock(); -} //end of the function RemoveThread -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void WaitForAllThreadsFinished(void) -{ - ThreadLock(); - while(firstthread) - { - ThreadUnlock(); - - //wait (NULL); - - ThreadLock(); - } //end while - ThreadUnlock(); -} //end of the function WaitForAllThreadsFinished -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int GetNumThreads(void) -{ - return currentnumthreads; -} //end of the function GetNumThreads - -#endif //_MIPS_ISA - - -//======================================================================= -// -// SINGLE THREAD -// -//======================================================================= - -#ifndef USED - -int numthreads = 1; -int currentnumthreads = 0; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetDefault(void) -{ - numthreads = 1; -} //end of the function ThreadSetDefault -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadLock(void) -{ -} //end of the function ThreadLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadUnlock(void) -{ -} //end of the function ThreadUnlock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetupLock(void) -{ - Log_Print("no multi-threading\n"); -} //end of the function ThreadInitLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadShutdownLock(void) -{ -} //end of the function ThreadShutdownLock -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSetupSemaphore(void) -{ -} //end of the function ThreadSetupSemaphore -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadShutdownSemaphore(void) -{ -} //end of the function ThreadShutdownSemaphore -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSemaphoreWait(void) -{ -} //end of the function ThreadSemaphoreWait -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ThreadSemaphoreIncrease(int count) -{ -} //end of the function ThreadSemaphoreIncrease -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RunThreadsOn(int workcnt, qboolean showpacifier, void(*func)(int)) -{ - int start, end; - - Log_Print("no multi-threading\n"); - dispatch = 0; - workcount = workcnt; - oldf = -1; - pacifier = showpacifier; - start = I_FloatTime (); -#ifdef NeXT - if (pacifier) - setbuf (stdout, NULL); -#endif - func(0); - - end = I_FloatTime (); - if (pacifier) - printf (" (%i)\n", end-start); -} //end of the function RunThreadsOn -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AddThread(void (*func)(int)) -{ - if (currentnumthreads >= numthreads) return; - currentnumthreads++; - func(-1); - currentnumthreads--; -} //end of the function AddThread -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RemoveThread(int threadid) -{ -} //end of the function RemoveThread -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void WaitForAllThreadsFinished(void) -{ -} //end of the function WaitForAllThreadsFinished -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int GetNumThreads(void) -{ - return currentnumthreads; -} //end of the function GetNumThreads - -#endif //USED diff --git a/tools/quake3/bspc/l_threads.h b/tools/quake3/bspc/l_threads.h deleted file mode 100644 index fe4df17e..00000000 --- a/tools/quake3/bspc/l_threads.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -extern int numthreads; - -void ThreadSetDefault (void); -int GetThreadWork (void); -void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int)); -void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int)); - -//mutex -void ThreadSetupLock(void); -void ThreadShutdownLock(void); -void ThreadLock (void); -void ThreadUnlock (void); -//semaphore -void ThreadSetupSemaphore(void); -void ThreadShutdownSemaphore(void); -void ThreadSemaphoreWait(void); -void ThreadSemaphoreIncrease(int count); -//add/remove threads -void AddThread(void (*func)(int)); -void RemoveThread(int threadid); -void WaitForAllThreadsFinished(void); -int GetNumThreads(void); - diff --git a/tools/quake3/bspc/l_utils.c b/tools/quake3/bspc/l_utils.c deleted file mode 100644 index 8b229aa4..00000000 --- a/tools/quake3/bspc/l_utils.c +++ /dev/null @@ -1,259 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -//#ifndef BOTLIB -//#define BOTLIB -//#endif //BOTLIB - -#ifdef BOTLIB -#include "q_shared.h" -#include "qfiles.h" -#include "botlib.h" -#include "l_log.h" -#include "l_libvar.h" -#include "l_memory.h" -//#include "l_utils.h" -#include "be_interface.h" -#else //BOTLIB -#include "qbsp.h" -#include "l_mem.h" -#endif //BOTLIB - -#ifdef BOTLIB -//======================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//======================================================================== -void Vector2Angles(vec3_t value1, vec3_t angles) -{ - float forward; - float yaw, pitch; - - if (value1[1] == 0 && value1[0] == 0) - { - yaw = 0; - if (value1[2] > 0) pitch = 90; - else pitch = 270; - } //end if - else - { - yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); - if (yaw < 0) yaw += 360; - - forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]); - pitch = (int) (atan2(value1[2], forward) * 180 / M_PI); - if (pitch < 0) pitch += 360; - } //end else - - angles[PITCH] = -pitch; - angles[YAW] = yaw; - angles[ROLL] = 0; -} //end of the function Vector2Angles -#endif //BOTLIB -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ConvertPath(char *path) -{ - while(*path) - { - if (*path == '/' || *path == '\\') *path = PATHSEPERATOR_CHAR; - path++; - } //end while -} //end of the function ConvertPath -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AppendPathSeperator(char *path, int length) -{ - int pathlen = strlen(path); - - if (strlen(path) && length-pathlen > 1 && path[pathlen-1] != '/' && path[pathlen-1] != '\\') - { - path[pathlen] = PATHSEPERATOR_CHAR; - path[pathlen+1] = '\0'; - } //end if -} //end of the function AppenPathSeperator - -#if 0 -//=========================================================================== -// returns pointer to file handle -// sets offset to and length of 'filename' in the pak file -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean FindFileInPak(char *pakfile, char *filename, foundfile_t *file) -{ - FILE *fp; - dpackheader_t packheader; - dpackfile_t *packfiles; - int numdirs, i; - char path[MAX_PATH]; - - //open the pak file - fp = fopen(pakfile, "rb"); - if (!fp) - { - return false; - } //end if - //read pak header, check for valid pak id and seek to the dir entries - if ((fread(&packheader, 1, sizeof(dpackheader_t), fp) != sizeof(dpackheader_t)) - || (packheader.ident != IDPAKHEADER) - || (fseek(fp, LittleLong(packheader.dirofs), SEEK_SET)) - ) - { - fclose(fp); - return false; - } //end if - //number of dir entries in the pak file - numdirs = LittleLong(packheader.dirlen) / sizeof(dpackfile_t); - packfiles = (dpackfile_t *) GetMemory(numdirs * sizeof(dpackfile_t)); - //read the dir entry - if (fread(packfiles, sizeof(dpackfile_t), numdirs, fp) != numdirs) - { - fclose(fp); - FreeMemory(packfiles); - return false; - } //end if - fclose(fp); - // - strcpy(path, filename); - ConvertPath(path); - //find the dir entry in the pak file - for (i = 0; i < numdirs; i++) - { - //convert the dir entry name - ConvertPath(packfiles[i].name); - //compare the dir entry name with the filename - if (Q_strcasecmp(packfiles[i].name, path) == 0) - { - strcpy(file->filename, pakfile); - file->offset = LittleLong(packfiles[i].filepos); - file->length = LittleLong(packfiles[i].filelen); - FreeMemory(packfiles); - return true; - } //end if - } //end for - FreeMemory(packfiles); - return false; -} //end of the function FindFileInPak -//=========================================================================== -// find a Quake2 file -// returns full path in 'filename' -// sets offset and length of the file -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean FindQuakeFile2(char *basedir, char *gamedir, char *filename, foundfile_t *file) -{ - int dir, i; - //NOTE: 3 is necessary (LCC bug???) - char gamedirs[3][MAX_PATH] = {"","",""}; - char filedir[MAX_PATH] = ""; - - // - if (gamedir) strncpy(gamedirs[0], gamedir, MAX_PATH); - strncpy(gamedirs[1], "baseq2", MAX_PATH); - // - //find the file in the two game directories - for (dir = 0; dir < 2; dir++) - { - //check if the file is in a directory - filedir[0] = 0; - if (basedir && strlen(basedir)) - { - strncpy(filedir, basedir, MAX_PATH); - AppendPathSeperator(filedir, MAX_PATH); - } //end if - if (strlen(gamedirs[dir])) - { - strncat(filedir, gamedirs[dir], MAX_PATH - strlen(filedir)); - AppendPathSeperator(filedir, MAX_PATH); - } //end if - strncat(filedir, filename, MAX_PATH - strlen(filedir)); - ConvertPath(filedir); - Log_Write("accessing %s", filedir); - if (!access(filedir, 0x04)) - { - strcpy(file->filename, filedir); - file->length = 0; - file->offset = 0; - return true; - } //end if - //check if the file is in a pak?.pak - for (i = 0; i < 10; i++) - { - filedir[0] = 0; - if (basedir && strlen(basedir)) - { - strncpy(filedir, basedir, MAX_PATH); - AppendPathSeperator(filedir, MAX_PATH); - } //end if - if (strlen(gamedirs[dir])) - { - strncat(filedir, gamedirs[dir], MAX_PATH - strlen(filedir)); - AppendPathSeperator(filedir, MAX_PATH); - } //end if - sprintf(&filedir[strlen(filedir)], "pak%d.pak\0", i); - if (!access(filedir, 0x04)) - { - Log_Write("searching %s in %s", filename, filedir); - if (FindFileInPak(filedir, filename, file)) return true; - } //end if - } //end for - } //end for - file->offset = 0; - file->length = 0; - return false; -} //end of the function FindQuakeFile2 -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef BOTLIB -qboolean FindQuakeFile(char *filename, foundfile_t *file) -{ - return FindQuakeFile2(LibVarGetString("basedir"), - LibVarGetString("gamedir"), filename, file); -} //end of the function FindQuakeFile -#else //BOTLIB -qboolean FindQuakeFile(char *basedir, char *gamedir, char *filename, foundfile_t *file) -{ - return FindQuakeFile2(basedir, gamedir, filename, file); -} //end of the function FindQuakeFile -#endif //BOTLIB - -#endif diff --git a/tools/quake3/bspc/l_utils.h b/tools/quake3/bspc/l_utils.h deleted file mode 100644 index ebd8dbb6..00000000 --- a/tools/quake3/bspc/l_utils.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#ifndef MAX_PATH - #define MAX_PATH 64 -#endif - -#ifndef PATH_SEPERATORSTR - #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) - #define PATHSEPERATOR_STR "\\" - #else - #define PATHSEPERATOR_STR "/" - #endif -#endif -#ifndef PATH_SEPERATORCHAR - #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) - #define PATHSEPERATOR_CHAR '\\' - #else - #define PATHSEPERATOR_CHAR '/' - #endif -#endif - -//random in the range [0, 1] -#define random() ((rand () & 0x7fff) / ((float)0x7fff)) -//random in the range [-1, 1] -#define crandom() (2.0 * (random() - 0.5)) -//min and max -#define Maximum(x,y) (x > y ? x : y) -#define Minimum(x,y) (x < y ? x : y) -//absolute value -#define FloatAbs(x) (*(float *) &((* (int *) &(x)) & 0x7FFFFFFF)) -#define IntAbs(x) (~(x)) -//coordinates -#define _X 0 -#define _Y 1 -#define _Z 2 - -typedef struct foundfile_s -{ - int offset; - int length; - char filename[MAX_PATH]; //screw LCC, array must be at end of struct -} foundfile_t; - -void Vector2Angles(vec3_t value1, vec3_t angles); -//set the correct path seperators -void ConvertPath(char *path); -//append a path seperator to the given path not exceeding the length -void AppendPathSeperator(char *path, int length); -//find a file in a pak file -qboolean FindFileInPak(char *pakfile, char *filename, foundfile_t *file); -//find a quake file -#ifdef BOTLIB -qboolean FindQuakeFile(char *filename, foundfile_t *file); -#else //BOTLIB -qboolean FindQuakeFile(char *basedir, char *gamedir, char *filename, foundfile_t *file); -#endif //BOTLIB - - - diff --git a/tools/quake3/bspc/lcc.mak b/tools/quake3/bspc/lcc.mak deleted file mode 100644 index 1b99864d..00000000 --- a/tools/quake3/bspc/lcc.mak +++ /dev/null @@ -1,61 +0,0 @@ -# -# Makefile for the BSPC tool for the Gladiator Bot -# Intended for LCC-Win32 -# - -CC=lcc -CFLAGS=-DC_ONLY -o -OBJS= _files.obj\ - aas_areamerging.obj\ - aas_cfg.obj\ - aas_create.obj\ - aas_edgemelting.obj\ - aas_facemerging.obj\ - aas_file.obj\ - aas_gsubdiv.obj\ - aas_map.obj\ - aas_prunenodes.obj\ - aas_store.obj\ - brushbsp.obj\ - bspc.obj\ - csg.obj\ - faces.obj\ - glfile.obj\ - l_bsp_hl.obj\ - l_bsp_q1.obj\ - l_bsp_q2.obj\ - l_bsp_sin.obj\ - l_cmd.obj\ - l_log.obj\ - l_math.obj\ - l_mem.obj\ - l_poly.obj\ - l_qfiles.obj\ - l_script.obj\ - l_threads.obj\ - l_utils.obj\ - leakfile.obj\ - map.obj\ - map_hl.obj\ - map_q1.obj\ - map_q2.obj\ - map_q2_new.obj\ - map_sin.obj\ - nodraw.obj\ - portals.obj\ - prtfile.obj\ - textures.obj\ - tree.obj\ - writebsp.obj - -all: bspc.exe - -bspc.exe: $(OBJS) - lcclnk - -clean: - del *.obj bspc.exe - -%.obj: %.c - $(CC) $(CFLAGS) $< - diff --git a/tools/quake3/bspc/leakfile.c b/tools/quake3/bspc/leakfile.c deleted file mode 100644 index 924b34d5..00000000 --- a/tools/quake3/bspc/leakfile.c +++ /dev/null @@ -1,101 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" - -/* -============================================================================== - -LEAF FILE GENERATION - -Save out name.line for qe3 to read -============================================================================== -*/ - - -/* -============= -LeakFile - -Finds the shortest possible chain of portals -that leads from the outside leaf to a specifically -occupied leaf -============= -*/ -void LeakFile (tree_t *tree) -{ - vec3_t mid; - FILE *linefile; - char filename[1024]; - node_t *node; - int count; - - if (!tree->outside_node.occupied) - return; - - qprintf ("--- LeakFile ---\n"); - - // - // write the points to the file - // - sprintf (filename, "%s.lin", source); - qprintf ("%s\n", filename); - linefile = fopen (filename, "w"); - if (!linefile) - Error ("Couldn't open %s\n", filename); - - count = 0; - node = &tree->outside_node; - while (node->occupied > 1) - { - int next; - portal_t *p, *nextportal; - node_t *nextnode; - int s; - - // find the best portal exit - next = node->occupied; - for (p=node->portals ; p ; p = p->next[!s]) - { - s = (p->nodes[0] == node); - if (p->nodes[s]->occupied - && p->nodes[s]->occupied < next) - { - nextportal = p; - nextnode = p->nodes[s]; - next = nextnode->occupied; - } - } - node = nextnode; - WindingCenter (nextportal->winding, mid); - fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]); - count++; - } - // add the occupant center - GetVectorForKey (node->occupant, "origin", mid); - - fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]); - qprintf ("%5i point linefile\n", count+1); - - fclose (linefile); -} - diff --git a/tools/quake3/bspc/linux-i386.mak b/tools/quake3/bspc/linux-i386.mak deleted file mode 100644 index fddff223..00000000 --- a/tools/quake3/bspc/linux-i386.mak +++ /dev/null @@ -1,109 +0,0 @@ -# -# Makefile for the BSPC tool for the Gladiator Bot -# Intended for gcc/Linux -# - -ARCH=i386 -CC=gcc -BASE_CFLAGS=-Dstricmp=strcasecmp - -#use these cflags to optimize it -CFLAGS=$(BASE_CFLAGS) -m486 -O6 -ffast-math -funroll-loops \ - -fomit-frame-pointer -fexpensive-optimizations -malign-loops=2 \ - -malign-jumps=2 -malign-functions=2 -DLINUX -DBSPC -#use these when debugging -#CFLAGS=$(BASE_CFLAGS) -g - -LDFLAGS=-ldl -lm -lpthread - -DO_CC=$(CC) $(CFLAGS) -o $@ -c $< - -############################################################################# -# SETUP AND BUILD BSPC -############################################################################# - -.c.o: - $(DO_CC) - -GAME_OBJS = \ - _files.o\ - aas_areamerging.o\ - aas_cfg.o\ - aas_create.o\ - aas_edgemelting.o\ - aas_facemerging.o\ - aas_file.o\ - aas_gsubdiv.o\ - aas_map.o\ - aas_prunenodes.o\ - aas_store.o\ - be_aas_bspc.o\ - deps/botlib/be_aas_bspq3.o\ - deps/botlib/be_aas_cluster.o\ - deps/botlib/be_aas_move.o\ - deps/botlib/be_aas_optimize.o\ - deps/botlib/be_aas_reach.o\ - deps/botlib/be_aas_sample.o\ - brushbsp.o\ - bspc.o\ - deps/qcommon/cm_load.o\ - deps/qcommon/cm_patch.o\ - deps/qcommon/cm_test.o\ - deps/qcommon/cm_trace.o\ - csg.o\ - glfile.o\ - l_bsp_ent.o\ - l_bsp_hl.o\ - l_bsp_q1.o\ - l_bsp_q2.o\ - l_bsp_q3.o\ - l_bsp_sin.o\ - l_cmd.o\ - deps/botlib/l_libvar.o\ - l_log.o\ - l_math.o\ - l_mem.o\ - l_poly.o\ - deps/botlib/l_precomp.o\ - l_qfiles.o\ - deps/botlib/l_script.o\ - deps/botlib/l_struct.o\ - l_threads.o\ - l_utils.o\ - leakfile.o\ - map.o\ - map_hl.o\ - map_q1.o\ - map_q2.o\ - map_q3.o\ - map_sin.o\ - deps/qcommon/md4.o\ - nodraw.o\ - portals.o\ - tetrahedron.o\ - textures.o\ - tree.o\ - deps/qcommon/unzip.o - -bspc$(ARCH) : $(GAME_OBJS) - $(CC) $(CFLAGS) -o $@ $(GAME_OBJS) $(LDFLAGS) - - -############################################################################# -# MISC -############################################################################# - -clean: - -rm -f $(GAME_OBJS) - -depend: - gcc -MM $(GAME_OBJS:.o=.c) - - -install: - cp bspci386 .. - -# -# From "make depend" -# - diff --git a/tools/quake3/bspc/map.c b/tools/quake3/bspc/map.c deleted file mode 100644 index c5d30455..00000000 --- a/tools/quake3/bspc/map.c +++ /dev/null @@ -1,1274 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "l_bsp_hl.h" -#include "l_bsp_q1.h" -#include "l_bsp_q2.h" -#include "l_bsp_q3.h" -#include "l_bsp_sin.h" -#include "l_mem.h" -#include "botlib/aasfile.h" //aas_bbox_t -#include "aas_store.h" //AAS_MAX_BBOXES -#include "aas_cfg.h" - -#define Sign(x) (x < 0 ? 1 : 0) - -int nummapbrushes; -mapbrush_t mapbrushes[MAX_MAPFILE_BRUSHES]; - -int nummapbrushsides; -side_t brushsides[MAX_MAPFILE_BRUSHSIDES]; -brush_texture_t side_brushtextures[MAX_MAPFILE_BRUSHSIDES]; - -int nummapplanes; -plane_t mapplanes[MAX_MAPFILE_PLANES]; -int mapplaneusers[MAX_MAPFILE_PLANES]; - -#define PLANE_HASHES 1024 -plane_t *planehash[PLANE_HASHES]; -vec3_t map_mins, map_maxs; - -#ifdef SIN -textureref_t side_newrefs[MAX_MAPFILE_BRUSHSIDES]; -#endif - -map_texinfo_t map_texinfo[MAX_MAPFILE_TEXINFO]; -int map_numtexinfo; -int loadedmaptype; //loaded map type - -// undefine to make plane finding use linear sort -#define USE_HASHING - -int c_boxbevels; -int c_edgebevels; -int c_areaportals; -int c_clipbrushes; -int c_squattbrushes; -int c_writtenbrushes; - -/* -============================================================================= - -PLANE FINDING - -============================================================================= -*/ - - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int PlaneSignBits(vec3_t normal) -{ - int i, signbits; - - signbits = 0; - for (i = 2; i >= 0; i--) - { - signbits = (signbits << 1) + Sign(normal[i]); - } //end for - return signbits; -} //end of the function PlaneSignBits -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int PlaneTypeForNormal(vec3_t normal) -{ - vec_t ax, ay, az; - -// NOTE: should these have an epsilon around 1.0? - if (normal[0] == 1.0 || normal[0] == -1.0) - return PLANE_X; - if (normal[1] == 1.0 || normal[1] == -1.0) - return PLANE_Y; - if (normal[2] == 1.0 || normal[2] == -1.0) - return PLANE_Z; - - ax = fabs(normal[0]); - ay = fabs(normal[1]); - az = fabs(normal[2]); - - if (ax >= ay && ax >= az) - return PLANE_ANYX; - if (ay >= ax && ay >= az) - return PLANE_ANYY; - return PLANE_ANYZ; -} //end of the function PlaneTypeForNormal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -//ME NOTE: changed from 0.00001 -#define NORMAL_EPSILON 0.0001 -//ME NOTE: changed from 0.01 -#define DIST_EPSILON 0.02 -qboolean PlaneEqual(plane_t *p, vec3_t normal, vec_t dist) -{ -#if 1 - if ( - fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON - && fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON - && fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON - && fabs(p->dist - dist) < DIST_EPSILON ) - return true; -#else - if (p->normal[0] == normal[0] - && p->normal[1] == normal[1] - && p->normal[2] == normal[2] - && p->dist == dist) - return true; -#endif - return false; -} //end of the function PlaneEqual -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AddPlaneToHash(plane_t *p) -{ - int hash; - - hash = (int)fabs(p->dist) / 8; - hash &= (PLANE_HASHES-1); - - p->hash_chain = planehash[hash]; - planehash[hash] = p; -} //end of the function AddPlaneToHash -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int CreateNewFloatPlane (vec3_t normal, vec_t dist) -{ - plane_t *p, temp; - - if (VectorLength(normal) < 0.5) - Error ("FloatPlane: bad normal"); - // create a new plane - if (nummapplanes+2 > MAX_MAPFILE_PLANES) - Error ("MAX_MAPFILE_PLANES"); - - p = &mapplanes[nummapplanes]; - VectorCopy (normal, p->normal); - p->dist = dist; - p->type = (p+1)->type = PlaneTypeForNormal (p->normal); - p->signbits = PlaneSignBits(p->normal); - - VectorSubtract (vec3_origin, normal, (p+1)->normal); - (p+1)->dist = -dist; - (p+1)->signbits = PlaneSignBits((p+1)->normal); - - nummapplanes += 2; - - // allways put axial planes facing positive first - if (p->type < 3) - { - if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0) - { - // flip order - temp = *p; - *p = *(p+1); - *(p+1) = temp; - - AddPlaneToHash (p); - AddPlaneToHash (p+1); - return nummapplanes - 1; - } - } - - AddPlaneToHash (p); - AddPlaneToHash (p+1); - return nummapplanes - 2; -} //end of the function CreateNewFloatPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void SnapVector(vec3_t normal) -{ - int i; - - for (i=0 ; i<3 ; i++) - { - if ( fabs(normal[i] - 1) < NORMAL_EPSILON ) - { - VectorClear (normal); - normal[i] = 1; - break; - } - if ( fabs(normal[i] - -1) < NORMAL_EPSILON ) - { - VectorClear (normal); - normal[i] = -1; - break; - } - } -} //end of the function SnapVector -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void SnapPlane(vec3_t normal, vec_t *dist) -{ - SnapVector(normal); - - if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON) - *dist = Q_rint(*dist); -} //end of the function SnapPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifndef USE_HASHING -int FindFloatPlane(vec3_t normal, vec_t dist) -{ - int i; - plane_t *p; - - SnapPlane(normal, &dist); - for (i = 0, p = mapplanes; i < nummapplanes; i++, p++) - { - if (PlaneEqual (p, normal, dist)) - { - mapplaneusers[i]++; - return i; - } //end if - } //end for - i = CreateNewFloatPlane (normal, dist); - mapplaneusers[i]++; - return i; -} //end of the function FindFloatPlane -#else -int FindFloatPlane (vec3_t normal, vec_t dist) -{ - int i; - plane_t *p; - int hash, h; - - SnapPlane (normal, &dist); - hash = (int)fabs(dist) / 8; - hash &= (PLANE_HASHES-1); - - // search the border bins as well - for (i = -1; i <= 1; i++) - { - h = (hash+i)&(PLANE_HASHES-1); - for (p = planehash[h]; p; p = p->hash_chain) - { - if (PlaneEqual(p, normal, dist)) - { - mapplaneusers[p-mapplanes]++; - return p - mapplanes; - } //end if - } //end for - } //end for - i = CreateNewFloatPlane (normal, dist); - mapplaneusers[i]++; - return i; -} //end of the function FindFloatPlane -#endif -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int PlaneFromPoints (int *p0, int *p1, int *p2) -{ - vec3_t t1, t2, normal; - vec_t dist; - - VectorSubtract (p0, p1, t1); - VectorSubtract (p2, p1, t2); - CrossProduct (t1, t2, normal); - VectorNormalize (normal); - - dist = DotProduct (p0, normal); - - return FindFloatPlane (normal, dist); -} //end of the function PlaneFromPoints -//=========================================================================== -// Adds any additional planes necessary to allow the brush to be expanded -// against axial bounding boxes -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AddBrushBevels (mapbrush_t *b) -{ - int axis, dir; - int i, j, k, l, order; - side_t sidetemp; - brush_texture_t tdtemp; -#ifdef SIN - textureref_t trtemp; -#endif - side_t *s, *s2; - vec3_t normal; - float dist; - winding_t *w, *w2; - vec3_t vec, vec2; - float d; - - // - // add the axial planes - // - order = 0; - for (axis=0 ; axis <3 ; axis++) - { - for (dir=-1 ; dir <= 1 ; dir+=2, order++) - { - // see if the plane is allready present - for (i=0, s=b->original_sides ; inumsides ; i++,s++) - { - if (mapplanes[s->planenum].normal[axis] == dir) - break; - } - - if (i == b->numsides) - { // add a new side - if (nummapbrushsides == MAX_MAP_BRUSHSIDES) - Error ("MAX_MAP_BRUSHSIDES"); - nummapbrushsides++; - b->numsides++; - VectorClear (normal); - normal[axis] = dir; - if (dir == 1) - dist = b->maxs[axis]; - else - dist = -b->mins[axis]; - s->planenum = FindFloatPlane (normal, dist); - s->texinfo = b->original_sides[0].texinfo; -#ifdef SIN - s->lightinfo = b->original_sides[0].lightinfo; -#endif - s->contents = b->original_sides[0].contents; - s->flags |= SFL_BEVEL; - c_boxbevels++; - } - - // if the plane is not in it canonical order, swap it - if (i != order) - { - sidetemp = b->original_sides[order]; - b->original_sides[order] = b->original_sides[i]; - b->original_sides[i] = sidetemp; - - j = b->original_sides - brushsides; - tdtemp = side_brushtextures[j+order]; - side_brushtextures[j+order] = side_brushtextures[j+i]; - side_brushtextures[j+i] = tdtemp; - -#ifdef SIN - trtemp = side_newrefs[j+order]; - side_newrefs[j+order] = side_newrefs[j+i]; - side_newrefs[j+i] = trtemp; -#endif - } - } - } - - // - // add the edge bevels - // - if (b->numsides == 6) - return; // pure axial - - // test the non-axial plane edges - for (i=6 ; inumsides ; i++) - { - s = b->original_sides + i; - w = s->winding; - if (!w) - continue; - for (j=0 ; jnumpoints ; j++) - { - k = (j+1)%w->numpoints; - VectorSubtract (w->p[j], w->p[k], vec); - if (VectorNormalize (vec) < 0.5) - continue; - SnapVector (vec); - for (k=0 ; k<3 ; k++) - if ( vec[k] == -1 || vec[k] == 1) - break; // axial - if (k != 3) - continue; // only test non-axial edges - - // try the six possible slanted axials from this edge - for (axis=0 ; axis <3 ; axis++) - { - for (dir=-1 ; dir <= 1 ; dir+=2) - { - // construct a plane - VectorClear (vec2); - vec2[axis] = dir; - CrossProduct (vec, vec2, normal); - if (VectorNormalize (normal) < 0.5) - continue; - dist = DotProduct (w->p[j], normal); - - // if all the points on all the sides are - // behind this plane, it is a proper edge bevel - for (k=0 ; knumsides ; k++) - { - // if this plane has allready been used, skip it - if (PlaneEqual (&mapplanes[b->original_sides[k].planenum] - , normal, dist) ) - break; - - w2 = b->original_sides[k].winding; - if (!w2) - continue; - for (l=0 ; lnumpoints ; l++) - { - d = DotProduct (w2->p[l], normal) - dist; - if (d > 0.1) - break; // point in front - } - if (l != w2->numpoints) - break; - } - - if (k != b->numsides) - continue; // wasn't part of the outer hull - // add this plane - if (nummapbrushsides == MAX_MAP_BRUSHSIDES) - Error ("MAX_MAP_BRUSHSIDES"); - nummapbrushsides++; - s2 = &b->original_sides[b->numsides]; - s2->planenum = FindFloatPlane (normal, dist); - s2->texinfo = b->original_sides[0].texinfo; -#ifdef SIN - s2->lightinfo = b->original_sides[0].lightinfo; -#endif - s2->contents = b->original_sides[0].contents; - s2->flags |= SFL_BEVEL; - c_edgebevels++; - b->numsides++; - } - } - } - } -} //end of the function AddBrushBevels -//=========================================================================== -// creates windigs for sides and mins / maxs for the brush -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean MakeBrushWindings(mapbrush_t *ob) -{ - int i, j; - winding_t *w; - side_t *side; - plane_t *plane; - - ClearBounds (ob->mins, ob->maxs); - - for (i = 0; i < ob->numsides; i++) - { - plane = &mapplanes[ob->original_sides[i].planenum]; - w = BaseWindingForPlane(plane->normal, plane->dist); - for (j = 0; j numsides && w; j++) - { - if (i == j) continue; - if (ob->original_sides[j].flags & SFL_BEVEL) continue; - plane = &mapplanes[ob->original_sides[j].planenum^1]; - ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); - } - - side = &ob->original_sides[i]; - side->winding = w; - if (w) - { - side->flags |= SFL_VISIBLE; - for (j = 0; j < w->numpoints; j++) - AddPointToBounds (w->p[j], ob->mins, ob->maxs); - } - } - - for (i = 0; i < 3; i++) - { - //IDBUG: all the indexes into the mins and maxs were zero (not using i) - if (ob->mins[i] < -MAX_MAP_BOUNDS || ob->maxs[i] > MAX_MAP_BOUNDS) - { - Log_Print("entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum); - ob->numsides = 0; //remove the brush - break; - } //end if - if (ob->mins[i] > MAX_MAP_BOUNDS || ob->maxs[i] < -MAX_MAP_BOUNDS) - { - Log_Print("entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum); - ob->numsides = 0; //remove the brush - break; - } //end if - } //end for - return true; -} //end of the function MakeBrushWindings -//=========================================================================== -// FIXME: currently doesn't mark all bevels -// NOTE: when one brush bevel is found the remaining sides of the brush -// are bevels as well (when the brush isn't expanded for AAS :)) -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void MarkBrushBevels(mapbrush_t *brush) -{ - int i; - int we; - side_t *s; - - //check all the sides of the brush - for (i = 0; i < brush->numsides; i++) - { - s = brush->original_sides + i; - //if the side has no winding - if (!s->winding) - { - Log_Write("MarkBrushBevels: brush %d no winding", brush->brushnum); - s->flags |= SFL_BEVEL; - } //end if - //if the winding is tiny - else if (WindingIsTiny(s->winding)) - { - s->flags |= SFL_BEVEL; - Log_Write("MarkBrushBevels: brush %d tiny winding", brush->brushnum); - } //end else if - //if the winding has errors - else - { - we = WindingError(s->winding); - if (we == WE_NOTENOUGHPOINTS - || we == WE_SMALLAREA - || we == WE_POINTBOGUSRANGE -// || we == WE_NONCONVEX - ) - { - Log_Write("MarkBrushBevels: brush %d %s", brush->brushnum, WindingErrorString()); - s->flags |= SFL_BEVEL; - } //end else if - } //end else - if (s->flags & SFL_BEVEL) - { - s->flags &= ~SFL_VISIBLE; - //if the side has a valid plane - if (s->planenum > 0 && s->planenum < nummapplanes) - { - //if it is an axial plane - if (mapplanes[s->planenum].type < 3) c_boxbevels++; - else c_edgebevels++; - } //end if - } //end if - } //end for -} //end of the function MarkBrushBevels -//=========================================================================== -// returns true if the map brush already exists -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BrushExists(mapbrush_t *brush) -{ - int i, s1, s2; - side_t *side1, *side2; - mapbrush_t *brush1, *brush2; - - for (i = 0; i < nummapbrushes; i++) - { - brush1 = brush; - brush2 = &mapbrushes[i]; - //compare the brushes - if (brush1->entitynum != brush2->entitynum) continue; - //if (brush1->contents != brush2->contents) continue; - if (brush1->numsides != brush2->numsides) continue; - for (s1 = 0; s1 < brush1->numsides; s1++) - { - side1 = brush1->original_sides + s1; - // - for (s2 = 0; s2 < brush2->numsides; s2++) - { - side2 = brush2->original_sides + s2; - // - if ((side1->planenum & ~1) == (side2->planenum & ~1) -// && side1->texinfo == side2->texinfo -// && side1->contents == side2->contents -// && side1->surf == side2->surf - ) break; - } //end if - if (s2 >= brush2->numsides) break; - } //end for - if (s1 >= brush1->numsides) return true; - } //end for - return false; -} //end of the function BrushExists -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WriteMapBrush(FILE *fp, mapbrush_t *brush, vec3_t origin) -{ - int sn, rotate, shift[2], sv, tv, planenum, p1, i, j; - float scale[2], originshift[2], ang1, ang2, newdist; - vec3_t vecs[2], axis[2]; - map_texinfo_t *ti; - winding_t *w; - side_t *s; - plane_t *plane; - - if (noliquids) - { - if (brush->contents & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) - { - return true; - } //end if - } //end if - //if the brush has no contents - if (!brush->contents) return true; - //print the leading { - if (fprintf(fp, " { //brush %d\n", brush->brushnum) < 0) return false; - //write brush sides - for (sn = 0; sn < brush->numsides; sn++) - { - s = brush->original_sides + sn; - //don't write out bevels - if (!(s->flags & SFL_BEVEL)) - { - //if the entity has an origin set - if (origin[0] || origin[1] || origin[2]) - { - newdist = mapplanes[s->planenum].dist + - DotProduct(mapplanes[s->planenum].normal, origin); - planenum = FindFloatPlane(mapplanes[s->planenum].normal, newdist); - } //end if - else - { - planenum = s->planenum; - } //end else - //always take the first plane, then flip the points if necesary - plane = &mapplanes[planenum & ~1]; - w = BaseWindingForPlane(plane->normal, plane->dist); - // - for (i = 0; i < 3; i++) - { - for (j = 0; j < 3; j++) - { - if (fabs(w->p[i][j]) < 0.2) w->p[i][j] = 0; - else if (fabs((int)w->p[i][j] - w->p[i][j]) < 0.3) w->p[i][j] = (int) w->p[i][j]; - //w->p[i][j] = (int) (w->p[i][j] + 0.2); - } //end for - } //end for - //three non-colinear points to define the plane - if (planenum & 1) p1 = 1; - else p1 = 0; - if (fprintf(fp," ( %5i %5i %5i ) ", (int)w->p[p1][0], (int)w->p[p1][1], (int)w->p[p1][2]) < 0) return false; - if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[!p1][0], (int)w->p[!p1][1], (int)w->p[!p1][2]) < 0) return false; - if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]) < 0) return false; - //free the winding - FreeWinding(w); - // - if (s->texinfo == TEXINFO_NODE) - { - if (brush->contents & CONTENTS_PLAYERCLIP) - { - //player clip - if (loadedmaptype == MAPTYPE_SIN) - { - if (fprintf(fp, "generic/misc/clip 0 0 0 1 1") < 0) return false; - } //end if - else if (loadedmaptype == MAPTYPE_QUAKE2) - { //FIXME: don't always use e1u1 - if (fprintf(fp, "e1u1/clip 0 0 0 1 1") < 0) return false; - } //end else - else if (loadedmaptype == MAPTYPE_QUAKE3) - { - if (fprintf(fp, "e1u1/clip 0 0 0 1 1") < 0) return false; - } //end else if - else - { - if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false; - } //end else - } //end if - else if (brush->contents == CONTENTS_MONSTERCLIP) - { - //monster clip - if (loadedmaptype == MAPTYPE_SIN) - { - if (fprintf(fp, "generic/misc/monster 0 0 0 1 1") < 0) return false; - } //end if - else if (loadedmaptype == MAPTYPE_QUAKE2) - { - if (fprintf(fp, "e1u1/clip_mon 0 0 0 1 1") < 0) return false; - } //end else - else - { - if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false; - } //end else - } //end else - else - { - if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false; - Log_Write("brush->contents = %d\n", brush->contents); - } //end else - } //end if - else if (loadedmaptype == MAPTYPE_SIN && s->texinfo == 0) - { - if (brush->contents & CONTENTS_DUMMYFENCE) - { - if (fprintf(fp, "generic/misc/fence 0 0 0 1 1") < 0) return false; - } //end if - else if (brush->contents & CONTENTS_MIST) - { - if (fprintf(fp, "generic/misc/volumetric_base 0 0 0 1 1") < 0) return false; - } //end if - else //unknown so far - { - if (fprintf(fp, "generic/misc/red 0 0 0 1 1") < 0) return false; - } //end else - } //end if - else if (loadedmaptype == MAPTYPE_QUAKE3) - { - //always use the same texture - if (fprintf(fp, "e2u3/floor1_2 0 0 0 1 1 1 0 0") < 0) return false; - } //end else if - else - { - //* - ti = &map_texinfo[s->texinfo]; - //the scaling of the texture - scale[0] = 1 / VectorNormalize2(ti->vecs[0], vecs[0]); - scale[1] = 1 / VectorNormalize2(ti->vecs[1], vecs[1]); - // - TextureAxisFromPlane(plane, axis[0], axis[1]); - //calculate texture shift done by entity origin - originshift[0] = DotProduct(origin, axis[0]); - originshift[1] = DotProduct(origin, axis[1]); - //the texture shift without origin shift - shift[0] = ti->vecs[0][3] - originshift[0]; - shift[1] = ti->vecs[1][3] - originshift[1]; - // - if (axis[0][0]) sv = 0; - else if (axis[0][1]) sv = 1; - else sv = 2; - if (axis[1][0]) tv = 0; - else if (axis[1][1]) tv = 1; - else tv = 2; - //calculate rotation of texture - if (vecs[0][tv] == 0) ang1 = vecs[0][sv] > 0 ? 90.0 : -90.0; - else ang1 = atan2(vecs[0][sv], vecs[0][tv]) * 180 / Q_PI; - if (ang1 < 0) ang1 += 360; - if (ang1 >= 360) ang1 -= 360; - if (axis[0][tv] == 0) ang2 = axis[0][sv] > 0 ? 90.0 : -90.0; - else ang2 = atan2(axis[0][sv], axis[0][tv]) * 180 / Q_PI; - if (ang2 < 0) ang2 += 360; - if (ang2 >= 360) ang2 -= 360; - rotate = ang2 - ang1; - if (rotate < 0) rotate += 360; - if (rotate >= 360) rotate -= 360; - //write the texture info - if (fprintf(fp, "%s %d %d %d", ti->texture, shift[0], shift[1], rotate) < 0) return false; - if (fabs(scale[0] - ((int) scale[0])) < 0.001) - { - if (fprintf(fp, " %d", (int) scale[0]) < 0) return false; - } //end if - else - { - if (fprintf(fp, " %4f", scale[0]) < 0) return false; - } //end if - if (fabs(scale[1] - ((int) scale[1])) < 0.001) - { - if (fprintf(fp, " %d", (int) scale[1]) < 0) return false; - } //end if - else - { - if (fprintf(fp, " %4f", scale[1]) < 0) return false; - } //end else - //write the extra brush side info - if (loadedmaptype == MAPTYPE_QUAKE2) - { - if (fprintf(fp, " %d %d %d", s->contents, ti->flags, ti->value) < 0) return false; - } //end if - //*/ - } //end else - if (fprintf(fp, "\n") < 0) return false; - } //end if - } //end if - if (fprintf(fp, " }\n") < 0) return false; - c_writtenbrushes++; - return true; -} //end of the function WriteMapBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WriteOriginBrush(FILE *fp, vec3_t origin) -{ - vec3_t normal; - float dist; - int i, s; - winding_t *w; - - if (fprintf(fp, " {\n") < 0) return false; - // - for (i = 0; i < 3; i++) - { - for (s = -1; s <= 1; s += 2) - { - // - VectorClear(normal); - normal[i] = s; - dist = origin[i] * s + 16; - // - w = BaseWindingForPlane(normal, dist); - //three non-colinear points to define the plane - if (fprintf(fp," ( %5i %5i %5i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]) < 0) return false; - if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]) < 0) return false; - if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]) < 0) return false; - //free the winding - FreeWinding(w); - //write origin texture: - // CONTENTS_ORIGIN = 16777216 - // SURF_NODRAW = 128 - if (loadedmaptype == MAPTYPE_SIN) - { - if (fprintf(fp, "generic/misc/origin 0 0 0 1 1") < 0) return false; - } //end if - else if (loadedmaptype == MAPTYPE_HALFLIFE) - { - if (fprintf(fp, "origin 0 0 0 1 1") < 0) return false; - } //end if - else - { - if (fprintf(fp, "e1u1/origin 0 0 0 1 1") < 0) return false; - } //end else - //Quake2 extra brush side info - if (loadedmaptype == MAPTYPE_QUAKE2) - { - //if (fprintf(fp, " 16777216 128 0") < 0) return false; - } //end if - if (fprintf(fp, "\n") < 0) return false; - } //end for - } //end for - if (fprintf(fp, " }\n") < 0) return false; - c_writtenbrushes++; - return true; -} //end of the function WriteOriginBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -mapbrush_t *GetAreaPortalBrush(entity_t *mapent) -{ - int portalnum, bn; - mapbrush_t *brush = NULL; - - //the area portal number - portalnum = mapent->areaportalnum; - //find the area portal brush in the world brushes - for (bn = 0; bn < nummapbrushes && portalnum; bn++) - { - brush = &mapbrushes[bn]; - //must be in world entity - if (brush->entitynum == 0) - { - if (brush->contents & CONTENTS_AREAPORTAL) - { - portalnum--; - } //end if - } //end if - } //end for - if (bn < nummapbrushes) - { - return brush; - } //end if - else - { - Log_Print("area portal %d brush not found\n", mapent->areaportalnum); - return NULL; - } //end else -} //end of the function GetAreaPortalBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WriteMapFileSafe(FILE *fp) -{ - char key[1024], value[1024]; - int i, bn, entitybrushes; - epair_t *ep; - mapbrush_t *brush; - entity_t *mapent; - //vec3_t vec_origin = {0, 0, 0}; - - // - if (fprintf(fp,"//=====================================================\n" - "//\n" - "// map file created with BSPC "BSPC_VERSION"\n" - "//\n" - "// BSPC is designed to decompile material in which you own the copyright\n" - "// or have obtained permission to decompile from the copyright owner. Unless\n" - "// you own the copyright or have permission to decompile from the copyright\n" - "// owner, you may be violating copyright law and be subject to payment of\n" - "// damages and other remedies. If you are uncertain about your rights, contact\n" - "// your legal advisor.\n" - "//\n") < 0) return false; - if (loadedmaptype == MAPTYPE_SIN) - { - if (fprintf(fp, - "// generic/misc/red is used for unknown textures\n") < 0) return false; - } //end if - if (fprintf(fp,"//\n" - "//=====================================================\n") < 0) return false; - //write out all the entities - for (i = 0; i < num_entities; i++) - { - mapent = &entities[i]; - if (!mapent->epairs) - { - continue; - } //end if - if (fprintf(fp, "{\n") < 0) return false; - // - if (loadedmaptype == MAPTYPE_QUAKE3) - { - if (!stricmp(ValueForKey(mapent, "classname"), "light")) - { - SetKeyValue(mapent, "light", "10000"); - } //end if - } //end if - //write epairs - for (ep = mapent->epairs; ep; ep = ep->next) - { - strcpy(key, ep->key); - StripTrailing (key); - strcpy(value, ep->value); - StripTrailing(value); - // - if (loadedmaptype == MAPTYPE_QUAKE2 || - loadedmaptype == MAPTYPE_SIN) - { - //don't write an origin for BSP models - if (mapent->modelnum >= 0 && !strcmp(key, "origin")) continue; - } //end if - //don't write BSP model numbers - if (mapent->modelnum >= 0 && !strcmp(key, "model") && value[0] == '*') continue; - // - if (fprintf(fp, " \"%s\" \"%s\"\n", key, value) < 0) return false; - } //end for - // - if (ValueForKey(mapent, "origin")) GetVectorForKey(mapent, "origin", mapent->origin); - else mapent->origin[0] = mapent->origin[1] = mapent->origin[2] = 0; - //if this is an area portal entity - if (!strcmp("func_areaportal", ValueForKey(mapent, "classname"))) - { - brush = GetAreaPortalBrush(mapent); - if (!brush) return false; - if (!WriteMapBrush(fp, brush, mapent->origin)) return false; - } //end if - else - { - entitybrushes = false; - //write brushes - for (bn = 0; bn < nummapbrushes; bn++) - { - brush = &mapbrushes[bn]; - //if the brush is part of this entity - if (brush->entitynum == i) - { - //don't write out area portal brushes in the world - if (!((brush->contents & CONTENTS_AREAPORTAL) && brush->entitynum == 0)) - { - /* - if (!strcmp("func_door_rotating", ValueForKey(mapent, "classname"))) - { - AAS_PositionFuncRotatingBrush(mapent, brush); - if (!WriteMapBrush(fp, brush, vec_origin)) return false; - } //end if - else //*/ - { - if (!WriteMapBrush(fp, brush, mapent->origin)) return false; - } //end else - entitybrushes = true; - } //end if - } //end if - } //end for - //if the entity had brushes - if (entitybrushes) - { - //if the entity has an origin set - if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2]) - { - if (!WriteOriginBrush(fp, mapent->origin)) return false; - } //end if - } //end if - } //end else - if (fprintf(fp, "}\n") < 0) return false; - } //end for - if (fprintf(fp, "//total of %d brushes\n", c_writtenbrushes) < 0) return false; - return true; -} //end of the function WriteMapFileSafe -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void WriteMapFile(char *filename) -{ - FILE *fp; - double start_time; - - c_writtenbrushes = 0; - //the time started - start_time = I_FloatTime(); - // - Log_Print("writing %s\n", filename); - fp = fopen(filename, "wb"); - if (!fp) - { - Log_Print("can't open %s\n", filename); - return; - } //end if - if (!WriteMapFileSafe(fp)) - { - fclose(fp); - Log_Print("error writing map file %s\n", filename); - return; - } //end if - fclose(fp); - //display creation time - Log_Print("written %d brushes\n", c_writtenbrushes); - Log_Print("map file written in %5.0f seconds\n", I_FloatTime() - start_time); -} //end of the function WriteMapFile -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintMapInfo(void) -{ - Log_Print("\n"); - Log_Print("%6i brushes\n", nummapbrushes); - Log_Print("%6i brush sides\n", nummapbrushsides); -// Log_Print("%6i clipbrushes\n", c_clipbrushes); -// Log_Print("%6i total sides\n", nummapbrushsides); -// Log_Print("%6i boxbevels\n", c_boxbevels); -// Log_Print("%6i edgebevels\n", c_edgebevels); -// Log_Print("%6i entities\n", num_entities); -// Log_Print("%6i planes\n", nummapplanes); -// Log_Print("%6i areaportals\n", c_areaportals); -// Log_Print("%6i squatt brushes\n", c_squattbrushes); -// Log_Print("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2], -// map_maxs[0],map_maxs[1],map_maxs[2]); -} //end of the function PrintMapInfo -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ResetMapLoading(void) -{ - int i; - epair_t *ep, *nextep; - - Q2_ResetMapLoading(); - Sin_ResetMapLoading(); - - //free all map brush side windings - for (i = 0; i < nummapbrushsides; i++) - { - if (brushsides[i].winding) - { - FreeWinding(brushsides[i].winding); - } //end for - } //end for - - //reset regular stuff - nummapbrushes = 0; - memset(mapbrushes, 0, MAX_MAPFILE_BRUSHES * sizeof(mapbrush_t)); - // - nummapbrushsides = 0; - memset(brushsides, 0, MAX_MAPFILE_BRUSHSIDES * sizeof(side_t)); - memset(side_brushtextures, 0, MAX_MAPFILE_BRUSHSIDES * sizeof(brush_texture_t)); - // - nummapplanes = 0; - memset(mapplanes, 0, MAX_MAPFILE_PLANES * sizeof(plane_t)); - // - memset(planehash, 0, PLANE_HASHES * sizeof(plane_t *)); - // - memset(map_texinfo, 0, MAX_MAPFILE_TEXINFO * sizeof(map_texinfo_t)); - map_numtexinfo = 0; - // - VectorClear(map_mins); - VectorClear(map_maxs); - // - c_boxbevels = 0; - c_edgebevels = 0; - c_areaportals = 0; - c_clipbrushes = 0; - c_writtenbrushes = 0; - //clear the entities - for (i = 0; i < num_entities; i++) - { - for (ep = entities[i].epairs; ep; ep = nextep) - { - nextep = ep->next; - FreeMemory(ep->key); - FreeMemory(ep->value); - FreeMemory(ep); - } //end for - } //end for - num_entities = 0; - memset(entities, 0, MAX_MAP_ENTITIES * sizeof(entity_t)); -} //end of the function ResetMapLoading -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifndef Q1_BSPVERSION -#define Q1_BSPVERSION 29 -#endif -#ifndef HL_BSPVERSION -#define HL_BSPVERSION 30 -#endif - -#define Q2_BSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I') //IBSP -#define Q2_BSPVERSION 38 - -#define SINGAME_BSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'R') //RBSP -#define SINGAME_BSPVERSION 1 - -#define SIN_BSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I') //IBSP -#define SIN_BSPVERSION 41 - -typedef struct -{ - int ident; - int version; -} idheader_t; - -int LoadMapFromBSP(struct quakefile_s *qf) -{ - idheader_t idheader; - - if (ReadQuakeFile(qf, &idheader, 0, sizeof(idheader_t)) != sizeof(idheader_t)) - { - return false; - } //end if - - idheader.ident = LittleLong(idheader.ident); - idheader.version = LittleLong(idheader.version); - //QuakeLive BSP file - if (idheader.ident == QL_BSP_IDENT && idheader.version == QL_BSP_VERSION) - { - ResetMapLoading(); - Q3_LoadMapFromBSP(qf); - Q3_FreeMaxBSP(); - } //end if - //Quake3 BSP file - else if (idheader.ident == Q3_BSP_IDENT && idheader.version == Q3_BSP_VERSION) - { - ResetMapLoading(); - Q3_LoadMapFromBSP(qf); - Q3_FreeMaxBSP(); - } //end if - //Quake2 BSP file - else if (idheader.ident == Q2_BSPHEADER && idheader.version == Q2_BSPVERSION) - { - ResetMapLoading(); - Q2_AllocMaxBSP(); - Q2_LoadMapFromBSP(qf->filename, qf->offset, qf->length); - Q2_FreeMaxBSP(); - } //endif - //Sin BSP file - else if ((idheader.ident == SIN_BSPHEADER && idheader.version == SIN_BSPVERSION) || - //the dorks gave the same format another ident and verions - (idheader.ident == SINGAME_BSPHEADER && idheader.version == SINGAME_BSPVERSION)) - { - ResetMapLoading(); - Sin_AllocMaxBSP(); - Sin_LoadMapFromBSP(qf->filename, qf->offset, qf->length); - Sin_FreeMaxBSP(); - } //end if - //the Quake1 bsp files don't have a ident only a version - else if (idheader.ident == Q1_BSPVERSION) - { - ResetMapLoading(); - Q1_AllocMaxBSP(); - Q1_LoadMapFromBSP(qf->filename, qf->offset, qf->length); - Q1_FreeMaxBSP(); - } //end if - //Half-Life also only uses a version number - else if (idheader.ident == HL_BSPVERSION) - { - ResetMapLoading(); - HL_AllocMaxBSP(); - HL_LoadMapFromBSP(qf->filename, qf->offset, qf->length); - HL_FreeMaxBSP(); - } //end if - else - { - Error("unknown BSP format %c%c%c%c, version %d\n", - (idheader.ident & 0xFF), - ((idheader.ident >> 8) & 0xFF), - ((idheader.ident >> 16) & 0xFF), - ((idheader.ident >> 24) & 0xFF), idheader.version); - return false; - } //end if - // - return true; -} //end of the function LoadMapFromBSP diff --git a/tools/quake3/bspc/map_hl.c b/tools/quake3/bspc/map_hl.c deleted file mode 100644 index 88e4fa79..00000000 --- a/tools/quake3/bspc/map_hl.c +++ /dev/null @@ -1,1114 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "l_bsp_hl.h" -#include "aas_map.h" //AAS_CreateMapBrushes - -int hl_numbrushes; -int hl_numclipbrushes; - -//#define HL_PRINT -#define HLCONTENTS - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int HL_TextureContents(char *name) -{ - if (!Q_strncasecmp (name, "sky",3)) - return CONTENTS_SOLID; - - if (!Q_strncasecmp(name+1,"!lava",5)) - return CONTENTS_LAVA; - - if (!Q_strncasecmp(name+1,"!slime",6)) - return CONTENTS_SLIME; - - /* - if (!Q_strncasecmp (name, "!cur_90",7)) - return CONTENTS_CURRENT_90; - if (!Q_strncasecmp (name, "!cur_0",6)) - return CONTENTS_CURRENT_0; - if (!Q_strncasecmp (name, "!cur_270",8)) - return CONTENTS_CURRENT_270; - if (!Q_strncasecmp (name, "!cur_180",8)) - return CONTENTS_CURRENT_180; - if (!Q_strncasecmp (name, "!cur_up",7)) - return CONTENTS_CURRENT_UP; - if (!Q_strncasecmp (name, "!cur_dwn",8)) - return CONTENTS_CURRENT_DOWN; - //*/ - if (name[0] == '!') - return CONTENTS_WATER; - /* - if (!Q_strncasecmp (name, "origin",6)) - return CONTENTS_ORIGIN; - if (!Q_strncasecmp (name, "clip",4)) - return CONTENTS_CLIP; - if( !Q_strncasecmp( name, "translucent", 11 ) ) - return CONTENTS_TRANSLUCENT; - if( name[0] == '@' ) - return CONTENTS_TRANSLUCENT; - //*/ - - return CONTENTS_SOLID; -} //end of the function HL_TextureContents -//=========================================================================== -// Generates two new brushes, leaving the original -// unchanged -// -// modified for Half-Life because there are quite a lot of tiny node leaves -// in the Half-Life bsps -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void HL_SplitBrush(bspbrush_t *brush, int planenum, int nodenum, - bspbrush_t **front, bspbrush_t **back) -{ - bspbrush_t *b[2]; - int i, j; - winding_t *w, *cw[2], *midwinding; - plane_t *plane, *plane2; - side_t *s, *cs; - float d, d_front, d_back; - - *front = *back = NULL; - plane = &mapplanes[planenum]; - - // check all points - d_front = d_back = 0; - for (i=0 ; inumsides ; i++) - { - w = brush->sides[i].winding; - if (!w) - continue; - for (j=0 ; jnumpoints ; j++) - { - d = DotProduct (w->p[j], plane->normal) - plane->dist; - if (d > 0 && d > d_front) - d_front = d; - if (d < 0 && d < d_back) - d_back = d; - } //end for - } //end for - - if (d_front < 0.1) // PLANESIDE_EPSILON) - { // only on back - *back = CopyBrush (brush); - Log_Print("HL_SplitBrush: only on back\n"); - return; - } //end if - if (d_back > -0.1) // PLANESIDE_EPSILON) - { // only on front - *front = CopyBrush (brush); - Log_Print("HL_SplitBrush: only on front\n"); - return; - } //end if - - // create a new winding from the split plane - - w = BaseWindingForPlane (plane->normal, plane->dist); - for (i = 0; i < brush->numsides && w; i++) - { - plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; - ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON); - } //end for - - if (!w || WindingIsTiny(w)) - { // the brush isn't really split - int side; - - Log_Print("HL_SplitBrush: no split winding\n"); - side = BrushMostlyOnSide (brush, plane); - if (side == PSIDE_FRONT) - *front = CopyBrush (brush); - if (side == PSIDE_BACK) - *back = CopyBrush (brush); - return; - } - - if (WindingIsHuge(w)) - { - Log_Print("HL_SplitBrush: WARNING huge split winding\n"); - } //end of - - midwinding = w; - - // split it for real - - for (i = 0; i < 2; i++) - { - b[i] = AllocBrush (brush->numsides+1); - b[i]->original = brush->original; - } //end for - - // split all the current windings - - for (i=0 ; inumsides ; i++) - { - s = &brush->sides[i]; - w = s->winding; - if (!w) - continue; - ClipWindingEpsilon (w, plane->normal, plane->dist, - 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); - for (j=0 ; j<2 ; j++) - { - if (!cw[j]) - continue; -#if 0 - if (WindingIsTiny (cw[j])) - { - FreeWinding (cw[j]); - continue; - } -#endif - cs = &b[j]->sides[b[j]->numsides]; - b[j]->numsides++; - *cs = *s; -// cs->planenum = s->planenum; -// cs->texinfo = s->texinfo; -// cs->visible = s->visible; -// cs->original = s->original; - cs->winding = cw[j]; - cs->flags &= ~SFL_TESTED; - } //end for - } //end for - - - // see if we have valid polygons on both sides - - for (i=0 ; i<2 ; i++) - { - BoundBrush (b[i]); - for (j=0 ; j<3 ; j++) - { - if (b[i]->mins[j] < -4096 || b[i]->maxs[j] > 4096) - { - Log_Print("HL_SplitBrush: bogus brush after clip\n"); - break; - } //end if - } //end for - - if (b[i]->numsides < 3 || j < 3) - { - FreeBrush (b[i]); - b[i] = NULL; - Log_Print("HL_SplitBrush: numsides < 3\n"); - } //end if - } //end for - - if ( !(b[0] && b[1]) ) - { - if (!b[0] && !b[1]) - Log_Print("HL_SplitBrush: split removed brush\n"); - else - Log_Print("HL_SplitBrush: split not on both sides\n"); - if (b[0]) - { - FreeBrush (b[0]); - *front = CopyBrush (brush); - } //end if - if (b[1]) - { - FreeBrush (b[1]); - *back = CopyBrush (brush); - } //end if - return; - } //end if - - // add the midwinding to both sides - for (i = 0; i < 2; i++) - { - cs = &b[i]->sides[b[i]->numsides]; - b[i]->numsides++; - - cs->planenum = planenum^i^1; - cs->texinfo = 0; - //store the node number in the surf to find the texinfo later on - cs->surf = nodenum; - // - cs->flags &= ~SFL_VISIBLE; - cs->flags &= ~SFL_TESTED; - if (i==0) - cs->winding = CopyWinding (midwinding); - else - cs->winding = midwinding; - } //end for - - -{ - vec_t v1; - int i; - - for (i=0 ; i<2 ; i++) - { - v1 = BrushVolume (b[i]); - if (v1 < 1) - { - FreeBrush (b[i]); - b[i] = NULL; - Log_Print("HL_SplitBrush: tiny volume after clip\n"); - } //end if - } //end for -} //*/ - - *front = b[0]; - *back = b[1]; -} //end of the function HL_SplitBrush -//=========================================================================== -// returns true if the tree starting at nodenum has only solid leaves -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int HL_SolidTree_r(int nodenum) -{ - if (nodenum < 0) - { - switch(hl_dleafs[(-nodenum) - 1].contents) - { - case HL_CONTENTS_EMPTY: - { - return false; - } //end case - case HL_CONTENTS_SOLID: -#ifdef HLCONTENTS - case HL_CONTENTS_CLIP: -#endif //HLCONTENTS - case HL_CONTENTS_SKY: -#ifdef HLCONTENTS - case HL_CONTENTS_TRANSLUCENT: -#endif //HLCONTENTS - { - return true; - } //end case - case HL_CONTENTS_WATER: - case HL_CONTENTS_SLIME: - case HL_CONTENTS_LAVA: -#ifdef HLCONTENTS - //these contents should not be found in the BSP - case HL_CONTENTS_ORIGIN: - case HL_CONTENTS_CURRENT_0: - case HL_CONTENTS_CURRENT_90: - case HL_CONTENTS_CURRENT_180: - case HL_CONTENTS_CURRENT_270: - case HL_CONTENTS_CURRENT_UP: - case HL_CONTENTS_CURRENT_DOWN: -#endif //HLCONTENTS - default: - { - return false; - } //end default - } //end switch - return false; - } //end if - if (!HL_SolidTree_r(hl_dnodes[nodenum].children[0])) return false; - if (!HL_SolidTree_r(hl_dnodes[nodenum].children[1])) return false; - return true; -} //end of the function HL_SolidTree_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *HL_CreateBrushes_r(bspbrush_t *brush, int nodenum) -{ - int planenum; - bspbrush_t *front, *back; - hl_dleaf_t *leaf; - - //if it is a leaf - if (nodenum < 0) - { - leaf = &hl_dleafs[(-nodenum) - 1]; - if (leaf->contents != HL_CONTENTS_EMPTY) - { -#ifdef HL_PRINT - qprintf("\r%5i", ++hl_numbrushes); -#endif //HL_PRINT - } //end if - switch(leaf->contents) - { - case HL_CONTENTS_EMPTY: - { - FreeBrush(brush); - return NULL; - } //end case - case HL_CONTENTS_SOLID: -#ifdef HLCONTENTS - case HL_CONTENTS_CLIP: -#endif //HLCONTENTS - case HL_CONTENTS_SKY: -#ifdef HLCONTENTS - case HL_CONTENTS_TRANSLUCENT: -#endif //HLCONTENTS - { - brush->side = CONTENTS_SOLID; - return brush; - } //end case - case HL_CONTENTS_WATER: - { - brush->side = CONTENTS_WATER; - return brush; - } //end case - case HL_CONTENTS_SLIME: - { - brush->side = CONTENTS_SLIME; - return brush; - } //end case - case HL_CONTENTS_LAVA: - { - brush->side = CONTENTS_LAVA; - return brush; - } //end case -#ifdef HLCONTENTS - //these contents should not be found in the BSP - case HL_CONTENTS_ORIGIN: - case HL_CONTENTS_CURRENT_0: - case HL_CONTENTS_CURRENT_90: - case HL_CONTENTS_CURRENT_180: - case HL_CONTENTS_CURRENT_270: - case HL_CONTENTS_CURRENT_UP: - case HL_CONTENTS_CURRENT_DOWN: - { - Error("HL_CreateBrushes_r: found contents %d in Half-Life BSP", leaf->contents); - return NULL; - } //end case -#endif //HLCONTENTS - default: - { - Error("HL_CreateBrushes_r: unknown contents %d in Half-Life BSP", leaf->contents); - return NULL; - } //end default - } //end switch - return NULL; - } //end if - //if the rest of the tree is solid - /*if (HL_SolidTree_r(nodenum)) - { - brush->side = CONTENTS_SOLID; - return brush; - } //end if*/ - // - planenum = hl_dnodes[nodenum].planenum; - planenum = FindFloatPlane(hl_dplanes[planenum].normal, hl_dplanes[planenum].dist); - //split the brush with the node plane - HL_SplitBrush(brush, planenum, nodenum, &front, &back); - //free the original brush - FreeBrush(brush); - //every node must split the brush in two - if (!front || !back) - { - Log_Print("HL_CreateBrushes_r: WARNING node not splitting brush\n"); - //return NULL; - } //end if - //create brushes recursively - if (front) front = HL_CreateBrushes_r(front, hl_dnodes[nodenum].children[0]); - if (back) back = HL_CreateBrushes_r(back, hl_dnodes[nodenum].children[1]); - //link the brushes if possible and return them - if (front) - { - for (brush = front; brush->next; brush = brush->next); - brush->next = back; - return front; - } //end if - else - { - return back; - } //end else -} //end of the function HL_CreateBrushes_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *HL_CreateBrushesFromBSP(int modelnum) -{ - bspbrush_t *brushlist; - bspbrush_t *brush; - hl_dnode_t *headnode; - vec3_t mins, maxs; - int i; - - // - headnode = &hl_dnodes[hl_dmodels[modelnum].headnode[0]]; - //get the mins and maxs of the world - VectorCopy(headnode->mins, mins); - VectorCopy(headnode->maxs, maxs); - //enlarge these mins and maxs - for (i = 0; i < 3; i++) - { - mins[i] -= 8; - maxs[i] += 8; - } //end for - //NOTE: have to add the BSP tree mins and maxs to the MAP mins and maxs - AddPointToBounds(mins, map_mins, map_maxs); - AddPointToBounds(maxs, map_mins, map_maxs); - // - if (!modelnum) - { - Log_Print("brush size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", - map_mins[0], map_mins[1], map_mins[2], - map_maxs[0], map_maxs[1], map_maxs[2]); - } //end if - //create one huge brush containing the whole world - brush = BrushFromBounds(mins, maxs); - VectorCopy(mins, brush->mins); - VectorCopy(maxs, brush->maxs); - // -#ifdef HL_PRINT - qprintf("creating Half-Life brushes\n"); - qprintf("%5d brushes", hl_numbrushes = 0); -#endif //HL_PRINT - //create the brushes - brushlist = HL_CreateBrushes_r(brush, hl_dmodels[modelnum].headnode[0]); - // -#ifdef HL_PRINT - qprintf("\n"); -#endif //HL_PRINT - //now we've got a list with brushes! - return brushlist; -} //end of the function HL_CreateBrushesFromBSP -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *HL_MergeBrushes(bspbrush_t *brushlist, int modelnum) -{ - int nummerges, merged; - bspbrush_t *b1, *b2, *tail, *newbrush, *newbrushlist; - bspbrush_t *lastb2; - - if (!brushlist) return NULL; - - if (!modelnum) qprintf("%5d brushes merged", nummerges = 0); - do - { - for (tail = brushlist; tail; tail = tail->next) - { - if (!tail->next) break; - } //end for - merged = 0; - newbrushlist = NULL; - for (b1 = brushlist; b1; b1 = brushlist) - { - lastb2 = b1; - for (b2 = b1->next; b2; b2 = b2->next) - { - //can't merge brushes with different contents - if (b1->side != b2->side) newbrush = NULL; - else newbrush = TryMergeBrushes(b1, b2); - //if a merged brush is created - if (newbrush) - { - //copy the brush contents - newbrush->side = b1->side; - //add the new brush to the end of the list - tail->next = newbrush; - //remove the second brush from the list - lastb2->next = b2->next; - //remove the first brush from the list - brushlist = brushlist->next; - //free the merged brushes - FreeBrush(b1); - FreeBrush(b2); - //get a new tail brush - for (tail = brushlist; tail; tail = tail->next) - { - if (!tail->next) break; - } //end for - merged++; - if (!modelnum) qprintf("\r%5d", nummerges++); - break; - } //end if - lastb2 = b2; - } //end for - //if b1 can't be merged with any of the other brushes - if (!b2) - { - brushlist = brushlist->next; - //keep b1 - b1->next = newbrushlist; - newbrushlist = b1; - } //end else - } //end for - brushlist = newbrushlist; - } while(merged); - if (!modelnum) qprintf("\n"); - return newbrushlist; -} //end of the function HL_MergeBrushes -//=========================================================================== -// returns the amount the face and the winding have overlap -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float HL_FaceOnWinding(hl_dface_t *face, winding_t *winding) -{ - int i, edgenum, side; - float dist, area; - hl_dplane_t plane; - vec_t *v1, *v2; - vec3_t normal, edgevec; - winding_t *w; - - // - w = CopyWinding(winding); - memcpy(&plane, &hl_dplanes[face->planenum], sizeof(hl_dplane_t)); - //check on which side of the plane the face is - if (face->side) - { - VectorNegate(plane.normal, plane.normal); - plane.dist = -plane.dist; - } //end if - for (i = 0; i < face->numedges && w; i++) - { - //get the first and second vertex of the edge - edgenum = hl_dsurfedges[face->firstedge + i]; - side = edgenum > 0; - //if the face plane is flipped - v1 = hl_dvertexes[hl_dedges[abs(edgenum)].v[side]].point; - v2 = hl_dvertexes[hl_dedges[abs(edgenum)].v[!side]].point; - //create a plane through the edge vector, orthogonal to the face plane - //and with the normal vector pointing out of the face - VectorSubtract(v1, v2, edgevec); - CrossProduct(edgevec, plane.normal, normal); - VectorNormalize(normal); - dist = DotProduct(normal, v1); - // - ChopWindingInPlace(&w, normal, dist, 0.9); //CLIP_EPSILON - } //end for - if (w) - { - area = WindingArea(w); - FreeWinding(w); - return area; - } //end if - return 0; -} //end of the function HL_FaceOnWinding -//=========================================================================== -// returns a list with brushes created by splitting the given brush with -// planes that go through the face edges and are orthogonal to the face plane -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *HL_SplitBrushWithFace(bspbrush_t *brush, hl_dface_t *face) -{ - int i, edgenum, side, planenum, splits; - float dist; - hl_dplane_t plane; - vec_t *v1, *v2; - vec3_t normal, edgevec; - bspbrush_t *front, *back, *brushlist; - - memcpy(&plane, &hl_dplanes[face->planenum], sizeof(hl_dplane_t)); - //check on which side of the plane the face is - if (face->side) - { - VectorNegate(plane.normal, plane.normal); - plane.dist = -plane.dist; - } //end if - splits = 0; - brushlist = NULL; - for (i = 0; i < face->numedges; i++) - { - //get the first and second vertex of the edge - edgenum = hl_dsurfedges[face->firstedge + i]; - side = edgenum > 0; - //if the face plane is flipped - v1 = hl_dvertexes[hl_dedges[abs(edgenum)].v[side]].point; - v2 = hl_dvertexes[hl_dedges[abs(edgenum)].v[!side]].point; - //create a plane through the edge vector, orthogonal to the face plane - //and with the normal vector pointing out of the face - VectorSubtract(v1, v2, edgevec); - CrossProduct(edgevec, plane.normal, normal); - VectorNormalize(normal); - dist = DotProduct(normal, v1); - // - planenum = FindFloatPlane(normal, dist); - //split the current brush - SplitBrush(brush, planenum, &front, &back); - //if there is a back brush just put it in the list - if (back) - { - //copy the brush contents - back->side = brush->side; - // - back->next = brushlist; - brushlist = back; - splits++; - } //end if - if (!front) - { - Log_Print("HL_SplitBrushWithFace: no new brush\n"); - FreeBrushList(brushlist); - return NULL; - } //end if - //copy the brush contents - front->side = brush->side; - //continue splitting the front brush - brush = front; - } //end for - if (!splits) - { - FreeBrush(front); - return NULL; - } //end if - front->next = brushlist; - brushlist = front; - return brushlist; -} //end of the function HL_SplitBrushWithFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *HL_TextureBrushes(bspbrush_t *brushlist, int modelnum) -{ - float area, largestarea; - int i, n, texinfonum, sn, numbrushes, ofs; - int bestfacenum, sidenodenum; - side_t *side; - hl_dmiptexlump_t *miptexlump; - hl_miptex_t *miptex; - bspbrush_t *brush, *nextbrush, *prevbrush, *newbrushes, *brushlistend; - vec_t defaultvec[4] = {1, 0, 0, 0}; - - if (!modelnum) qprintf("texturing brushes\n"); - if (!modelnum) qprintf("%5d brushes", numbrushes = 0); - //get a pointer to the last brush in the list - for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next) - { - if (!brushlistend->next) break; - } //end for - //there's no previous brush when at the start of the list - prevbrush = NULL; - //go over the brush list - for (brush = brushlist; brush; brush = nextbrush) - { - nextbrush = brush->next; - //find a texinfo for every brush side - for (sn = 0; sn < brush->numsides; sn++) - { - side = &brush->sides[sn]; - // - if (side->flags & SFL_TEXTURED) continue; - //number of the node that created this brush side - sidenodenum = side->surf; //see midwinding in HL_SplitBrush - //no face found yet - bestfacenum = -1; - //minimum face size - largestarea = 1; - //if optimizing the texture placement and not going for the - //least number of brushes - if (!lessbrushes) - { - for (i = 0; i < hl_numfaces; i++) - { - //the face must be in the same plane as the node plane that created - //this brush side - if (hl_dfaces[i].planenum == hl_dnodes[sidenodenum].planenum) - { - //get the area the face and the brush side overlap - area = HL_FaceOnWinding(&hl_dfaces[i], side->winding); - //if this face overlaps the brush side winding more than previous faces - if (area > largestarea) - { - //if there already was a face for texturing this brush side with - //a different texture - if (bestfacenum >= 0 && - (hl_dfaces[bestfacenum].texinfo != hl_dfaces[i].texinfo)) - { - //split the brush to fit the texture - newbrushes = HL_SplitBrushWithFace(brush, &hl_dfaces[i]); - //if new brushes where created - if (newbrushes) - { - //remove the current brush from the list - if (prevbrush) prevbrush->next = brush->next; - else brushlist = brush->next; - if (brushlistend == brush) - { - brushlistend = prevbrush; - nextbrush = newbrushes; - } //end if - //add the new brushes to the end of the list - if (brushlistend) brushlistend->next = newbrushes; - else brushlist = newbrushes; - //free the current brush - FreeBrush(brush); - //don't forget about the prevbrush pointer at the bottom of - //the outer loop - brush = prevbrush; - //find the end of the list - for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next) - { - if (!brushlistend->next) break; - } //end for - break; - } //end if - else - { - Log_Write("brush %d: no real texture split", numbrushes); - } //end else - } //end if - else - { - //best face for texturing this brush side - bestfacenum = i; - } //end else - } //end if - } //end if - } //end for - //if the brush was split the original brush is removed - //and we just continue with the next one in the list - if (i < hl_numfaces) break; - } //end if - else - { - //find the face with the largest overlap with this brush side - //for texturing the brush side - for (i = 0; i < hl_numfaces; i++) - { - //the face must be in the same plane as the node plane that created - //this brush side - if (hl_dfaces[i].planenum == hl_dnodes[sidenodenum].planenum) - { - //get the area the face and the brush side overlap - area = HL_FaceOnWinding(&hl_dfaces[i], side->winding); - //if this face overlaps the brush side winding more than previous faces - if (area > largestarea) - { - largestarea = area; - bestfacenum = i; - } //end if - } //end if - } //end for - } //end else - //if a face was found for texturing this brush side - if (bestfacenum >= 0) - { - //set the MAP texinfo values - texinfonum = hl_dfaces[bestfacenum].texinfo; - for (n = 0; n < 4; n++) - { - map_texinfo[texinfonum].vecs[0][n] = hl_texinfo[texinfonum].vecs[0][n]; - map_texinfo[texinfonum].vecs[1][n] = hl_texinfo[texinfonum].vecs[1][n]; - } //end for - //make sure the two vectors aren't of zero length otherwise use the default - //vector to prevent a divide by zero in the map writing - if (VectorLength(map_texinfo[texinfonum].vecs[0]) < 0.01) - memcpy(map_texinfo[texinfonum].vecs[0], defaultvec, sizeof(defaultvec)); - if (VectorLength(map_texinfo[texinfonum].vecs[1]) < 0.01) - memcpy(map_texinfo[texinfonum].vecs[1], defaultvec, sizeof(defaultvec)); - // - map_texinfo[texinfonum].flags = hl_texinfo[texinfonum].flags; - map_texinfo[texinfonum].value = 0; //HL_ and HL texinfos don't have a value - //the mip texture - miptexlump = (hl_dmiptexlump_t *) hl_dtexdata; - ofs = miptexlump->dataofs[hl_texinfo[texinfonum].miptex]; - if ( ofs > hl_texdatasize ) { - ofs = miptexlump->dataofs[0]; - } - miptex = (hl_miptex_t *)((byte *)miptexlump + ofs ); - //get the mip texture name - strcpy(map_texinfo[texinfonum].texture, miptex->name); - //no animations in Quake1 and Half-Life mip textures - map_texinfo[texinfonum].nexttexinfo = -1; - //store the texinfo number - side->texinfo = texinfonum; - // - if (texinfonum > map_numtexinfo) map_numtexinfo = texinfonum; - //this side is textured - side->flags |= SFL_TEXTURED; - } //end if - else - { - //no texture for this side - side->texinfo = TEXINFO_NODE; - //this side is textured - side->flags |= SFL_TEXTURED; - } //end if - } //end for - // - if (!modelnum && prevbrush != brush) qprintf("\r%5d", ++numbrushes); - //previous brush in the list - prevbrush = brush; - } //end for - if (!modelnum) qprintf("\n"); - //return the new list with brushes - return brushlist; -} //end of the function HL_TextureBrushes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void HL_FixContentsTextures(bspbrush_t *brushlist) -{ - int i, texinfonum; - bspbrush_t *brush; - - for (brush = brushlist; brush; brush = brush->next) - { - //only fix the textures of water, slime and lava brushes - if (brush->side != CONTENTS_WATER && - brush->side != CONTENTS_SLIME && - brush->side != CONTENTS_LAVA) continue; - // - for (i = 0; i < brush->numsides; i++) - { - texinfonum = brush->sides[i].texinfo; - if (HL_TextureContents(map_texinfo[texinfonum].texture) == brush->side) break; - } //end for - //if no specific contents texture was found - if (i >= brush->numsides) - { - texinfonum = -1; - for (i = 0; i < map_numtexinfo; i++) - { - if (HL_TextureContents(map_texinfo[i].texture) == brush->side) - { - texinfonum = i; - break; - } //end if - } //end for - } //end if - // - if (texinfonum >= 0) - { - //give all the brush sides this contents texture - for (i = 0; i < brush->numsides; i++) - { - brush->sides[i].texinfo = texinfonum; - } //end for - } //end if - else Log_Print("brush contents %d with wrong textures\n", brush->side); - // - } //end for - /* - for (brush = brushlist; brush; brush = brush->next) - { - //give all the brush sides this contents texture - for (i = 0; i < brush->numsides; i++) - { - if (HL_TextureContents(map_texinfo[texinfonum].texture) != brush->side) - { - Error("brush contents %d with wrong contents textures %s\n", brush->side, - HL_TextureContents(map_texinfo[texinfonum].texture)); - } //end if - } //end for - } //end for*/ -} //end of the function HL_FixContentsTextures -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void HL_BSPBrushToMapBrush(bspbrush_t *bspbrush, entity_t *mapent) -{ - mapbrush_t *mapbrush; - side_t *side; - int i, besttexinfo; - - if (nummapbrushes >= MAX_MAPFILE_BRUSHES) - Error ("nummapbrushes == MAX_MAPFILE_BRUSHES"); - - mapbrush = &mapbrushes[nummapbrushes]; - mapbrush->original_sides = &brushsides[nummapbrushsides]; - mapbrush->entitynum = mapent - entities; - mapbrush->brushnum = nummapbrushes - mapent->firstbrush; - mapbrush->leafnum = -1; - mapbrush->numsides = 0; - - besttexinfo = TEXINFO_NODE; - for (i = 0; i < bspbrush->numsides; i++) - { - if (!bspbrush->sides[i].winding) continue; - // - if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES) - Error ("MAX_MAPFILE_BRUSHSIDES"); - side = &brushsides[nummapbrushsides]; - //the contents of the bsp brush is stored in the side variable - side->contents = bspbrush->side; - side->surf = 0; - side->planenum = bspbrush->sides[i].planenum; - side->texinfo = bspbrush->sides[i].texinfo; - if (side->texinfo != TEXINFO_NODE) - { - //this brush side is textured - side->flags |= SFL_TEXTURED; - besttexinfo = side->texinfo; - } //end if - // - nummapbrushsides++; - mapbrush->numsides++; - } //end for - // - if (besttexinfo == TEXINFO_NODE) - { - mapbrush->numsides = 0; - hl_numclipbrushes++; - return; - } //end if - //set the texinfo for all the brush sides without texture - for (i = 0; i < mapbrush->numsides; i++) - { - if (mapbrush->original_sides[i].texinfo == TEXINFO_NODE) - { - mapbrush->original_sides[i].texinfo = besttexinfo; - } //end if - } //end for - //contents of the brush - mapbrush->contents = bspbrush->side; - // - if (create_aas) - { - //create the AAS brushes from this brush, add brush bevels - AAS_CreateMapBrushes(mapbrush, mapent, true); - return; - } //end if - //create windings for sides and bounds for brush - MakeBrushWindings(mapbrush); - //add brush bevels - AddBrushBevels(mapbrush); - //a new brush has been created - nummapbrushes++; - mapent->numbrushes++; -} //end of the function HL_BSPBrushToMapBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void HL_CreateMapBrushes(entity_t *mapent, int modelnum) -{ - bspbrush_t *brushlist, *brush, *nextbrush; - int i; - - //create brushes from the model BSP tree - brushlist = HL_CreateBrushesFromBSP(modelnum); - //texture the brushes and split them when necesary - brushlist = HL_TextureBrushes(brushlist, modelnum); - //fix the contents textures of all brushes - HL_FixContentsTextures(brushlist); - // - if (!nobrushmerge) - { - brushlist = HL_MergeBrushes(brushlist, modelnum); - //brushlist = HL_MergeBrushes(brushlist, modelnum); - } //end if - // - if (!modelnum) qprintf("converting brushes to map brushes\n"); - if (!modelnum) qprintf("%5d brushes", i = 0); - for (brush = brushlist; brush; brush = nextbrush) - { - nextbrush = brush->next; - HL_BSPBrushToMapBrush(brush, mapent); - brush->next = NULL; - FreeBrush(brush); - if (!modelnum) qprintf("\r%5d", ++i); - } //end for - if (!modelnum) qprintf("\n"); -} //end of the function HL_CreateMapBrushes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void HL_ResetMapLoading(void) -{ -} //end of the function HL_ResetMapLoading -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void HL_LoadMapFromBSP(char *filename, int offset, int length) -{ - int i, modelnum; - char *model, *classname; - - Log_Print("-- HL_LoadMapFromBSP --\n"); - //loaded map type - loadedmaptype = MAPTYPE_HALFLIFE; - // - qprintf("loading map from %s at %d\n", filename, offset); - //load the Half-Life BSP file - HL_LoadBSPFile(filename, offset, length); - // - hl_numclipbrushes = 0; - //parse the entities from the BSP - HL_ParseEntities(); - //clear the map mins and maxs - ClearBounds(map_mins, map_maxs); - // - qprintf("creating Half-Life brushes\n"); - if (lessbrushes) qprintf("creating minimum number of brushes\n"); - else qprintf("placing textures correctly\n"); - // - for (i = 0; i < num_entities; i++) - { - entities[i].firstbrush = nummapbrushes; - entities[i].numbrushes = 0; - // - classname = ValueForKey(&entities[i], "classname"); - if (classname && !strcmp(classname, "worldspawn")) - { - modelnum = 0; - } //end if - else - { - // - model = ValueForKey(&entities[i], "model"); - if (!model || *model != '*') continue; - model++; - modelnum = atoi(model); - } //end else - //create map brushes for the entity - HL_CreateMapBrushes(&entities[i], modelnum); - } //end for - // - qprintf("%5d map brushes\n", nummapbrushes); - qprintf("%5d clip brushes\n", hl_numclipbrushes); -} //end of the function HL_LoadMapFromBSP diff --git a/tools/quake3/bspc/map_q1.c b/tools/quake3/bspc/map_q1.c deleted file mode 100644 index ede944db..00000000 --- a/tools/quake3/bspc/map_q1.c +++ /dev/null @@ -1,1174 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "l_bsp_q1.h" -#include "aas_map.h" //AAS_CreateMapBrushes - -int q1_numbrushes; -int q1_numclipbrushes; - -//#define Q1_PRINT - -//=========================================================================== -// water, slime and lava brush textures names always start with a * -// followed by the type: "slime", "lava" or otherwise water -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Q1_TextureContents(char *name) -{ - if (!Q_strcasecmp(name, "clip")) return CONTENTS_SOLID; - if (name[0] == '*') - { - if (!Q_strncasecmp(name+1,"lava",4)) return CONTENTS_LAVA; - else if (!Q_strncasecmp(name+1,"slime",5)) return CONTENTS_SLIME; - else return CONTENTS_WATER; - } //end if - else if (!Q_strncasecmp(name, "sky", 3)) return CONTENTS_SOLID; - else return CONTENTS_SOLID; -} //end of the function Q1_TextureContents -//=========================================================================== -// Generates two new brushes, leaving the original -// unchanged -// -// modified for Half-Life because there are quite a lot of tiny node leaves -// in the Half-Life bsps -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q1_SplitBrush(bspbrush_t *brush, int planenum, int nodenum, - bspbrush_t **front, bspbrush_t **back) -{ - bspbrush_t *b[2]; - int i, j; - winding_t *w, *cw[2], *midwinding; - plane_t *plane, *plane2; - side_t *s, *cs; - float d, d_front, d_back; - - *front = *back = NULL; - plane = &mapplanes[planenum]; - - // check all points - d_front = d_back = 0; - for (i=0 ; inumsides ; i++) - { - w = brush->sides[i].winding; - if (!w) - continue; - for (j=0 ; jnumpoints ; j++) - { - d = DotProduct (w->p[j], plane->normal) - plane->dist; - if (d > 0 && d > d_front) - d_front = d; - if (d < 0 && d < d_back) - d_back = d; - } //end for - } //end for - - if (d_front < 0.1) // PLANESIDE_EPSILON) - { // only on back - *back = CopyBrush (brush); - Log_Print("Q1_SplitBrush: only on back\n"); - return; - } //end if - if (d_back > -0.1) // PLANESIDE_EPSILON) - { // only on front - *front = CopyBrush (brush); - Log_Print("Q1_SplitBrush: only on front\n"); - return; - } //end if - - // create a new winding from the split plane - - w = BaseWindingForPlane (plane->normal, plane->dist); - for (i = 0; i < brush->numsides && w; i++) - { - plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; - ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON); - } //end for - - if (!w || WindingIsTiny(w)) - { // the brush isn't really split - int side; - - Log_Print("Q1_SplitBrush: no split winding\n"); - side = BrushMostlyOnSide (brush, plane); - if (side == PSIDE_FRONT) - *front = CopyBrush (brush); - if (side == PSIDE_BACK) - *back = CopyBrush (brush); - return; - } - - if (WindingIsHuge(w)) - { - Log_Print("Q1_SplitBrush: WARNING huge split winding\n"); - } //end of - - midwinding = w; - - // split it for real - - for (i = 0; i < 2; i++) - { - b[i] = AllocBrush (brush->numsides+1); - b[i]->original = brush->original; - } //end for - - // split all the current windings - - for (i=0 ; inumsides ; i++) - { - s = &brush->sides[i]; - w = s->winding; - if (!w) - continue; - ClipWindingEpsilon (w, plane->normal, plane->dist, - 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); - for (j=0 ; j<2 ; j++) - { - if (!cw[j]) - continue; -#if 0 - if (WindingIsTiny (cw[j])) - { - FreeWinding (cw[j]); - continue; - } -#endif - cs = &b[j]->sides[b[j]->numsides]; - b[j]->numsides++; - *cs = *s; -// cs->planenum = s->planenum; -// cs->texinfo = s->texinfo; -// cs->visible = s->visible; -// cs->original = s->original; - cs->winding = cw[j]; - cs->flags &= ~SFL_TESTED; - } //end for - } //end for - - - // see if we have valid polygons on both sides - - for (i=0 ; i<2 ; i++) - { - BoundBrush (b[i]); - for (j=0 ; j<3 ; j++) - { - if (b[i]->mins[j] < -4096 || b[i]->maxs[j] > 4096) - { - Log_Print("Q1_SplitBrush: bogus brush after clip\n"); - break; - } //end if - } //end for - - if (b[i]->numsides < 3 || j < 3) - { - FreeBrush (b[i]); - b[i] = NULL; - Log_Print("Q1_SplitBrush: numsides < 3\n"); - } //end if - } //end for - - if ( !(b[0] && b[1]) ) - { - if (!b[0] && !b[1]) - Log_Print("Q1_SplitBrush: split removed brush\n"); - else - Log_Print("Q1_SplitBrush: split not on both sides\n"); - if (b[0]) - { - FreeBrush (b[0]); - *front = CopyBrush (brush); - } //end if - if (b[1]) - { - FreeBrush (b[1]); - *back = CopyBrush (brush); - } //end if - return; - } //end if - - // add the midwinding to both sides - for (i = 0; i < 2; i++) - { - cs = &b[i]->sides[b[i]->numsides]; - b[i]->numsides++; - - cs->planenum = planenum^i^1; - cs->texinfo = 0; - //store the node number in the surf to find the texinfo later on - cs->surf = nodenum; - // - cs->flags &= ~SFL_VISIBLE; - cs->flags &= ~SFL_TESTED; - cs->flags &= ~SFL_TEXTURED; - if (i==0) - cs->winding = CopyWinding (midwinding); - else - cs->winding = midwinding; - } //end for - - -{ - vec_t v1; - int i; - - for (i=0 ; i<2 ; i++) - { - v1 = BrushVolume (b[i]); - if (v1 < 1) - { - FreeBrush (b[i]); - b[i] = NULL; - Log_Print("Q1_SplitBrush: tiny volume after clip\n"); - } //end if - } //end for -} //*/ - - *front = b[0]; - *back = b[1]; -} //end of the function Q1_SplitBrush -//=========================================================================== -// returns true if the tree starting at nodenum has only solid leaves -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Q1_SolidTree_r(int nodenum) -{ - if (nodenum < 0) - { - switch(q1_dleafs[(-nodenum) - 1].contents) - { - case Q1_CONTENTS_EMPTY: - { - return false; - } //end case - case Q1_CONTENTS_SOLID: -#ifdef HLCONTENTS - case Q1_CONTENTS_CLIP: -#endif - case Q1_CONTENTS_SKY: -#ifdef HLCONTENTS - case Q1_CONTENTS_TRANSLUCENT: -#endif - { - return true; - } //end case - case Q1_CONTENTS_WATER: - case Q1_CONTENTS_SLIME: - case Q1_CONTENTS_LAVA: -#ifdef HLCONTENTS - //these contents should not be found in the BSP - case Q1_CONTENTS_ORIGIN: - case Q1_CONTENTS_CURRENT_0: - case Q1_CONTENTS_CURRENT_90: - case Q1_CONTENTS_CURRENT_180: - case Q1_CONTENTS_CURRENT_270: - case Q1_CONTENTS_CURRENT_UP: - case Q1_CONTENTS_CURRENT_DOWN: -#endif - default: - { - return false; - } //end default - } //end switch - return false; - } //end if - if (!Q1_SolidTree_r(q1_dnodes[nodenum].children[0])) return false; - if (!Q1_SolidTree_r(q1_dnodes[nodenum].children[1])) return false; - return true; -} //end of the function Q1_SolidTree_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *Q1_CreateBrushes_r(bspbrush_t *brush, int nodenum) -{ - int planenum; - bspbrush_t *front, *back; - q1_dleaf_t *leaf; - - //if it is a leaf - if (nodenum < 0) - { - leaf = &q1_dleafs[(-nodenum) - 1]; - if (leaf->contents != Q1_CONTENTS_EMPTY) - { -#ifdef Q1_PRINT - qprintf("\r%5i", ++q1_numbrushes); -#endif //Q1_PRINT - } //end if - switch(leaf->contents) - { - case Q1_CONTENTS_EMPTY: - { - FreeBrush(brush); - return NULL; - } //end case - case Q1_CONTENTS_SOLID: -#ifdef HLCONTENTS - case Q1_CONTENTS_CLIP: -#endif - case Q1_CONTENTS_SKY: -#ifdef HLCONTENTS - case Q1_CONTENTS_TRANSLUCENT: -#endif - { - brush->side = CONTENTS_SOLID; - return brush; - } //end case - case Q1_CONTENTS_WATER: - { - brush->side = CONTENTS_WATER; - return brush; - } //end case - case Q1_CONTENTS_SLIME: - { - brush->side = CONTENTS_SLIME; - return brush; - } //end case - case Q1_CONTENTS_LAVA: - { - brush->side = CONTENTS_LAVA; - return brush; - } //end case -#ifdef HLCONTENTS - //these contents should not be found in the BSP - case Q1_CONTENTS_ORIGIN: - case Q1_CONTENTS_CURRENT_0: - case Q1_CONTENTS_CURRENT_90: - case Q1_CONTENTS_CURRENT_180: - case Q1_CONTENTS_CURRENT_270: - case Q1_CONTENTS_CURRENT_UP: - case Q1_CONTENTS_CURRENT_DOWN: - { - Error("Q1_CreateBrushes_r: found contents %d in Half-Life BSP", leaf->contents); - return NULL; - } //end case -#endif - default: - { - Error("Q1_CreateBrushes_r: unknown contents %d in Half-Life BSP", leaf->contents); - return NULL; - } //end default - } //end switch - return NULL; - } //end if - //if the rest of the tree is solid - /*if (Q1_SolidTree_r(nodenum)) - { - brush->side = CONTENTS_SOLID; - return brush; - } //end if*/ - // - planenum = q1_dnodes[nodenum].planenum; - planenum = FindFloatPlane(q1_dplanes[planenum].normal, q1_dplanes[planenum].dist); - //split the brush with the node plane - Q1_SplitBrush(brush, planenum, nodenum, &front, &back); - //free the original brush - FreeBrush(brush); - //every node must split the brush in two - if (!front || !back) - { - Log_Print("Q1_CreateBrushes_r: WARNING node not splitting brush\n"); - //return NULL; - } //end if - //create brushes recursively - if (front) front = Q1_CreateBrushes_r(front, q1_dnodes[nodenum].children[0]); - if (back) back = Q1_CreateBrushes_r(back, q1_dnodes[nodenum].children[1]); - //link the brushes if possible and return them - if (front) - { - for (brush = front; brush->next; brush = brush->next); - brush->next = back; - return front; - } //end if - else - { - return back; - } //end else -} //end of the function Q1_CreateBrushes_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *Q1_CreateBrushesFromBSP(int modelnum) -{ - bspbrush_t *brushlist; - bspbrush_t *brush; - q1_dnode_t *headnode; - vec3_t mins, maxs; - int i; - - // - headnode = &q1_dnodes[q1_dmodels[modelnum].headnode[0]]; - //get the mins and maxs of the world - VectorCopy(headnode->mins, mins); - VectorCopy(headnode->maxs, maxs); - //enlarge these mins and maxs - for (i = 0; i < 3; i++) - { - mins[i] -= 8; - maxs[i] += 8; - } //end for - //NOTE: have to add the BSP tree mins and maxs to the MAP mins and maxs - AddPointToBounds(mins, map_mins, map_maxs); - AddPointToBounds(maxs, map_mins, map_maxs); - // - if (!modelnum) - { - Log_Print("brush size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", - map_mins[0], map_mins[1], map_mins[2], - map_maxs[0], map_maxs[1], map_maxs[2]); - } //end if - //create one huge brush containing the whole world - brush = BrushFromBounds(mins, maxs); - VectorCopy(mins, brush->mins); - VectorCopy(maxs, brush->maxs); - // -#ifdef Q1_PRINT - qprintf("creating Quake brushes\n"); - qprintf("%5d brushes", q1_numbrushes = 0); -#endif //Q1_PRINT - //create the brushes - brushlist = Q1_CreateBrushes_r(brush, q1_dmodels[modelnum].headnode[0]); - // -#ifdef Q1_PRINT - qprintf("\n"); -#endif //Q1_PRINT - //now we've got a list with brushes! - return brushlist; -} //end of the function Q1_CreateBrushesFromBSP -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -q1_dleaf_t *Q1_PointInLeaf(int startnode, vec3_t point) -{ - int nodenum; - vec_t dist; - q1_dnode_t *node; - q1_dplane_t *plane; - - nodenum = startnode; - while (nodenum >= 0) - { - node = &q1_dnodes[nodenum]; - plane = &q1_dplanes[node->planenum]; - dist = DotProduct(point, plane->normal) - plane->dist; - if (dist > 0) - nodenum = node->children[0]; - else - nodenum = node->children[1]; - } //end while - - return &q1_dleafs[-nodenum - 1]; -} //end of the function Q1_PointInLeaf -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float Q1_FaceArea(q1_dface_t *face) -{ - int i; - float total; - vec_t *v; - vec3_t d1, d2, cross; - q1_dedge_t *edge; - - edge = &q1_dedges[face->firstedge]; - v = q1_dvertexes[edge->v[0]].point; - - total = 0; - for (i = 1; i < face->numedges - 1; i++) - { - edge = &q1_dedges[face->firstedge + i]; - VectorSubtract(q1_dvertexes[edge->v[0]].point, v, d1); - VectorSubtract(q1_dvertexes[edge->v[1]].point, v, d2); - CrossProduct(d1, d2, cross); - total += 0.5 * VectorLength(cross); - } //end for - return total; -} //end of the function AAS_FaceArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q1_FacePlane(q1_dface_t *face, vec3_t normal, float *dist) -{ - vec_t *v1, *v2, *v3; - vec3_t vec1, vec2; - int side, edgenum; - - edgenum = q1_dsurfedges[face->firstedge]; - side = edgenum < 0; - v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point; - v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point; - edgenum = q1_dsurfedges[face->firstedge+1]; - side = edgenum < 0; - v3 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point; - // - VectorSubtract(v2, v1, vec1); - VectorSubtract(v3, v1, vec2); - - CrossProduct(vec1, vec2, normal); - VectorNormalize(normal); - *dist = DotProduct(v1, normal); -} //end of the function Q1_FacePlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *Q1_MergeBrushes(bspbrush_t *brushlist, int modelnum) -{ - int nummerges, merged; - bspbrush_t *b1, *b2, *tail, *newbrush, *newbrushlist; - bspbrush_t *lastb2; - - if (!brushlist) return NULL; - - if (!modelnum) qprintf("%5d brushes merged", nummerges = 0); - do - { - for (tail = brushlist; tail; tail = tail->next) - { - if (!tail->next) break; - } //end for - merged = 0; - newbrushlist = NULL; - for (b1 = brushlist; b1; b1 = brushlist) - { - lastb2 = b1; - for (b2 = b1->next; b2; b2 = b2->next) - { - //can't merge brushes with different contents - if (b1->side != b2->side) newbrush = NULL; - else newbrush = TryMergeBrushes(b1, b2); - //if a merged brush is created - if (newbrush) - { - //copy the brush contents - newbrush->side = b1->side; - //add the new brush to the end of the list - tail->next = newbrush; - //remove the second brush from the list - lastb2->next = b2->next; - //remove the first brush from the list - brushlist = brushlist->next; - //free the merged brushes - FreeBrush(b1); - FreeBrush(b2); - //get a new tail brush - for (tail = brushlist; tail; tail = tail->next) - { - if (!tail->next) break; - } //end for - merged++; - if (!modelnum) qprintf("\r%5d", nummerges++); - break; - } //end if - lastb2 = b2; - } //end for - //if b1 can't be merged with any of the other brushes - if (!b2) - { - brushlist = brushlist->next; - //keep b1 - b1->next = newbrushlist; - newbrushlist = b1; - } //end else - } //end for - brushlist = newbrushlist; - } while(merged); - if (!modelnum) qprintf("\n"); - return newbrushlist; -} //end of the function Q1_MergeBrushes -//=========================================================================== -// returns the amount the face and the winding overlap -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float Q1_FaceOnWinding(q1_dface_t *face, winding_t *winding) -{ - int i, edgenum, side; - float dist, area; - q1_dplane_t plane; - vec_t *v1, *v2; - vec3_t normal, edgevec; - winding_t *w; - - // - w = CopyWinding(winding); - memcpy(&plane, &q1_dplanes[face->planenum], sizeof(q1_dplane_t)); - //check on which side of the plane the face is - if (face->side) - { - VectorNegate(plane.normal, plane.normal); - plane.dist = -plane.dist; - } //end if - for (i = 0; i < face->numedges && w; i++) - { - //get the first and second vertex of the edge - edgenum = q1_dsurfedges[face->firstedge + i]; - side = edgenum > 0; - //if the face plane is flipped - v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point; - v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point; - //create a plane through the edge vector, orthogonal to the face plane - //and with the normal vector pointing out of the face - VectorSubtract(v1, v2, edgevec); - CrossProduct(edgevec, plane.normal, normal); - VectorNormalize(normal); - dist = DotProduct(normal, v1); - // - ChopWindingInPlace(&w, normal, dist, 0.9); //CLIP_EPSILON - } //end for - if (w) - { - area = WindingArea(w); - FreeWinding(w); - return area; - } //end if - return 0; -} //end of the function Q1_FaceOnWinding -//=========================================================================== -// returns a list with brushes created by splitting the given brush with -// planes that go through the face edges and are orthogonal to the face plane -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *Q1_SplitBrushWithFace(bspbrush_t *brush, q1_dface_t *face) -{ - int i, edgenum, side, planenum, splits; - float dist; - q1_dplane_t plane; - vec_t *v1, *v2; - vec3_t normal, edgevec; - bspbrush_t *front, *back, *brushlist; - - memcpy(&plane, &q1_dplanes[face->planenum], sizeof(q1_dplane_t)); - //check on which side of the plane the face is - if (face->side) - { - VectorNegate(plane.normal, plane.normal); - plane.dist = -plane.dist; - } //end if - splits = 0; - brushlist = NULL; - for (i = 0; i < face->numedges; i++) - { - //get the first and second vertex of the edge - edgenum = q1_dsurfedges[face->firstedge + i]; - side = edgenum > 0; - //if the face plane is flipped - v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point; - v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point; - //create a plane through the edge vector, orthogonal to the face plane - //and with the normal vector pointing out of the face - VectorSubtract(v1, v2, edgevec); - CrossProduct(edgevec, plane.normal, normal); - VectorNormalize(normal); - dist = DotProduct(normal, v1); - // - planenum = FindFloatPlane(normal, dist); - //split the current brush - SplitBrush(brush, planenum, &front, &back); - //if there is a back brush just put it in the list - if (back) - { - //copy the brush contents - back->side = brush->side; - // - back->next = brushlist; - brushlist = back; - splits++; - } //end if - if (!front) - { - Log_Print("Q1_SplitBrushWithFace: no new brush\n"); - FreeBrushList(brushlist); - return NULL; - } //end if - //copy the brush contents - front->side = brush->side; - //continue splitting the front brush - brush = front; - } //end for - if (!splits) - { - FreeBrush(front); - return NULL; - } //end if - front->next = brushlist; - brushlist = front; - return brushlist; -} //end of the function Q1_SplitBrushWithFace -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *Q1_TextureBrushes(bspbrush_t *brushlist, int modelnum) -{ - float area, largestarea; - int i, n, texinfonum, sn, numbrushes, ofs; - int bestfacenum, sidenodenum; - side_t *side; - q1_dmiptexlump_t *miptexlump; - q1_miptex_t *miptex; - bspbrush_t *brush, *nextbrush, *prevbrush, *newbrushes, *brushlistend; - vec_t defaultvec[4] = {1, 0, 0, 0}; - - if (!modelnum) qprintf("texturing brushes\n"); - if (!modelnum) qprintf("%5d brushes", numbrushes = 0); - //get a pointer to the last brush in the list - for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next) - { - if (!brushlistend->next) break; - } //end for - //there's no previous brush when at the start of the list - prevbrush = NULL; - //go over the brush list - for (brush = brushlist; brush; brush = nextbrush) - { - nextbrush = brush->next; - //find a texinfo for every brush side - for (sn = 0; sn < brush->numsides; sn++) - { - side = &brush->sides[sn]; - // - if (side->flags & SFL_TEXTURED) continue; - //number of the node that created this brush side - sidenodenum = side->surf; //see midwinding in Q1_SplitBrush - //no face found yet - bestfacenum = -1; - //minimum face size - largestarea = 1; - //if optimizing the texture placement and not going for the - //least number of brushes - if (!lessbrushes) - { - for (i = 0; i < q1_numfaces; i++) - { - //the face must be in the same plane as the node plane that created - //this brush side - if (q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum) - { - //get the area the face and the brush side overlap - area = Q1_FaceOnWinding(&q1_dfaces[i], side->winding); - //if this face overlaps the brush side winding more than previous faces - if (area > largestarea) - { - //if there already was a face for texturing this brush side with - //a different texture - if (bestfacenum >= 0 && - (q1_dfaces[bestfacenum].texinfo != q1_dfaces[i].texinfo)) - { - //split the brush to fit the texture - newbrushes = Q1_SplitBrushWithFace(brush, &q1_dfaces[i]); - //if new brushes where created - if (newbrushes) - { - //remove the current brush from the list - if (prevbrush) prevbrush->next = brush->next; - else brushlist = brush->next; - if (brushlistend == brush) - { - brushlistend = prevbrush; - nextbrush = newbrushes; - } //end if - //add the new brushes to the end of the list - if (brushlistend) brushlistend->next = newbrushes; - else brushlist = newbrushes; - //free the current brush - FreeBrush(brush); - //don't forget about the prevbrush pointer at the bottom of - //the outer loop - brush = prevbrush; - //find the end of the list - for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next) - { - if (!brushlistend->next) break; - } //end for - break; - } //end if - else - { - Log_Write("brush %d: no real texture split", numbrushes); - } //end else - } //end if - else - { - //best face for texturing this brush side - bestfacenum = i; - } //end else - } //end if - } //end if - } //end for - //if the brush was split the original brush is removed - //and we just continue with the next one in the list - if (i < q1_numfaces) break; - } //end if - else - { - //find the face with the largest overlap with this brush side - //for texturing the brush side - for (i = 0; i < q1_numfaces; i++) - { - //the face must be in the same plane as the node plane that created - //this brush side - if (q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum) - { - //get the area the face and the brush side overlap - area = Q1_FaceOnWinding(&q1_dfaces[i], side->winding); - //if this face overlaps the brush side winding more than previous faces - if (area > largestarea) - { - largestarea = area; - bestfacenum = i; - } //end if - } //end if - } //end for - } //end else - //if a face was found for texturing this brush side - if (bestfacenum >= 0) - { - //set the MAP texinfo values - texinfonum = q1_dfaces[bestfacenum].texinfo; - for (n = 0; n < 4; n++) - { - map_texinfo[texinfonum].vecs[0][n] = q1_texinfo[texinfonum].vecs[0][n]; - map_texinfo[texinfonum].vecs[1][n] = q1_texinfo[texinfonum].vecs[1][n]; - } //end for - //make sure the two vectors aren't of zero length otherwise use the default - //vector to prevent a divide by zero in the map writing - if (VectorLength(map_texinfo[texinfonum].vecs[0]) < 0.01) - memcpy(map_texinfo[texinfonum].vecs[0], defaultvec, sizeof(defaultvec)); - if (VectorLength(map_texinfo[texinfonum].vecs[1]) < 0.01) - memcpy(map_texinfo[texinfonum].vecs[1], defaultvec, sizeof(defaultvec)); - // - map_texinfo[texinfonum].flags = q1_texinfo[texinfonum].flags; - map_texinfo[texinfonum].value = 0; //Q1 and HL texinfos don't have a value - //the mip texture - miptexlump = (q1_dmiptexlump_t *) q1_dtexdata; - ofs = miptexlump->dataofs[q1_texinfo[texinfonum].miptex]; - if ( ofs > q1_texdatasize ) { - ofs = miptexlump->dataofs[0]; - } - miptex = (q1_miptex_t *)((byte *)miptexlump + ofs); - //get the mip texture name - strcpy(map_texinfo[texinfonum].texture, miptex->name); - //no animations in Quake1 and Half-Life mip textures - map_texinfo[texinfonum].nexttexinfo = -1; - //store the texinfo number - side->texinfo = texinfonum; - // - if (texinfonum > map_numtexinfo) map_numtexinfo = texinfonum; - //this side is textured - side->flags |= SFL_TEXTURED; - } //end if - else - { - //no texture for this side - side->texinfo = TEXINFO_NODE; - //this side is textured - side->flags |= SFL_TEXTURED; - } //end if - } //end for - // - if (!modelnum && prevbrush != brush) qprintf("\r%5d", ++numbrushes); - //previous brush in the list - prevbrush = brush; - } //end for - if (!modelnum) qprintf("\n"); - //return the new list with brushes - return brushlist; -} //end of the function Q1_TextureBrushes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q1_FixContentsTextures(bspbrush_t *brushlist) -{ - int i, texinfonum; - bspbrush_t *brush; - - for (brush = brushlist; brush; brush = brush->next) - { - //only fix the textures of water, slime and lava brushes - if (brush->side != CONTENTS_WATER && - brush->side != CONTENTS_SLIME && - brush->side != CONTENTS_LAVA) continue; - // - for (i = 0; i < brush->numsides; i++) - { - texinfonum = brush->sides[i].texinfo; - if (Q1_TextureContents(map_texinfo[texinfonum].texture) == brush->side) break; - } //end for - //if no specific contents texture was found - if (i >= brush->numsides) - { - texinfonum = -1; - for (i = 0; i < map_numtexinfo; i++) - { - if (Q1_TextureContents(map_texinfo[i].texture) == brush->side) - { - texinfonum = i; - break; - } //end if - } //end for - } //end if - // - if (texinfonum >= 0) - { - //give all the brush sides this contents texture - for (i = 0; i < brush->numsides; i++) - { - brush->sides[i].texinfo = texinfonum; - } //end for - } //end if - else Log_Print("brush contents %d with wrong textures\n", brush->side); - // - } //end for - /* - for (brush = brushlist; brush; brush = brush->next) - { - //give all the brush sides this contents texture - for (i = 0; i < brush->numsides; i++) - { - if (Q1_TextureContents(map_texinfo[texinfonum].texture) != brush->side) - { - Error("brush contents %d with wrong contents textures %s\n", brush->side, - Q1_TextureContents(map_texinfo[texinfonum].texture)); - } //end if - } //end for - } //end for*/ -} //end of the function Q1_FixContentsTextures -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q1_BSPBrushToMapBrush(bspbrush_t *bspbrush, entity_t *mapent) -{ - mapbrush_t *mapbrush; - side_t *side; - int i, besttexinfo; - - CheckBSPBrush(bspbrush); - - if (nummapbrushes >= MAX_MAPFILE_BRUSHES) - Error ("nummapbrushes == MAX_MAPFILE_BRUSHES"); - - mapbrush = &mapbrushes[nummapbrushes]; - mapbrush->original_sides = &brushsides[nummapbrushsides]; - mapbrush->entitynum = mapent - entities; - mapbrush->brushnum = nummapbrushes - mapent->firstbrush; - mapbrush->leafnum = -1; - mapbrush->numsides = 0; - - besttexinfo = TEXINFO_NODE; - for (i = 0; i < bspbrush->numsides; i++) - { - if (!bspbrush->sides[i].winding) continue; - // - if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES) - Error ("MAX_MAPFILE_BRUSHSIDES"); - side = &brushsides[nummapbrushsides]; - //the contents of the bsp brush is stored in the side variable - side->contents = bspbrush->side; - side->surf = 0; - side->planenum = bspbrush->sides[i].planenum; - side->texinfo = bspbrush->sides[i].texinfo; - if (side->texinfo != TEXINFO_NODE) - { - //this brush side is textured - side->flags |= SFL_TEXTURED; - besttexinfo = side->texinfo; - } //end if - // - nummapbrushsides++; - mapbrush->numsides++; - } //end for - // - if (besttexinfo == TEXINFO_NODE) - { - mapbrush->numsides = 0; - q1_numclipbrushes++; - return; - } //end if - //set the texinfo for all the brush sides without texture - for (i = 0; i < mapbrush->numsides; i++) - { - if (mapbrush->original_sides[i].texinfo == TEXINFO_NODE) - { - mapbrush->original_sides[i].texinfo = besttexinfo; - } //end if - } //end for - //contents of the brush - mapbrush->contents = bspbrush->side; - // - if (create_aas) - { - //create the AAS brushes from this brush, add brush bevels - AAS_CreateMapBrushes(mapbrush, mapent, true); - return; - } //end if - //create windings for sides and bounds for brush - MakeBrushWindings(mapbrush); - //add brush bevels - AddBrushBevels(mapbrush); - //a new brush has been created - nummapbrushes++; - mapent->numbrushes++; -} //end of the function Q1_BSPBrushToMapBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q1_CreateMapBrushes(entity_t *mapent, int modelnum) -{ - bspbrush_t *brushlist, *brush, *nextbrush; - int i; - - //create brushes from the model BSP tree - brushlist = Q1_CreateBrushesFromBSP(modelnum); - //texture the brushes and split them when necesary - brushlist = Q1_TextureBrushes(brushlist, modelnum); - //fix the contents textures of all brushes - Q1_FixContentsTextures(brushlist); - // - if (!nobrushmerge) - { - brushlist = Q1_MergeBrushes(brushlist, modelnum); - //brushlist = Q1_MergeBrushes(brushlist, modelnum); - } //end if - // - if (!modelnum) qprintf("converting brushes to map brushes\n"); - if (!modelnum) qprintf("%5d brushes", i = 0); - for (brush = brushlist; brush; brush = nextbrush) - { - nextbrush = brush->next; - Q1_BSPBrushToMapBrush(brush, mapent); - brush->next = NULL; - FreeBrush(brush); - if (!modelnum) qprintf("\r%5d", ++i); - } //end for - if (!modelnum) qprintf("\n"); -} //end of the function Q1_CreateMapBrushes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q1_ResetMapLoading(void) -{ -} //end of the function Q1_ResetMapLoading -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q1_LoadMapFromBSP(char *filename, int offset, int length) -{ - int i, modelnum; - char *model, *classname; - - Log_Print("-- Q1_LoadMapFromBSP --\n"); - //the loaded map type - loadedmaptype = MAPTYPE_QUAKE1; - // - qprintf("loading map from %s at %d\n", filename, offset); - //load the Half-Life BSP file - Q1_LoadBSPFile(filename, offset, length); - // - q1_numclipbrushes = 0; - //CreatePath(path); - //Q1_CreateQ2WALFiles(path); - //parse the entities from the BSP - Q1_ParseEntities(); - //clear the map mins and maxs - ClearBounds(map_mins, map_maxs); - // - qprintf("creating Quake1 brushes\n"); - if (lessbrushes) qprintf("creating minimum number of brushes\n"); - else qprintf("placing textures correctly\n"); - // - for (i = 0; i < num_entities; i++) - { - entities[i].firstbrush = nummapbrushes; - entities[i].numbrushes = 0; - // - classname = ValueForKey(&entities[i], "classname"); - if (classname && !strcmp(classname, "worldspawn")) - { - modelnum = 0; - } //end if - else - { - // - model = ValueForKey(&entities[i], "model"); - if (!model || *model != '*') continue; - model++; - modelnum = atoi(model); - } //end else - //create map brushes for the entity - Q1_CreateMapBrushes(&entities[i], modelnum); - } //end for - // - qprintf("%5d map brushes\n", nummapbrushes); - qprintf("%5d clip brushes\n", q1_numclipbrushes); -} //end of the function Q1_LoadMapFromBSP diff --git a/tools/quake3/bspc/map_q2.c b/tools/quake3/bspc/map_q2.c deleted file mode 100644 index 2792109a..00000000 --- a/tools/quake3/bspc/map_q2.c +++ /dev/null @@ -1,1162 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -//=========================================================================== -// ANSI, Area Navigational System Interface -// AAS, Area Awareness System -//=========================================================================== - -#include "qbsp.h" -#include "l_mem.h" -#include "botlib/aasfile.h" //aas_bbox_t -#include "aas_store.h" //AAS_MAX_BBOXES -#include "aas_cfg.h" -#include "aas_map.h" //AAS_CreateMapBrushes -#include "l_bsp_q2.h" - - -#ifdef ME - -#define NODESTACKSIZE 1024 - -int nodestack[NODESTACKSIZE]; -int *nodestackptr; -int nodestacksize = 0; -int brushmodelnumbers[MAX_MAPFILE_BRUSHES]; -int dbrushleafnums[MAX_MAPFILE_BRUSHES]; -int dplanes2mapplanes[MAX_MAPFILE_PLANES]; - -#endif //ME - -//==================================================================== - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q2_CreateMapTexinfo(void) -{ - int i; - - for (i = 0; i < numtexinfo; i++) - { - memcpy(map_texinfo[i].vecs, texinfo[i].vecs, sizeof(float) * 2 * 4); - map_texinfo[i].flags = texinfo[i].flags; - map_texinfo[i].value = texinfo[i].value; - strcpy(map_texinfo[i].texture, texinfo[i].texture); - map_texinfo[i].nexttexinfo = 0; - } //end for -} //end of the function Q2_CreateMapTexinfo - -/* -=========== -Q2_BrushContents -=========== -*/ -int Q2_BrushContents (mapbrush_t *b) -{ - int contents; - side_t *s; - int i; - int trans; - - s = &b->original_sides[0]; - contents = s->contents; - trans = texinfo[s->texinfo].flags; - for (i = 1; i < b->numsides; i++, s++) - { - s = &b->original_sides[i]; - trans |= texinfo[s->texinfo].flags; - if (s->contents != contents) - { - Log_Print("Entity %i, Brush %i: mixed face contents\n" - , b->entitynum, b->brushnum); - Log_Print("texture name = %s\n", texinfo[s->texinfo].texture); - break; - } - } - - // if any side is translucent, mark the contents - // and change solid to window - if ( trans & (SURF_TRANS33|SURF_TRANS66) ) - { - contents |= CONTENTS_Q2TRANSLUCENT; - if (contents & CONTENTS_SOLID) - { - contents &= ~CONTENTS_SOLID; - contents |= CONTENTS_WINDOW; - } - } - - return contents; -} - -#ifdef ME - -#define BBOX_NORMAL_EPSILON 0.0001 - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void MakeAreaPortalBrush(mapbrush_t *brush) -{ - int sn; - side_t *s; - - brush->contents = CONTENTS_AREAPORTAL; - - for (sn = 0; sn < brush->numsides; sn++) - { - s = brush->original_sides + sn; - //make sure the surfaces are not hint or skip - s->surf &= ~(SURF_HINT|SURF_SKIP); - // - s->texinfo = 0; - s->contents = CONTENTS_AREAPORTAL; - } //end for -} //end of the function MakeAreaPortalBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void DPlanes2MapPlanes(void) -{ - int i; - - for (i = 0; i < numplanes; i++) - { - dplanes2mapplanes[i] = FindFloatPlane(dplanes[i].normal, dplanes[i].dist); - } //end for -} //end of the function DPlanes2MapPlanes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void MarkVisibleBrushSides(mapbrush_t *brush) -{ - int n, i, planenum; - side_t *side; - dface_t *face; - // - for (n = 0; n < brush->numsides; n++) - { - side = brush->original_sides + n; - //if this side is a bevel or the leaf number of the brush is unknown - if ((side->flags & SFL_BEVEL) || brush->leafnum < 0) - { - //this side is a valid splitter - side->flags |= SFL_VISIBLE; - continue; - } //end if - //assum this side will not be used as a splitter - side->flags &= ~SFL_VISIBLE; - //check if the side plane is used by a visible face - for (i = 0; i < numfaces; i++) - { - face = &dfaces[i]; - planenum = dplanes2mapplanes[face->planenum]; - if ((planenum & ~1) == (side->planenum & ~1)) - { - //this side is a valid splitter - side->flags |= SFL_VISIBLE; - } //end if - } //end for - } //end for -} //end of the function MarkVisibleBrushSides - -#endif //ME - -/* -================= -Q2_ParseBrush -================= -*/ -void Q2_ParseBrush (script_t *script, entity_t *mapent) -{ - mapbrush_t *b; - int i, j, k; - int mt; - side_t *side, *s2; - int planenum; - brush_texture_t td; - int planepts[3][3]; - token_t token; - - if (nummapbrushes >= MAX_MAPFILE_BRUSHES) - Error ("nummapbrushes == MAX_MAPFILE_BRUSHES"); - - b = &mapbrushes[nummapbrushes]; - b->original_sides = &brushsides[nummapbrushsides]; - b->entitynum = num_entities-1; - b->brushnum = nummapbrushes - mapent->firstbrush; - b->leafnum = -1; - - do - { - if (!PS_ReadToken(script, &token)) - break; - if (!strcmp(token.string, "}") ) - break; - - //IDBUG: mixed use of MAX_MAPFILE_? and MAX_MAP_? this could - // lead to out of bound indexing of the arrays - if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES) - Error ("MAX_MAPFILE_BRUSHSIDES"); - side = &brushsides[nummapbrushsides]; - - //read the three point plane definition - for (i = 0; i < 3; i++) - { - if (i != 0) PS_ExpectTokenString(script, "("); - for (j = 0; j < 3; j++) - { - PS_ExpectAnyToken(script, &token); - planepts[i][j] = atof(token.string); - } //end for - PS_ExpectTokenString(script, ")"); - } //end for - - // - //read the texturedef - // - PS_ExpectAnyToken(script, &token); - strcpy(td.name, token.string); - - PS_ExpectAnyToken(script, &token); - td.shift[0] = atol(token.string); - PS_ExpectAnyToken(script, &token); - td.shift[1] = atol(token.string); - PS_ExpectAnyToken(script, &token); - td.rotate = atol(token.string); - PS_ExpectAnyToken(script, &token); - td.scale[0] = atof(token.string); - PS_ExpectAnyToken(script, &token); - td.scale[1] = atof(token.string); - - //find default flags and values - mt = FindMiptex (td.name); - td.flags = textureref[mt].flags; - td.value = textureref[mt].value; - side->contents = textureref[mt].contents; - side->surf = td.flags = textureref[mt].flags; - - //check if there's a number available - if (PS_CheckTokenType(script, TT_NUMBER, 0, &token)) - { - side->contents = token.intvalue; - PS_ExpectTokenType(script, TT_NUMBER, 0, &token); - side->surf = td.flags = token.intvalue; - PS_ExpectTokenType(script, TT_NUMBER, 0, &token); - td.value = token.intvalue; - } - - // translucent objects are automatically classified as detail - if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) - side->contents |= CONTENTS_DETAIL; - if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) - side->contents |= CONTENTS_DETAIL; - if (fulldetail) - side->contents &= ~CONTENTS_DETAIL; - if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) - | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST) ) ) - side->contents |= CONTENTS_SOLID; - - // hints and skips are never detail, and have no content - if (side->surf & (SURF_HINT|SURF_SKIP) ) - { - side->contents = 0; - side->surf &= ~CONTENTS_DETAIL; - } - -#ifdef ME - //for creating AAS... this side is textured - side->flags |= SFL_TEXTURED; -#endif //ME - // - // find the plane number - // - planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]); - if (planenum == -1) - { - Log_Print("Entity %i, Brush %i: plane with no normal\n" - , b->entitynum, b->brushnum); - continue; - } - - // - // see if the plane has been used already - // - for (k=0 ; knumsides ; k++) - { - s2 = b->original_sides + k; - if (s2->planenum == planenum) - { - Log_Print("Entity %i, Brush %i: duplicate plane\n" - , b->entitynum, b->brushnum); - break; - } - if ( s2->planenum == (planenum^1) ) - { - Log_Print("Entity %i, Brush %i: mirrored plane\n" - , b->entitynum, b->brushnum); - break; - } - } - if (k != b->numsides) - continue; // duplicated - - // - // keep this side - // - - side = b->original_sides + b->numsides; - side->planenum = planenum; - side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], - &td, vec3_origin); - - // save the td off in case there is an origin brush and we - // have to recalculate the texinfo - side_brushtextures[nummapbrushsides] = td; - - nummapbrushsides++; - b->numsides++; - } while (1); - - // get the content for the entire brush - b->contents = Q2_BrushContents (b); - -#ifdef ME - if (BrushExists(b)) - { - c_squattbrushes++; - b->numsides = 0; - return; - } //end if - - if (create_aas) - { - //create AAS brushes, and add brush bevels - AAS_CreateMapBrushes(b, mapent, true); - //NOTE: if we return here then duplicate plane errors occur for the non world entities - return; - } //end if -#endif //ME - - // allow detail brushes to be removed - if (nodetail && (b->contents & CONTENTS_DETAIL) ) - { - b->numsides = 0; - return; - } - - // allow water brushes to be removed - if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) - { - b->numsides = 0; - return; - } - - // create windings for sides and bounds for brush - MakeBrushWindings (b); - - // brushes that will not be visible at all will never be - // used as bsp splitters - if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) - { - c_clipbrushes++; - for (i=0 ; inumsides ; i++) - b->original_sides[i].texinfo = TEXINFO_NODE; - } - - // - // origin brushes are removed, but they set - // the rotation origin for the rest of the brushes - // in the entity. After the entire entity is parsed, - // the planenums and texinfos will be adjusted for - // the origin brush - // - if (b->contents & CONTENTS_ORIGIN) - { - char string[32]; - vec3_t origin; - - if (num_entities == 1) - { - Error ("Entity %i, Brush %i: origin brushes not allowed in world" - , b->entitynum, b->brushnum); - return; - } - - VectorAdd (b->mins, b->maxs, origin); - VectorScale (origin, 0.5, origin); - - sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); - SetKeyValue (&entities[b->entitynum], "origin", string); - - VectorCopy (origin, entities[b->entitynum].origin); - - // don't keep this brush - b->numsides = 0; - - return; - } - - AddBrushBevels(b); - - nummapbrushes++; - mapent->numbrushes++; -} - -/* -================ -Q2_MoveBrushesToWorld - -Takes all of the brushes from the current entity and -adds them to the world's brush list. - -Used by func_group and func_areaportal -================ -*/ -void Q2_MoveBrushesToWorld (entity_t *mapent) -{ - int newbrushes; - int worldbrushes; - mapbrush_t *temp; - int i; - - // this is pretty gross, because the brushes are expected to be - // in linear order for each entity - - newbrushes = mapent->numbrushes; - worldbrushes = entities[0].numbrushes; - - temp = GetMemory(newbrushes*sizeof(mapbrush_t)); - memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t)); - -#if 0 // let them keep their original brush numbers - for (i=0 ; inumbrushes = 0; -} - -/* -================ -Q2_ParseMapEntity -================ -*/ -qboolean Q2_ParseMapEntity(script_t *script) -{ - entity_t *mapent; - epair_t *e; - side_t *s; - int i, j; - int startbrush, startsides; - vec_t newdist; - mapbrush_t *b; - token_t token; - - if (!PS_ReadToken(script, &token)) return false; - - if (strcmp(token.string, "{") ) - Error ("ParseEntity: { not found"); - - if (num_entities == MAX_MAP_ENTITIES) - Error ("num_entities == MAX_MAP_ENTITIES"); - - startbrush = nummapbrushes; - startsides = nummapbrushsides; - - mapent = &entities[num_entities]; - num_entities++; - memset (mapent, 0, sizeof(*mapent)); - mapent->firstbrush = nummapbrushes; - mapent->numbrushes = 0; -// mapent->portalareas[0] = -1; -// mapent->portalareas[1] = -1; - - do - { - if (!PS_ReadToken(script, &token)) - { - Error("ParseEntity: EOF without closing brace"); - } //end if - if (!strcmp(token.string, "}")) break; - if (!strcmp(token.string, "{")) - { - Q2_ParseBrush(script, mapent); - } //end if - else - { - PS_UnreadLastToken(script); - e = ParseEpair(script); - e->next = mapent->epairs; - mapent->epairs = e; - } //end else - } while(1); - - GetVectorForKey(mapent, "origin", mapent->origin); - - // - // if there was an origin brush, offset all of the planes and texinfo - // - if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2]) - { - for (i=0 ; inumbrushes ; i++) - { - b = &mapbrushes[mapent->firstbrush + i]; - for (j=0 ; jnumsides ; j++) - { - s = &b->original_sides[j]; - newdist = mapplanes[s->planenum].dist - - DotProduct (mapplanes[s->planenum].normal, mapent->origin); - s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist); - s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], - &side_brushtextures[s-brushsides], mapent->origin); - } - MakeBrushWindings (b); - } - } - - // group entities are just for editor convenience - // toss all brushes into the world entity - if (!strcmp ("func_group", ValueForKey (mapent, "classname"))) - { - Q2_MoveBrushesToWorld (mapent); - mapent->numbrushes = 0; - return true; - } - - // areaportal entities move their brushes, but don't eliminate - // the entity - if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname"))) - { - char str[128]; - - if (mapent->numbrushes != 1) - Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1); - - b = &mapbrushes[nummapbrushes-1]; - b->contents = CONTENTS_AREAPORTAL; - c_areaportals++; - mapent->areaportalnum = c_areaportals; - // set the portal number as "style" - sprintf (str, "%i", c_areaportals); - SetKeyValue (mapent, "style", str); - Q2_MoveBrushesToWorld (mapent); - return true; - } - - return true; -} - -//=================================================================== - -/* -================ -LoadMapFile -================ -*/ -void Q2_LoadMapFile(char *filename) -{ - int i; - script_t *script; - - Log_Print("-- Q2_LoadMapFile --\n"); -#ifdef ME - //loaded map type - loadedmaptype = MAPTYPE_QUAKE2; - //reset the map loading - ResetMapLoading(); -#endif //ME - - script = LoadScriptFile(filename); - if (!script) - { - Log_Print("couldn't open %s\n", filename); - return; - } //end if - //white spaces and escape characters inside a string are not allowed - SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES | - SCFL_NOSTRINGESCAPECHARS | - SCFL_PRIMITIVE); - - nummapbrushsides = 0; - num_entities = 0; - - while (Q2_ParseMapEntity(script)) - { - } - - ClearBounds (map_mins, map_maxs); - for (i=0 ; i 4096) - continue; // no valid points - AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs); - AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs); - } //end for - - PrintMapInfo(); - - //free the script - FreeScript(script); -// TestExpandBrushes (); - // - Q2_CreateMapTexinfo(); -} //end of the function Q2_LoadMapFile - -#ifdef ME //Begin MAP loading from BSP file -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q2_SetLeafBrushesModelNumbers(int leafnum, int modelnum) -{ - int i, brushnum; - dleaf_t *leaf; - - leaf = &dleafs[leafnum]; - for (i = 0; i < leaf->numleafbrushes; i++) - { - brushnum = dleafbrushes[leaf->firstleafbrush + i]; - brushmodelnumbers[brushnum] = modelnum; - dbrushleafnums[brushnum] = leafnum; - } //end for -} //end of the function Q2_SetLeafBrushesModelNumbers -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q2_InitNodeStack(void) -{ - nodestackptr = nodestack; - nodestacksize = 0; -} //end of the function Q2_InitNodeStack -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q2_PushNodeStack(int num) -{ - *nodestackptr = num; - nodestackptr++; - nodestacksize++; - // - if (nodestackptr >= &nodestack[NODESTACKSIZE]) - { - Error("Q2_PushNodeStack: stack overflow\n"); - } //end if -} //end of the function Q2_PushNodeStack -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Q2_PopNodeStack(void) -{ - //if the stack is empty - if (nodestackptr <= nodestack) return -1; - //decrease stack pointer - nodestackptr--; - nodestacksize--; - //return the top value from the stack - return *nodestackptr; -} //end of the function Q2_PopNodeStack -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q2_SetBrushModelNumbers(entity_t *mapent) -{ - int n, pn; - int leafnum; - - // - Q2_InitNodeStack(); - //head node (root) of the bsp tree - n = dmodels[mapent->modelnum].headnode; - pn = 0; - - do - { - //if we are in a leaf (negative node number) - if (n < 0) - { - //number of the leaf - leafnum = (-n) - 1; - //set the brush numbers - Q2_SetLeafBrushesModelNumbers(leafnum, mapent->modelnum); - //walk back into the tree to find a second child to continue with - for (pn = Q2_PopNodeStack(); pn >= 0; n = pn, pn = Q2_PopNodeStack()) - { - //if we took the first child at the parent node - if (dnodes[pn].children[0] == n) break; - } //end for - //if the stack wasn't empty (if not processed whole tree) - if (pn >= 0) - { - //push the parent node again - Q2_PushNodeStack(pn); - //we proceed with the second child of the parent node - n = dnodes[pn].children[1]; - } //end if - } //end if - else - { - //push the current node onto the stack - Q2_PushNodeStack(n); - //walk forward into the tree to the first child - n = dnodes[n].children[0]; - } //end else - } while(pn >= 0); -} //end of the function Q2_SetBrushModelNumbers -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q2_BSPBrushToMapBrush(dbrush_t *bspbrush, entity_t *mapent) -{ - mapbrush_t *b; - int i, k, n; - side_t *side, *s2; - int planenum; - dbrushside_t *bspbrushside; - dplane_t *bspplane; - - if (nummapbrushes >= MAX_MAPFILE_BRUSHES) - Error ("nummapbrushes >= MAX_MAPFILE_BRUSHES"); - - b = &mapbrushes[nummapbrushes]; - b->original_sides = &brushsides[nummapbrushsides]; - b->entitynum = mapent-entities; - b->brushnum = nummapbrushes - mapent->firstbrush; - b->leafnum = dbrushleafnums[bspbrush - dbrushes]; - - for (n = 0; n < bspbrush->numsides; n++) - { - //pointer to the bsp brush side - bspbrushside = &dbrushsides[bspbrush->firstside + n]; - - if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES) - { - Error ("MAX_MAPFILE_BRUSHSIDES"); - } //end if - //pointer to the map brush side - side = &brushsides[nummapbrushsides]; - //if the BSP brush side is textured - if (brushsidetextured[bspbrush->firstside + n]) side->flags |= SFL_TEXTURED; - else side->flags &= ~SFL_TEXTURED; - //ME: can get side contents and surf directly from BSP file - side->contents = bspbrush->contents; - //if the texinfo is TEXINFO_NODE - if (bspbrushside->texinfo < 0) side->surf = 0; - else side->surf = texinfo[bspbrushside->texinfo].flags; - - // translucent objects are automatically classified as detail - if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) - side->contents |= CONTENTS_DETAIL; - if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) - side->contents |= CONTENTS_DETAIL; - if (fulldetail) - side->contents &= ~CONTENTS_DETAIL; - if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) - | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST) ) ) - side->contents |= CONTENTS_SOLID; - - // hints and skips are never detail, and have no content - if (side->surf & (SURF_HINT|SURF_SKIP) ) - { - side->contents = 0; - side->surf &= ~CONTENTS_DETAIL; - } - - //ME: get a plane for this side - bspplane = &dplanes[bspbrushside->planenum]; - planenum = FindFloatPlane(bspplane->normal, bspplane->dist); - // - // see if the plane has been used already - // - //ME: this really shouldn't happen!!! - //ME: otherwise the bsp file is corrupted?? - //ME: still it seems to happen, maybe Johny Boy's - //ME: brush bevel adding is crappy ? - for (k = 0; k < b->numsides; k++) - { - s2 = b->original_sides + k; -// if (DotProduct (mapplanes[s2->planenum].normal, mapplanes[planenum].normal) > 0.999 -// && fabs(mapplanes[s2->planenum].dist - mapplanes[planenum].dist) < 0.01 ) - - if (s2->planenum == planenum) - { - Log_Print("Entity %i, Brush %i: duplicate plane\n" - , b->entitynum, b->brushnum); - break; - } - if ( s2->planenum == (planenum^1) ) - { - Log_Print("Entity %i, Brush %i: mirrored plane\n" - , b->entitynum, b->brushnum); - break; - } - } - if (k != b->numsides) - continue; // duplicated - - // - // keep this side - // - //ME: reset pointer to side, why? hell I dunno (pointer is set above already) - side = b->original_sides + b->numsides; - //ME: store the plane number - side->planenum = planenum; - //ME: texinfo is already stored when bsp is loaded - //NOTE: check for TEXINFO_NODE, otherwise crash in Q2_BrushContents - if (bspbrushside->texinfo < 0) side->texinfo = 0; - else side->texinfo = bspbrushside->texinfo; - - // save the td off in case there is an origin brush and we - // have to recalculate the texinfo - // ME: don't need to recalculate because it's already done - // (for non-world entities) in the BSP file -// side_brushtextures[nummapbrushsides] = td; - - nummapbrushsides++; - b->numsides++; - } //end for - - // get the content for the entire brush - b->contents = bspbrush->contents; - Q2_BrushContents(b); - - if (BrushExists(b)) - { - c_squattbrushes++; - b->numsides = 0; - return; - } //end if - - //if we're creating AAS - if (create_aas) - { - //create the AAS brushes from this brush, don't add brush bevels - AAS_CreateMapBrushes(b, mapent, false); - return; - } //end if - - // allow detail brushes to be removed - if (nodetail && (b->contents & CONTENTS_DETAIL) ) - { - b->numsides = 0; - return; - } //end if - - // allow water brushes to be removed - if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) - { - b->numsides = 0; - return; - } //end if - - // create windings for sides and bounds for brush - MakeBrushWindings(b); - - //mark brushes without winding or with a tiny window as bevels - MarkBrushBevels(b); - - // brushes that will not be visible at all will never be - // used as bsp splitters - if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) - { - c_clipbrushes++; - for (i = 0; i < b->numsides; i++) - b->original_sides[i].texinfo = TEXINFO_NODE; - } //end for - - // - // origin brushes are removed, but they set - // the rotation origin for the rest of the brushes - // in the entity. After the entire entity is parsed, - // the planenums and texinfos will be adjusted for - // the origin brush - // - //ME: not needed because the entities in the BSP file already - // have an origin set -// if (b->contents & CONTENTS_ORIGIN) -// { -// char string[32]; -// vec3_t origin; -// -// if (num_entities == 1) -// { -// Error ("Entity %i, Brush %i: origin brushes not allowed in world" -// , b->entitynum, b->brushnum); -// return; -// } -// -// VectorAdd (b->mins, b->maxs, origin); -// VectorScale (origin, 0.5, origin); -// -// sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); -// SetKeyValue (&entities[b->entitynum], "origin", string); -// -// VectorCopy (origin, entities[b->entitynum].origin); -// -// // don't keep this brush -// b->numsides = 0; -// -// return; -// } - - //ME: the bsp brushes already have bevels, so we won't try to - // add them again (especially since Johny Boy's bevel adding might - // be crappy) -// AddBrushBevels(b); - - nummapbrushes++; - mapent->numbrushes++; -} //end of the function Q2_BSPBrushToMapBrush -//=========================================================================== -//=========================================================================== -void Q2_ParseBSPBrushes(entity_t *mapent) -{ - int i; - - //give all the brushes that belong to this entity the number of the - //BSP model used by this entity - Q2_SetBrushModelNumbers(mapent); - //now parse all the brushes with the correct mapent->modelnum - for (i = 0; i < numbrushes; i++) - { - if (brushmodelnumbers[i] == mapent->modelnum) - { - Q2_BSPBrushToMapBrush(&dbrushes[i], mapent); - } //end if - } //end for -} //end of the function Q2_ParseBSPBrushes -//=========================================================================== -//=========================================================================== -qboolean Q2_ParseBSPEntity(int entnum) -{ - entity_t *mapent; - char *model; - int startbrush, startsides; - - startbrush = nummapbrushes; - startsides = nummapbrushsides; - - mapent = &entities[entnum];//num_entities]; - mapent->firstbrush = nummapbrushes; - mapent->numbrushes = 0; - mapent->modelnum = -1; //-1 = no model - - model = ValueForKey(mapent, "model"); - if (model && strlen(model)) - { - if (*model != '*') - { - Error("Q2_ParseBSPEntity: model number without leading *"); - } //end if - //get the model number of this entity (skip the leading *) - mapent->modelnum = atoi(&model[1]); - } //end if - - GetVectorForKey(mapent, "origin", mapent->origin); - - //if this is the world entity it has model number zero - //the world entity has no model key - if (!strcmp("worldspawn", ValueForKey(mapent, "classname"))) - { - mapent->modelnum = 0; - } //end if - //if the map entity has a BSP model (a modelnum of -1 is used for - //entities that aren't using a BSP model) - if (mapent->modelnum >= 0) - { - //parse the bsp brushes - Q2_ParseBSPBrushes(mapent); - } //end if - // - //the origin of the entity is already taken into account - // - //func_group entities can't be in the bsp file - // - //check out the func_areaportal entities - if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname"))) - { - c_areaportals++; - mapent->areaportalnum = c_areaportals; - return true; - } //end if - return true; -} //end of the function Q2_ParseBSPEntity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q2_LoadMapFromBSP(char *filename, int offset, int length) -{ - int i; - - Log_Print("-- Q2_LoadMapFromBSP --\n"); - //loaded map type - loadedmaptype = MAPTYPE_QUAKE2; - - Log_Print("Loading map from %s...\n", filename); - //load the bsp file - Q2_LoadBSPFile(filename, offset, length); - - //create an index from bsp planes to map planes - //DPlanes2MapPlanes(); - //clear brush model numbers - for (i = 0; i < MAX_MAPFILE_BRUSHES; i++) - brushmodelnumbers[i] = -1; - - nummapbrushsides = 0; - num_entities = 0; - - Q2_ParseEntities(); - // - for (i = 0; i < num_entities; i++) - { - Q2_ParseBSPEntity(i); - } //end for - - //get the map mins and maxs from the world model - ClearBounds(map_mins, map_maxs); - for (i = 0; i < entities[0].numbrushes; i++) - { - if (mapbrushes[i].mins[0] > 4096) - continue; //no valid points - AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs); - AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs); - } //end for - - PrintMapInfo(); - // - Q2_CreateMapTexinfo(); -} //end of the function Q2_LoadMapFromBSP - -void Q2_ResetMapLoading(void) -{ - //reset for map loading from bsp - memset(nodestack, 0, NODESTACKSIZE * sizeof(int)); - nodestackptr = NULL; - nodestacksize = 0; - memset(brushmodelnumbers, 0, MAX_MAPFILE_BRUSHES * sizeof(int)); -} //end of the function Q2_ResetMapLoading - -//End MAP loading from BSP file -#endif //ME - -//==================================================================== - -/* -================ -TestExpandBrushes - -Expands all the brush planes and saves a new map out -================ -*/ -void TestExpandBrushes (void) -{ - FILE *f; - side_t *s; - int i, j, bn; - winding_t *w; - char *name = "expanded.map"; - mapbrush_t *brush; - vec_t dist; - - Log_Print("writing %s\n", name); - f = fopen (name, "wb"); - if (!f) - Error ("Can't write %s\n", name); - - fprintf (f, "{\n\"classname\" \"worldspawn\"\n"); - - for (bn=0 ; bnnumsides ; i++) - { - s = brush->original_sides + i; - dist = mapplanes[s->planenum].dist; - for (j=0 ; j<3 ; j++) - dist += fabs( 16 * mapplanes[s->planenum].normal[j] ); - - w = BaseWindingForPlane (mapplanes[s->planenum].normal, dist); - - fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]); - fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]); - fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]); - - fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture); - FreeWinding (w); - } - fprintf (f, "}\n"); - } - fprintf (f, "}\n"); - - fclose (f); - - Error ("can't proceed after expanding brushes"); -} //end of the function TestExpandBrushes - diff --git a/tools/quake3/bspc/map_q3.c b/tools/quake3/bspc/map_q3.c deleted file mode 100644 index f8df4c71..00000000 --- a/tools/quake3/bspc/map_q3.c +++ /dev/null @@ -1,680 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "l_mem.h" -#include "deps/botlib/aasfile.h" //aas_bbox_t -#include "aas_store.h" //AAS_MAX_BBOXES -#include "aas_cfg.h" -#include "aas_map.h" //AAS_CreateMapBrushes -#include "l_bsp_q3.h" -#include "deps/qcommon/cm_patch.h" -#include "qcommon/surfaceflags.h" - -#define NODESTACKSIZE 1024 - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintContents(int contents); - -int Q3_BrushContents(mapbrush_t *b) -{ - int contents, i, mixed, hint; - side_t *s; - - s = &b->original_sides[0]; - contents = s->contents; - // - mixed = false; - hint = false; - for (i = 1; i < b->numsides; i++) - { - s = &b->original_sides[i]; - if (s->contents != contents) mixed = true; - if (s->surf & (SURF_HINT|SURF_SKIP)) hint = true; - contents |= s->contents; - } //end for - // - if (hint) - { - if (contents) - { - Log_Write("WARNING: hint brush with contents: "); - PrintContents(contents); - Log_Write("\r\n"); - // - Log_Write("brush contents is: "); - PrintContents(b->contents); - Log_Write("\r\n"); - } //end if - return 0; - } //end if - //Log_Write("brush %d contents ", nummapbrushes); - //PrintContents(contents); - //Log_Write("\r\n"); - //remove ladder and fog contents - contents &= ~(CONTENTS_LADDER|CONTENTS_FOG); - // - if (mixed) - { - Log_Write("Entity %i, Brush %i: mixed face contents " - , b->entitynum, b->brushnum); - PrintContents(contents); - Log_Write("\r\n"); - // - Log_Write("brush contents is: "); - PrintContents(b->contents); - Log_Write("\r\n"); - // - if (contents & CONTENTS_DONOTENTER) return CONTENTS_DONOTENTER;//Log_Print("mixed contents with donotenter\n"); - /* - Log_Print("contents:"); PrintContents(contents); - Log_Print("\ncontents:"); PrintContents(s->contents); - Log_Print("\n"); - Log_Print("texture name = %s\n", texinfo[s->texinfo].texture); - */ - //if liquid brush - if (contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) - { - return (contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)); - } //end if - if (contents & CONTENTS_PLAYERCLIP) return (contents & CONTENTS_PLAYERCLIP); - return (contents & CONTENTS_SOLID); - } //end if - /* - if (contents & CONTENTS_AREAPORTAL) - { - static int num; - Log_Write("Entity %i, Brush %i: area portal %d\r\n", b->entitynum, b->brushnum, num++); - } //end if*/ - if (contents == (contents & CONTENTS_STRUCTURAL)) - { - //Log_Print("brush %i is only structural\n", b->brushnum); - contents = 0; - } //end if - if (contents & CONTENTS_DONOTENTER) - { - Log_Print("brush %i is a donotenter brush, c = %X\n", b->brushnum, contents); - } //end if - return contents; -} //end of the function Q3_BrushContents -#define BBOX_NORMAL_EPSILON 0.0001 -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q3_DPlanes2MapPlanes(void) -{ - int i; - - for (i = 0; i < q3_numplanes; i++) - { - dplanes2mapplanes[i] = FindFloatPlane(q3_dplanes[i].normal, q3_dplanes[i].dist); - } //end for -} //end of the function Q3_DPlanes2MapPlanes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q3_BSPBrushToMapBrush(q3_dbrush_t *bspbrush, entity_t *mapent) -{ - mapbrush_t *b; - int i, k, n; - side_t *side, *s2; - int planenum; - q3_dbrushside_t *bspbrushside; - q3_dplane_t *bspplane; - - if (nummapbrushes >= MAX_MAPFILE_BRUSHES) - Error ("nummapbrushes >= MAX_MAPFILE_BRUSHES"); - - b = &mapbrushes[nummapbrushes]; - b->original_sides = &brushsides[nummapbrushsides]; - b->entitynum = mapent-entities; - b->brushnum = nummapbrushes - mapent->firstbrush; - b->leafnum = dbrushleafnums[bspbrush - q3_dbrushes]; - - for (n = 0; n < bspbrush->numSides; n++) - { - //pointer to the bsp brush side - bspbrushside = &q3_dbrushsides[bspbrush->firstSide + n]; - - if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES) - { - Error ("MAX_MAPFILE_BRUSHSIDES"); - } //end if - //pointer to the map brush side - side = &brushsides[nummapbrushsides]; - //if the BSP brush side is textured - if (q3_dbrushsidetextured[bspbrush->firstSide + n]) side->flags |= SFL_TEXTURED|SFL_VISIBLE; - else side->flags &= ~SFL_TEXTURED; - //NOTE: all Quake3 sides are assumed textured - //side->flags |= SFL_TEXTURED|SFL_VISIBLE; - // - if (bspbrushside->shaderNum < 0) - { - side->contents = 0; - side->surf = 0; - } //end if - else - { - side->contents = q3_dshaders[bspbrushside->shaderNum].contentFlags; - side->surf = q3_dshaders[bspbrushside->shaderNum].surfaceFlags; - if (strstr(q3_dshaders[bspbrushside->shaderNum].shader, "common/hint")) - { - //Log_Print("found hint side\n"); - side->surf |= SURF_HINT; - } //end if - } //end else - // - if (side->surf & SURF_NODRAW) - { - side->flags |= SFL_TEXTURED|SFL_VISIBLE; - } //end if - /* - if (side->contents & (CONTENTS_TRANSLUCENT|CONTENTS_STRUCTURAL)) - { - side->flags |= SFL_TEXTURED|SFL_VISIBLE; - } //end if*/ - - // hints and skips are never detail, and have no content - if (side->surf & (SURF_HINT|SURF_SKIP) ) - { - side->contents = 0; - //Log_Print("found hint brush side\n"); - } - /* - if ((side->surf & SURF_NODRAW) && (side->surf & SURF_NOIMPACT)) - { - side->contents = 0; - side->surf &= ~CONTENTS_DETAIL; - Log_Print("probably found hint brush in a BSP without hints being used\n"); - } //end if*/ - - //ME: get a plane for this side - bspplane = &q3_dplanes[bspbrushside->planeNum]; - planenum = FindFloatPlane(bspplane->normal, bspplane->dist); - // - // see if the plane has been used already - // - //ME: this really shouldn't happen!!! - //ME: otherwise the bsp file is corrupted?? - //ME: still it seems to happen, maybe Johny Boy's - //ME: brush bevel adding is crappy ? - for (k = 0; k < b->numsides; k++) - { - s2 = b->original_sides + k; -// if (DotProduct (mapplanes[s2->planenum].normal, mapplanes[planenum].normal) > 0.999 -// && fabs(mapplanes[s2->planenum].dist - mapplanes[planenum].dist) < 0.01 ) - - if (s2->planenum == planenum) - { - Log_Print("Entity %i, Brush %i: duplicate plane\n" - , b->entitynum, b->brushnum); - break; - } - if ( s2->planenum == (planenum^1) ) - { - Log_Print("Entity %i, Brush %i: mirrored plane\n" - , b->entitynum, b->brushnum); - break; - } - } - if (k != b->numsides) - continue; // duplicated - - // - // keep this side - // - //ME: reset pointer to side, why? hell I dunno (pointer is set above already) - side = b->original_sides + b->numsides; - //ME: store the plane number - side->planenum = planenum; - //ME: texinfo is already stored when bsp is loaded - //NOTE: check for TEXINFO_NODE, otherwise crash in Q3_BrushContents - //if (bspbrushside->texinfo < 0) side->texinfo = 0; - //else side->texinfo = bspbrushside->texinfo; - - // save the td off in case there is an origin brush and we - // have to recalculate the texinfo - // ME: don't need to recalculate because it's already done - // (for non-world entities) in the BSP file -// side_brushtextures[nummapbrushsides] = td; - - nummapbrushsides++; - b->numsides++; - } //end for - - // get the content for the entire brush - b->contents = q3_dshaders[bspbrush->shaderNum].contentFlags; - b->contents &= ~(CONTENTS_LADDER|CONTENTS_FOG|CONTENTS_STRUCTURAL); -// b->contents = Q3_BrushContents(b); - // - - if (BrushExists(b)) - { - c_squattbrushes++; - b->numsides = 0; - return; - } //end if - - //if we're creating AAS - if (create_aas) - { - //create the AAS brushes from this brush, don't add brush bevels - AAS_CreateMapBrushes(b, mapent, false); - return; - } //end if - - // allow detail brushes to be removed - if (nodetail && (b->contents & CONTENTS_DETAIL) ) - { - b->numsides = 0; - return; - } //end if - - // allow water brushes to be removed - if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) - { - b->numsides = 0; - return; - } //end if - - // create windings for sides and bounds for brush - MakeBrushWindings(b); - - //mark brushes without winding or with a tiny window as bevels - MarkBrushBevels(b); - - // brushes that will not be visible at all will never be - // used as bsp splitters - if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) - { - c_clipbrushes++; - for (i = 0; i < b->numsides; i++) - b->original_sides[i].texinfo = TEXINFO_NODE; - } //end for - - // - // origin brushes are removed, but they set - // the rotation origin for the rest of the brushes - // in the entity. After the entire entity is parsed, - // the planenums and texinfos will be adjusted for - // the origin brush - // - //ME: not needed because the entities in the BSP file already - // have an origin set -// if (b->contents & CONTENTS_ORIGIN) -// { -// char string[32]; -// vec3_t origin; -// -// if (num_entities == 1) -// { -// Error ("Entity %i, Brush %i: origin brushes not allowed in world" -// , b->entitynum, b->brushnum); -// return; -// } -// -// VectorAdd (b->mins, b->maxs, origin); -// VectorScale (origin, 0.5, origin); -// -// sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); -// SetKeyValue (&entities[b->entitynum], "origin", string); -// -// VectorCopy (origin, entities[b->entitynum].origin); -// -// // don't keep this brush -// b->numsides = 0; -// -// return; -// } - - //ME: the bsp brushes already have bevels, so we won't try to - // add them again (especially since Johny Boy's bevel adding might - // be crappy) -// AddBrushBevels(b); - - nummapbrushes++; - mapent->numbrushes++; -} //end of the function Q3_BSPBrushToMapBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q3_ParseBSPBrushes(entity_t *mapent) -{ - int i; - - for (i = 0; i < q3_dmodels[mapent->modelnum].numBrushes; i++) - { - Q3_BSPBrushToMapBrush(&q3_dbrushes[q3_dmodels[mapent->modelnum].firstBrush + i], mapent); - } //end for -} //end of the function Q3_ParseBSPBrushes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean Q3_ParseBSPEntity(int entnum) -{ - entity_t *mapent; - char *model; - int startbrush, startsides; - - startbrush = nummapbrushes; - startsides = nummapbrushsides; - - mapent = &entities[entnum];//num_entities]; - mapent->firstbrush = nummapbrushes; - mapent->numbrushes = 0; - mapent->modelnum = -1; //-1 = no BSP model - - model = ValueForKey(mapent, "model"); - if (model && strlen(model)) - { - if (*model == '*') - { - //get the model number of this entity (skip the leading *) - mapent->modelnum = atoi(&model[1]); - } //end if - } //end if - - GetVectorForKey(mapent, "origin", mapent->origin); - - //if this is the world entity it has model number zero - //the world entity has no model key - if (!strcmp("worldspawn", ValueForKey(mapent, "classname"))) - { - mapent->modelnum = 0; - } //end if - //if the map entity has a BSP model (a modelnum of -1 is used for - //entities that aren't using a BSP model) - if (mapent->modelnum >= 0) - { - //parse the bsp brushes - Q3_ParseBSPBrushes(mapent); - } //end if - // - //the origin of the entity is already taken into account - // - //func_group entities can't be in the bsp file - // - //check out the func_areaportal entities - if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname"))) - { - c_areaportals++; - mapent->areaportalnum = c_areaportals; - return true; - } //end if - return true; -} //end of the function Q3_ParseBSPEntity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#define MAX_PATCH_VERTS 1024 - -void AAS_CreateCurveBrushes(void) -{ - int i, j, n, planenum, numcurvebrushes = 0; - q3_dsurface_t *surface; - q3_drawVert_t *dv_p; - vec3_t points[MAX_PATCH_VERTS]; - int width, height, c; - patchCollide_t *pc; - facet_t *facet; - mapbrush_t *brush; - side_t *side; - entity_t *mapent; - winding_t *winding; - - qprintf("nummapbrushsides = %d\n", nummapbrushsides); - mapent = &entities[0]; - for (i = 0; i < q3_numDrawSurfaces; i++) - { - surface = &q3_drawSurfaces[i]; - if ( ! surface->patchWidth ) continue; - // if the curve is not solid - if (!(q3_dshaders[surface->shaderNum].contentFlags & (CONTENTS_SOLID|CONTENTS_PLAYERCLIP))) - { - //Log_Print("skipped non-solid curve\n"); - continue; - } //end if - // if this curve should not be used for AAS - if ( q3_dshaders[surface->shaderNum].contentFlags & CONTENTS_NOBOTCLIP ) { - continue; - } - // - width = surface->patchWidth; - height = surface->patchHeight; - c = width * height; - if (c > MAX_PATCH_VERTS) - { - Error("ParseMesh: MAX_PATCH_VERTS"); - } //end if - - dv_p = q3_drawVerts + surface->firstVert; - for ( j = 0 ; j < c ; j++, dv_p++ ) - { - points[j][0] = dv_p->xyz[0]; - points[j][1] = dv_p->xyz[1]; - points[j][2] = dv_p->xyz[2]; - } //end for - // create the internal facet structure - pc = CM_GeneratePatchCollide(width, height, points); - // - for (j = 0; j < pc->numFacets; j++) - { - facet = &pc->facets[j]; - // - brush = &mapbrushes[nummapbrushes]; - brush->original_sides = &brushsides[nummapbrushsides]; - brush->entitynum = 0; - brush->brushnum = nummapbrushes - mapent->firstbrush; - // - brush->numsides = facet->numBorders + 2; - nummapbrushsides += brush->numsides; - brush->contents = CONTENTS_SOLID; - // - //qprintf("\r%6d curve brushes", nummapbrushsides);//++numcurvebrushes); - qprintf("\r%6d curve brushes", ++numcurvebrushes); - // - planenum = FindFloatPlane(pc->planes[facet->surfacePlane].plane, pc->planes[facet->surfacePlane].plane[3]); - // - side = &brush->original_sides[0]; - side->planenum = planenum; - side->contents = CONTENTS_SOLID; - side->flags |= SFL_TEXTURED|SFL_VISIBLE|SFL_CURVE; - side->surf = 0; - // - side = &brush->original_sides[1]; - if (create_aas) - { - //the plane is expanded later so it's not a problem that - //these first two opposite sides are coplanar - side->planenum = planenum ^ 1; - } //end if - else - { - side->planenum = FindFloatPlane(mapplanes[planenum^1].normal, mapplanes[planenum^1].dist + 1); - side->flags |= SFL_TEXTURED|SFL_VISIBLE; - } //end else - side->contents = CONTENTS_SOLID; - side->flags |= SFL_CURVE; - side->surf = 0; - // - winding = BaseWindingForPlane(mapplanes[side->planenum].normal, mapplanes[side->planenum].dist); - for (n = 0; n < facet->numBorders; n++) - { - //never use the surface plane as a border - if (facet->borderPlanes[n] == facet->surfacePlane) continue; - // - side = &brush->original_sides[2 + n]; - side->planenum = FindFloatPlane(pc->planes[facet->borderPlanes[n]].plane, pc->planes[facet->borderPlanes[n]].plane[3]); - if (facet->borderInward[n]) side->planenum ^= 1; - side->contents = CONTENTS_SOLID; - side->flags |= SFL_TEXTURED|SFL_CURVE; - side->surf = 0; - //chop the winding in place - if (winding) ChopWindingInPlace(&winding, mapplanes[side->planenum^1].normal, mapplanes[side->planenum^1].dist, 0.1); //CLIP_EPSILON); - } //end for - //VectorCopy(pc->bounds[0], brush->mins); - //VectorCopy(pc->bounds[1], brush->maxs); - if (!winding) - { - Log_Print("WARNING: AAS_CreateCurveBrushes: no winding\n"); - brush->numsides = 0; - continue; - } //end if - brush->original_sides[0].winding = winding; - WindingBounds(winding, brush->mins, brush->maxs); - for (n = 0; n < 3; n++) - { - //IDBUG: all the indexes into the mins and maxs were zero (not using i) - if (brush->mins[n] < -MAX_MAP_BOUNDS || brush->maxs[n] > MAX_MAP_BOUNDS) - { - Log_Print("entity %i, brush %i: bounds out of range\n", brush->entitynum, brush->brushnum); - Log_Print("brush->mins[%d] = %f, brush->maxs[%d] = %f\n", n, brush->mins[n], n, brush->maxs[n]); - brush->numsides = 0; //remove the brush - break; - } //end if - if (brush->mins[n] > MAX_MAP_BOUNDS || brush->maxs[n] < -MAX_MAP_BOUNDS) - { - Log_Print("entity %i, brush %i: no visible sides on brush\n", brush->entitynum, brush->brushnum); - Log_Print("brush->mins[%d] = %f, brush->maxs[%d] = %f\n", n, brush->mins[n], n, brush->maxs[n]); - brush->numsides = 0; //remove the brush - break; - } //end if - } //end for - if (create_aas) - { - //NOTE: brush bevels now already added - //AddBrushBevels(brush); - AAS_CreateMapBrushes(brush, mapent, false); - } //end if - else - { - // create windings for sides and bounds for brush - MakeBrushWindings(brush); - AddBrushBevels(brush); - nummapbrushes++; - mapent->numbrushes++; - } //end else - } //end for - } //end for - //qprintf("\r%6d curve brushes", nummapbrushsides);//++numcurvebrushes); - qprintf("\r%6d curve brushes\n", numcurvebrushes); -} //end of the function AAS_CreateCurveBrushes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AAS_ExpandMapBrush(mapbrush_t *brush, vec3_t mins, vec3_t maxs); - -void Q3_LoadMapFromBSP(struct quakefile_s *qf) -{ - int i; - - Log_Print("-- Q3_LoadMapFromBSP --\n"); - //loaded map type - loadedmaptype = MAPTYPE_QUAKE3; - - Log_Print("Loading map from %s...\n", qf->filename); - //load the bsp file - Q3_LoadBSPFile(qf); - - //create an index from bsp planes to map planes - //DPlanes2MapPlanes(); - //clear brush model numbers - for (i = 0; i < MAX_MAPFILE_BRUSHES; i++) - brushmodelnumbers[i] = -1; - - nummapbrushsides = 0; - num_entities = 0; - - Q3_ParseEntities(); - // - for (i = 0; i < num_entities; i++) - { - Q3_ParseBSPEntity(i); - } //end for - - AAS_CreateCurveBrushes(); - //get the map mins and maxs from the world model - ClearBounds(map_mins, map_maxs); - for (i = 0; i < entities[0].numbrushes; i++) - { - if (mapbrushes[i].numsides <= 0) - continue; - AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs); - AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs); - } //end for - /*/ - for (i = 0; i < nummapbrushes; i++) - { - //if (!mapbrushes[i].original_sides) continue; - //AddBrushBevels(&mapbrushes[i]); - //AAS_ExpandMapBrush(&mapbrushes[i], mins, maxs); - } //end for*/ - /* - for (i = 0; i < nummapbrushsides; i++) - { - Log_Write("side %d flags = %d", i, brushsides[i].flags); - } //end for - for (i = 0; i < nummapbrushes; i++) - { - Log_Write("brush contents: "); - PrintContents(mapbrushes[i].contents); - Log_Print("\n"); - } //end for*/ -} //end of the function Q3_LoadMapFromBSP -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Q3_ResetMapLoading(void) -{ - //reset for map loading from bsp - memset(nodestack, 0, NODESTACKSIZE * sizeof(int)); - nodestackptr = NULL; - nodestacksize = 0; - memset(brushmodelnumbers, 0, MAX_MAPFILE_BRUSHES * sizeof(int)); -} //end of the function Q3_ResetMapLoading - diff --git a/tools/quake3/bspc/map_sin.c b/tools/quake3/bspc/map_sin.c deleted file mode 100644 index b6e67ffb..00000000 --- a/tools/quake3/bspc/map_sin.c +++ /dev/null @@ -1,1211 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -//----------------------------------------------------------------------------- -// -// $Logfile:: /MissionPack/code/bspc/map_sin.c $ - -#include "qbsp.h" -#include "l_bsp_sin.h" -#include "aas_map.h" //AAS_CreateMapBrushes - - -//==================================================================== - - -/* -=========== -Sin_BrushContents -=========== -*/ - -int Sin_BrushContents(mapbrush_t *b) -{ - int contents; - side_t *s; - int i; -#ifdef SIN - float trans = 0; -#else - int trans; -#endif - - s = &b->original_sides[0]; - contents = s->contents; - -#ifdef SIN - trans = sin_texinfo[s->texinfo].translucence; -#else - trans = texinfo[s->texinfo].flags; -#endif - for (i=1 ; inumsides ; i++, s++) - { - s = &b->original_sides[i]; -#ifdef SIN - trans += sin_texinfo[s->texinfo].translucence; -#else - trans |= texinfo[s->texinfo].flags; -#endif - if (s->contents != contents) - { -#ifdef SIN - if ( - ( s->contents & CONTENTS_DETAIL && !(contents & CONTENTS_DETAIL) ) || - ( !(s->contents & CONTENTS_DETAIL) && contents & CONTENTS_DETAIL ) - ) - { - s->contents |= CONTENTS_DETAIL; - contents |= CONTENTS_DETAIL; - continue; - } -#endif - printf ("Entity %i, Brush %i: mixed face contents\n" - , b->entitynum, b->brushnum); - break; - } - } - - -#ifdef SIN - if (contents & CONTENTS_FENCE) - { -// contents |= CONTENTS_TRANSLUCENT; - contents |= CONTENTS_DETAIL; - contents |= CONTENTS_DUMMYFENCE; - contents &= ~CONTENTS_SOLID; - contents &= ~CONTENTS_FENCE; - contents |= CONTENTS_WINDOW; - } -#endif - - // if any side is translucent, mark the contents - // and change solid to window -#ifdef SIN - if ( trans > 0 ) -#else - if ( trans & (SURF_TRANS33|SURF_TRANS66) ) -#endif - { - contents |= CONTENTS_Q2TRANSLUCENT; - if (contents & CONTENTS_SOLID) - { - contents &= ~CONTENTS_SOLID; - contents |= CONTENTS_WINDOW; - } - } - - return contents; -} //*/ - - -//============================================================================ - - - -/* -================= -ParseBrush -================= -* / -void ParseBrush (entity_t *mapent) -{ - mapbrush_t *b; - int i,j, k; - int mt; - side_t *side, *s2; - int planenum; - brush_texture_t td; -#ifdef SIN - textureref_t newref; -#endif - int planepts[3][3]; - - if (nummapbrushes == MAX_MAP_BRUSHES) - Error ("nummapbrushes == MAX_MAP_BRUSHES"); - - b = &mapbrushes[nummapbrushes]; - b->original_sides = &brushsides[nummapbrushsides]; - b->entitynum = num_entities-1; - b->brushnum = nummapbrushes - mapent->firstbrush; - - do - { - if (!GetToken (true)) - break; - if (!strcmp (token, "}") ) - break; - - if (nummapbrushsides == MAX_MAP_BRUSHSIDES) - Error ("MAX_MAP_BRUSHSIDES"); - side = &brushsides[nummapbrushsides]; - - // read the three point plane definition - for (i=0 ; i<3 ; i++) - { - if (i != 0) - GetToken (true); - if (strcmp (token, "(") ) - Error ("parsing brush"); - - for (j=0 ; j<3 ; j++) - { - GetToken (false); - planepts[i][j] = atoi(token); - } - - GetToken (false); - if (strcmp (token, ")") ) - Error ("parsing brush"); - - } - - - // - // read the texturedef - // - GetToken (false); - strcpy (td.name, token); - - GetToken (false); - td.shift[0] = atoi(token); - GetToken (false); - td.shift[1] = atoi(token); - GetToken (false); -#ifdef SIN - td.rotate = atof(token); -#else - td.rotate = atoi(token); -#endif - GetToken (false); - td.scale[0] = atof(token); - GetToken (false); - td.scale[1] = atof(token); - - // find default flags and values - mt = FindMiptex (td.name); -#ifdef SIN - // clear out the masks on newref - memset(&newref,0,sizeof(newref)); - // copy over the name - strcpy( newref.name, td.name ); - - ParseSurfaceInfo( &newref ); - MergeRefs( &bsp_textureref[mt], &newref, &td.tref ); - side->contents = td.tref.contents; - side->surf = td.tref.flags; -#else - td.flags = textureref[mt].flags; - td.value = textureref[mt].value; - side->contents = textureref[mt].contents; - side->surf = td.flags = textureref[mt].flags; - - if (TokenAvailable()) - { - GetToken (false); - side->contents = atoi(token); - GetToken (false); - side->surf = td.flags = atoi(token); - GetToken (false); - td.value = atoi(token); - } -#endif - - // translucent objects are automatically classified as detail -#ifdef SIN - if ( td.tref.translucence > 0 ) -#else - if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) -#endif - side->contents |= CONTENTS_DETAIL; - if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) - side->contents |= CONTENTS_DETAIL; - if (fulldetail) - side->contents &= ~CONTENTS_DETAIL; - if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) - | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST) ) ) - side->contents |= CONTENTS_SOLID; - - // hints and skips are never detail, and have no content - if (side->surf & (SURF_HINT|SURF_SKIP) ) - { - side->contents = 0; -#ifndef SIN // I think this is a bug of some kind - side->surf &= ~CONTENTS_DETAIL; -#endif - } - - // - // find the plane number - // - planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]); - if (planenum == -1) - { - printf ("Entity %i, Brush %i: plane with no normal\n" - , b->entitynum, b->brushnum); - continue; - } - - // - // see if the plane has been used already - // - for (k=0 ; knumsides ; k++) - { - s2 = b->original_sides + k; - if (s2->planenum == planenum) - { - printf ("Entity %i, Brush %i: duplicate plane\n" - , b->entitynum, b->brushnum); - break; - } - if ( s2->planenum == (planenum^1) ) - { - printf ("Entity %i, Brush %i: mirrored plane\n" - , b->entitynum, b->brushnum); - break; - } - } - if (k != b->numsides) - continue; // duplicated - - // - // keep this side - // - - side = b->original_sides + b->numsides; - side->planenum = planenum; -#ifdef SIN - side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], - &td, vec3_origin, &newref); - // - // save off lightinfo - // - side->lightinfo = LightinfoForBrushTexture ( &td ); -#else - side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], - &td, vec3_origin); - -#endif - - // save the td off in case there is an origin brush and we - // have to recalculate the texinfo - side_brushtextures[nummapbrushsides] = td; -#ifdef SIN - // save off the merged tref for animating textures - side_newrefs[nummapbrushsides] = newref; -#endif - - nummapbrushsides++; - b->numsides++; - } while (1); - - // get the content for the entire brush - b->contents = Sin_BrushContents (b); - - // allow detail brushes to be removed - if (nodetail && (b->contents & CONTENTS_DETAIL) ) - { - b->numsides = 0; - return; - } - - // allow water brushes to be removed - if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) - { - b->numsides = 0; - return; - } - - // create windings for sides and bounds for brush - MakeBrushWindings (b); - - // brushes that will not be visible at all will never be - // used as bsp splitters - if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) - { - c_clipbrushes++; - for (i=0 ; inumsides ; i++) - b->original_sides[i].texinfo = TEXINFO_NODE; - } - - // - // origin brushes are removed, but they set - // the rotation origin for the rest of the brushes - // in the entity. After the entire entity is parsed, - // the planenums and texinfos will be adjusted for - // the origin brush - // - if (b->contents & CONTENTS_ORIGIN) - { - char string[32]; - vec3_t origin; - - if (num_entities == 1) - { - Error ("Entity %i, Brush %i: origin brushes not allowed in world" - , b->entitynum, b->brushnum); - return; - } - - VectorAdd (b->mins, b->maxs, origin); - VectorScale (origin, 0.5, origin); - - sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); - SetKeyValue (&entities[b->entitynum], "origin", string); - - VectorCopy (origin, entities[b->entitynum].origin); - - // don't keep this brush - b->numsides = 0; - - return; - } - - AddBrushBevels (b); - - nummapbrushes++; - mapent->numbrushes++; -} //*/ - -/* -================ -MoveBrushesToWorld - -Takes all of the brushes from the current entity and -adds them to the world's brush list. - -Used by func_group and func_areaportal -================ -* / -void MoveBrushesToWorld (entity_t *mapent) -{ - int newbrushes; - int worldbrushes; - mapbrush_t *temp; - int i; - - // this is pretty gross, because the brushes are expected to be - // in linear order for each entity - - newbrushes = mapent->numbrushes; - worldbrushes = entities[0].numbrushes; - - temp = malloc(newbrushes*sizeof(mapbrush_t)); - memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t)); - -#if 0 // let them keep their original brush numbers - for (i=0 ; inumbrushes = 0; -} //*/ - -/* -================ -ParseMapEntity -================ -* / -qboolean Sin_ParseMapEntity (void) -{ - entity_t *mapent; - epair_t *e; - side_t *s; - int i, j; - int startbrush, startsides; - vec_t newdist; - mapbrush_t *b; - - if (!GetToken (true)) - return false; - - if (strcmp (token, "{") ) - Error ("ParseEntity: { not found"); - - if (num_entities == MAX_MAP_ENTITIES) - Error ("num_entities == MAX_MAP_ENTITIES"); - - startbrush = nummapbrushes; - startsides = nummapbrushsides; - - mapent = &entities[num_entities]; - num_entities++; - memset (mapent, 0, sizeof(*mapent)); - mapent->firstbrush = nummapbrushes; - mapent->numbrushes = 0; -// mapent->portalareas[0] = -1; -// mapent->portalareas[1] = -1; - - do - { - if (!GetToken (true)) - Error ("ParseEntity: EOF without closing brace"); - if (!strcmp (token, "}") ) - break; - if (!strcmp (token, "{") ) - ParseBrush (mapent); - else - { - e = ParseEpair (); -#ifdef SIN - //HACK HACK HACK - // MED Gotta do this here - if ( !stricmp(e->key, "surfacefile") ) - { - if (!surfacefile[0]) - { - strcpy( surfacefile, e->value ); - } - printf ("--- ParseSurfaceFile ---\n"); - printf ("Surface script: %s\n", surfacefile); - if (!ParseSurfaceFile(surfacefile)) - { - Error ("Script file not found: %s\n", surfacefile); - } - } -#endif - e->next = mapent->epairs; - mapent->epairs = e; - } - } while (1); - -#ifdef SIN - if (!(strlen(ValueForKey(mapent, "origin"))) && ((num_entities-1) != 0)) - { - mapbrush_t *brush; - vec3_t origin; - char string[32]; - vec3_t mins, maxs; - int start, end; - // Calculate bounds - - start = mapent->firstbrush; - end = start + mapent->numbrushes; - ClearBounds (mins, maxs); - - for (j=start ; jnumsides) - continue; // not a real brush (origin brush) - shouldn't happen - AddPointToBounds (brush->mins, mins, maxs); - AddPointToBounds (brush->maxs, mins, maxs); - } - - // Set the origin to be the centroid of the entity. - VectorAdd ( mins, maxs, origin); - VectorScale( origin, 0.5f, origin ); - - sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); - SetKeyValue ( mapent, "origin", string); -// qprintf("Setting origin to %s\n",string); - } -#endif - - GetVectorForKey (mapent, "origin", mapent->origin); - -#ifdef SIN - if ( - (!strcmp ("func_areaportal", ValueForKey (mapent, "classname"))) || - (!strcmp ("func_group", ValueForKey (mapent, "classname"))) || - (!strcmp ("detail", ValueForKey (mapent, "classname")) && !entitydetails) - ) - { - VectorClear( mapent->origin ); - } -#endif - - // - // if there was an origin brush, offset all of the planes and texinfo - // - if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2]) - { - for (i=0 ; inumbrushes ; i++) - { - b = &mapbrushes[mapent->firstbrush + i]; - for (j=0 ; jnumsides ; j++) - { - s = &b->original_sides[j]; - newdist = mapplanes[s->planenum].dist - - DotProduct (mapplanes[s->planenum].normal, mapent->origin); - s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist); -#ifdef SIN - s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], - &side_brushtextures[s-brushsides], mapent->origin, &side_newrefs[s-brushsides]); - // - // save off lightinfo - // - s->lightinfo = LightinfoForBrushTexture ( &side_brushtextures[s-brushsides] ); -#else - s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], - &side_brushtextures[s-brushsides], mapent->origin); -#endif - } - MakeBrushWindings (b); - } - } - - // group entities are just for editor convenience - // toss all brushes into the world entity - if (!strcmp ("func_group", ValueForKey (mapent, "classname"))) - { - MoveBrushesToWorld (mapent); - mapent->numbrushes = 0; - mapent->wasdetail = true; - FreeValueKeys( mapent ); - return true; - } -#ifdef SIN - // detail entities are just for editor convenience - // toss all brushes into the world entity as detail brushes - if (!strcmp ("detail", ValueForKey (mapent, "classname")) && !entitydetails) - { - for (i=0 ; inumbrushes ; i++) - { - int j; - side_t * s; - b = &mapbrushes[mapent->firstbrush + i]; - if (nodetail) - { - b->numsides = 0; - continue; - } - if (!fulldetail) - { - // set the contents for the entire brush - b->contents |= CONTENTS_DETAIL; - // set the contents in the sides as well - for (j=0, s=b->original_sides ; jnumsides ; j++,s++) - { - s->contents |= CONTENTS_DETAIL; - } - } - else - { - // set the contents for the entire brush - b->contents |= CONTENTS_SOLID; - // set the contents in the sides as well - for (j=0, s=b->original_sides ; jnumsides ; j++,s++) - { - s->contents |= CONTENTS_SOLID; - } - } - } - MoveBrushesToWorld (mapent); - mapent->wasdetail = true; - FreeValueKeys( mapent ); - // kill off the entity - // num_entities--; - return true; - } -#endif - - // areaportal entities move their brushes, but don't eliminate - // the entity - if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname"))) - { - char str[128]; - - if (mapent->numbrushes != 1) - Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1); - - b = &mapbrushes[nummapbrushes-1]; - b->contents = CONTENTS_AREAPORTAL; - c_areaportals++; - mapent->areaportalnum = c_areaportals; - // set the portal number as "style" - sprintf (str, "%i", c_areaportals); - SetKeyValue (mapent, "style", str); - MoveBrushesToWorld (mapent); - return true; - } - - return true; -} //end of the function Sin_ParseMapEntity */ - -//=================================================================== - -/* -================ -LoadMapFile -================ -* / -void Sin_LoadMapFile (char *filename) -{ - int i; -#ifdef SIN - int num_detailsides=0; - int num_detailbrushes=0; - int num_worldsides=0; - int num_worldbrushes=0; - int j,k; -#endif - - qprintf ("--- LoadMapFile ---\n"); - - LoadScriptFile (filename); - - nummapbrushsides = 0; - num_entities = 0; - - while (ParseMapEntity ()) - { - } - - ClearBounds (map_mins, map_maxs); - for (i=0 ; i 4096) - continue; // no valid points - AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs); - AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs); - } -#ifdef SIN - for (j=0; jnumsides && b->contents & CONTENTS_DETAIL) - num_detailbrushes++; - else if (b->numsides) - num_worldbrushes++; - for (k=0, s=b->original_sides ; knumsides ; k++,s++) - { - if (s->contents & CONTENTS_DETAIL) - num_detailsides++; - else - num_worldsides++; - } - } - } -#endif - - qprintf ("%5i brushes\n", nummapbrushes); - qprintf ("%5i clipbrushes\n", c_clipbrushes); - qprintf ("%5i total sides\n", nummapbrushsides); - qprintf ("%5i boxbevels\n", c_boxbevels); - qprintf ("%5i edgebevels\n", c_edgebevels); - qprintf ("%5i entities\n", num_entities); - qprintf ("%5i planes\n", nummapplanes); - qprintf ("%5i areaportals\n", c_areaportals); - qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2], - map_maxs[0],map_maxs[1],map_maxs[2]); -#ifdef SIN - qprintf ("%5i detailbrushes\n", num_detailbrushes); - qprintf ("%5i worldbrushes\n", num_worldbrushes); - qprintf ("%5i detailsides\n", num_detailsides); - qprintf ("%5i worldsides\n", num_worldsides); -#endif - -} //end of the function Sin_LoadMap */ - - -#ifdef ME //Begin MAP loading from BSP file -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Sin_CreateMapTexinfo(void) -{ - int i; - vec_t defaultvec[4] = {1, 0, 0, 0}; - - memcpy(map_texinfo[0].vecs[0], defaultvec, sizeof(defaultvec)); - memcpy(map_texinfo[0].vecs[1], defaultvec, sizeof(defaultvec)); - map_texinfo[0].flags = 0; - map_texinfo[0].value = 0; - strcpy(map_texinfo[0].texture, "generic/misc/red"); //no texture - map_texinfo[0].nexttexinfo = -1; - for (i = 1; i < sin_numtexinfo; i++) - { - memcpy(map_texinfo[i].vecs, sin_texinfo[i].vecs, sizeof(float) * 2 * 4); - map_texinfo[i].flags = sin_texinfo[i].flags; - map_texinfo[i].value = 0; - strcpy(map_texinfo[i].texture, sin_texinfo[i].texture); - map_texinfo[i].nexttexinfo = -1; - } //end for -} //end of the function Sin_CreateMapTexinfo -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Sin_SetLeafBrushesModelNumbers(int leafnum, int modelnum) -{ - int i, brushnum; - sin_dleaf_t *leaf; - - leaf = &sin_dleafs[leafnum]; - for (i = 0; i < leaf->numleafbrushes; i++) - { - brushnum = sin_dleafbrushes[leaf->firstleafbrush + i]; - brushmodelnumbers[brushnum] = modelnum; - dbrushleafnums[brushnum] = leafnum; - } //end for -} //end of the function Sin_SetLeafBrushesModelNumbers -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Sin_InitNodeStack(void) -{ - nodestackptr = nodestack; - nodestacksize = 0; -} //end of the function Sin_InitNodeStack -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Sin_PushNodeStack(int num) -{ - *nodestackptr = num; - nodestackptr++; - nodestacksize++; - // - if (nodestackptr >= &nodestack[NODESTACKSIZE]) - { - Error("Sin_PushNodeStack: stack overflow\n"); - } //end if -} //end of the function Sin_PushNodeStack -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int Sin_PopNodeStack(void) -{ - //if the stack is empty - if (nodestackptr <= nodestack) return -1; - //decrease stack pointer - nodestackptr--; - nodestacksize--; - //return the top value from the stack - return *nodestackptr; -} //end of the function Sin_PopNodeStack -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Sin_SetBrushModelNumbers(entity_t *mapent) -{ - int n, pn; - int leafnum; - - // - Sin_InitNodeStack(); - //head node (root) of the bsp tree - n = sin_dmodels[mapent->modelnum].headnode; - pn = 0; - - do - { - //if we are in a leaf (negative node number) - if (n < 0) - { - //number of the leaf - leafnum = (-n) - 1; - //set the brush numbers - Sin_SetLeafBrushesModelNumbers(leafnum, mapent->modelnum); - //walk back into the tree to find a second child to continue with - for (pn = Sin_PopNodeStack(); pn >= 0; n = pn, pn = Sin_PopNodeStack()) - { - //if we took the first child at the parent node - if (sin_dnodes[pn].children[0] == n) break; - } //end for - //if the stack wasn't empty (if not processed whole tree) - if (pn >= 0) - { - //push the parent node again - Sin_PushNodeStack(pn); - //we proceed with the second child of the parent node - n = sin_dnodes[pn].children[1]; - } //end if - } //end if - else - { - //push the current node onto the stack - Sin_PushNodeStack(n); - //walk forward into the tree to the first child - n = sin_dnodes[n].children[0]; - } //end else - } while(pn >= 0); -} //end of the function Sin_SetBrushModelNumbers -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Sin_BSPBrushToMapBrush(sin_dbrush_t *bspbrush, entity_t *mapent) -{ - mapbrush_t *b; - int i, k, n; - side_t *side, *s2; - int planenum; - sin_dbrushside_t *bspbrushside; - sin_dplane_t *bspplane; - - if (nummapbrushes >= MAX_MAPFILE_BRUSHES) - Error ("nummapbrushes >= MAX_MAPFILE_BRUSHES"); - - b = &mapbrushes[nummapbrushes]; - b->original_sides = &brushsides[nummapbrushsides]; - b->entitynum = mapent-entities; - b->brushnum = nummapbrushes - mapent->firstbrush; - b->leafnum = dbrushleafnums[bspbrush - sin_dbrushes]; - - for (n = 0; n < bspbrush->numsides; n++) - { - //pointer to the bsp brush side - bspbrushside = &sin_dbrushsides[bspbrush->firstside + n]; - - if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES) - { - Error ("MAX_MAPFILE_BRUSHSIDES"); - } //end if - //pointer to the map brush side - side = &brushsides[nummapbrushsides]; - //if the BSP brush side is textured - if (sin_dbrushsidetextured[bspbrush->firstside + n]) side->flags |= SFL_TEXTURED; - else side->flags &= ~SFL_TEXTURED; - //ME: can get side contents and surf directly from BSP file - side->contents = bspbrush->contents; - //if the texinfo is TEXINFO_NODE - if (bspbrushside->texinfo < 0) side->surf = 0; - else side->surf = sin_texinfo[bspbrushside->texinfo].flags; - - // translucent objects are automatically classified as detail - if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) - side->contents |= CONTENTS_DETAIL; - if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) - side->contents |= CONTENTS_DETAIL; - if (fulldetail) - side->contents &= ~CONTENTS_DETAIL; - if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) - | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST) ) ) - side->contents |= CONTENTS_SOLID; - - // hints and skips are never detail, and have no content - if (side->surf & (SURF_HINT|SURF_SKIP) ) - { - side->contents = 0; - side->surf &= ~CONTENTS_DETAIL; - } - - //ME: get a plane for this side - bspplane = &sin_dplanes[bspbrushside->planenum]; - planenum = FindFloatPlane(bspplane->normal, bspplane->dist); - // - // see if the plane has been used already - // - //ME: this really shouldn't happen!!! - //ME: otherwise the bsp file is corrupted?? - //ME: still it seems to happen, maybe Johny Boy's - //ME: brush bevel adding is crappy ? - for (k = 0; k < b->numsides; k++) - { - s2 = b->original_sides + k; - if (s2->planenum == planenum) - { - Log_Print("Entity %i, Brush %i: duplicate plane\n" - , b->entitynum, b->brushnum); - break; - } - if ( s2->planenum == (planenum^1) ) - { - Log_Print("Entity %i, Brush %i: mirrored plane\n" - , b->entitynum, b->brushnum); - break; - } - } - if (k != b->numsides) - continue; // duplicated - - // - // keep this side - // - //ME: reset pointer to side, why? hell I dunno (pointer is set above already) - side = b->original_sides + b->numsides; - //ME: store the plane number - side->planenum = planenum; - //ME: texinfo is already stored when bsp is loaded - //NOTE: check for TEXINFO_NODE, otherwise crash in Sin_BrushContents - if (bspbrushside->texinfo < 0) side->texinfo = 0; - else side->texinfo = bspbrushside->texinfo; - - // save the td off in case there is an origin brush and we - // have to recalculate the texinfo - // ME: don't need to recalculate because it's already done - // (for non-world entities) in the BSP file -// side_brushtextures[nummapbrushsides] = td; - - nummapbrushsides++; - b->numsides++; - } //end for - - // get the content for the entire brush - b->contents = bspbrush->contents; - Sin_BrushContents(b); - - if (BrushExists(b)) - { - c_squattbrushes++; - b->numsides = 0; - return; - } //end if - - //if we're creating AAS - if (create_aas) - { - //create the AAS brushes from this brush, don't add brush bevels - AAS_CreateMapBrushes(b, mapent, false); - return; - } //end if - - // allow detail brushes to be removed - if (nodetail && (b->contents & CONTENTS_DETAIL) ) - { - b->numsides = 0; - return; - } //end if - - // allow water brushes to be removed - if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) - { - b->numsides = 0; - return; - } //end if - - // create windings for sides and bounds for brush - MakeBrushWindings(b); - - //mark brushes without winding or with a tiny window as bevels - MarkBrushBevels(b); - - // brushes that will not be visible at all will never be - // used as bsp splitters - if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) - { - c_clipbrushes++; - for (i = 0; i < b->numsides; i++) - b->original_sides[i].texinfo = TEXINFO_NODE; - } //end for - - // - // origin brushes are removed, but they set - // the rotation origin for the rest of the brushes - // in the entity. After the entire entity is parsed, - // the planenums and texinfos will be adjusted for - // the origin brush - // - //ME: not needed because the entities in the BSP file already - // have an origin set -// if (b->contents & CONTENTS_ORIGIN) -// { -// char string[32]; -// vec3_t origin; -// -// if (num_entities == 1) -// { -// Error ("Entity %i, Brush %i: origin brushes not allowed in world" -// , b->entitynum, b->brushnum); -// return; -// } -// -// VectorAdd (b->mins, b->maxs, origin); -// VectorScale (origin, 0.5, origin); -// -// sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); -// SetKeyValue (&entities[b->entitynum], "origin", string); -// -// VectorCopy (origin, entities[b->entitynum].origin); -// -// // don't keep this brush -// b->numsides = 0; -// -// return; -// } - - //ME: the bsp brushes already have bevels, so we won't try to - // add them again (especially since Johny Boy's bevel adding might - // be crappy) -// AddBrushBevels(b); - - nummapbrushes++; - mapent->numbrushes++; -} //end of the function Sin_BSPBrushToMapBrush -//=========================================================================== -//=========================================================================== -void Sin_ParseBSPBrushes(entity_t *mapent) -{ - int i, testnum = 0; - - //give all the brushes that belong to this entity the number of the - //BSP model used by this entity - Sin_SetBrushModelNumbers(mapent); - //now parse all the brushes with the correct mapent->modelnum - for (i = 0; i < sin_numbrushes; i++) - { - if (brushmodelnumbers[i] == mapent->modelnum) - { - testnum++; - Sin_BSPBrushToMapBrush(&sin_dbrushes[i], mapent); - } //end if - } //end for -} //end of the function Sin_ParseBSPBrushes -//=========================================================================== -//=========================================================================== -qboolean Sin_ParseBSPEntity(int entnum) -{ - entity_t *mapent; - char *model; - int startbrush, startsides; - - startbrush = nummapbrushes; - startsides = nummapbrushsides; - - mapent = &entities[entnum];//num_entities]; - mapent->firstbrush = nummapbrushes; - mapent->numbrushes = 0; - mapent->modelnum = -1; //-1 = no model - - model = ValueForKey(mapent, "model"); - if (model && *model == '*') - { - mapent->modelnum = atoi(&model[1]); - //Log_Print("model = %s\n", model); - //Log_Print("mapent->modelnum = %d\n", mapent->modelnum); - } //end if - - GetVectorForKey(mapent, "origin", mapent->origin); - - //if this is the world entity it has model number zero - //the world entity has no model key - if (!strcmp("worldspawn", ValueForKey(mapent, "classname"))) - { - mapent->modelnum = 0; - } //end if - //if the map entity has a BSP model (a modelnum of -1 is used for - //entities that aren't using a BSP model) - if (mapent->modelnum >= 0) - { - //parse the bsp brushes - Sin_ParseBSPBrushes(mapent); - } //end if - // - //the origin of the entity is already taken into account - // - //func_group entities can't be in the bsp file - // - //check out the func_areaportal entities - if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname"))) - { - c_areaportals++; - mapent->areaportalnum = c_areaportals; - return true; - } //end if - return true; -} //end of the function Sin_ParseBSPEntity -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Sin_LoadMapFromBSP(char *filename, int offset, int length) -{ - int i; - - Log_Print("-- Sin_LoadMapFromBSP --\n"); - //loaded map type - loadedmaptype = MAPTYPE_SIN; - - Log_Print("Loading map from %s...\n", filename); - //load the bsp file - Sin_LoadBSPFile(filename, offset, length); - - //create an index from bsp planes to map planes - //DPlanes2MapPlanes(); - //clear brush model numbers - for (i = 0; i < MAX_MAPFILE_BRUSHES; i++) - brushmodelnumbers[i] = -1; - - nummapbrushsides = 0; - num_entities = 0; - - Sin_ParseEntities(); - // - for (i = 0; i < num_entities; i++) - { - Sin_ParseBSPEntity(i); - } //end for - - //get the map mins and maxs from the world model - ClearBounds(map_mins, map_maxs); - for (i = 0; i < entities[0].numbrushes; i++) - { - if (mapbrushes[i].mins[0] > 4096) - continue; //no valid points - AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs); - AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs); - } //end for - // - Sin_CreateMapTexinfo(); -} //end of the function Sin_LoadMapFromBSP - -void Sin_ResetMapLoading(void) -{ - //reset for map loading from bsp - memset(nodestack, 0, NODESTACKSIZE * sizeof(int)); - nodestackptr = NULL; - nodestacksize = 0; - memset(brushmodelnumbers, 0, MAX_MAPFILE_BRUSHES * sizeof(int)); -} //end of the function Sin_ResetMapLoading - -//End MAP loading from BSP file - -#endif //ME diff --git a/tools/quake3/bspc/nodraw.c b/tools/quake3/bspc/nodraw.c deleted file mode 100644 index 7503a48d..00000000 --- a/tools/quake3/bspc/nodraw.c +++ /dev/null @@ -1,47 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" - -vec3_t draw_mins, draw_maxs; -qboolean drawflag; - -void Draw_ClearWindow (void) -{ -} - -//============================================================ - -#define GLSERV_PORT 25001 - - -void GLS_BeginScene (void) -{ -} - -void GLS_Winding (winding_t *w, int code) -{ -} - -void GLS_EndScene (void) -{ -} diff --git a/tools/quake3/bspc/portals.c b/tools/quake3/bspc/portals.c deleted file mode 100644 index cde4f60b..00000000 --- a/tools/quake3/bspc/portals.c +++ /dev/null @@ -1,1297 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "l_mem.h" - -int c_active_portals; -int c_peak_portals; -int c_boundary; -int c_boundary_sides; -int c_portalmemory; - -//portal_t *portallist = NULL; -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -portal_t *AllocPortal (void) -{ - portal_t *p; - - p = GetMemory(sizeof(portal_t)); - memset (p, 0, sizeof(portal_t)); - - if (numthreads == 1) - { - c_active_portals++; - if (c_active_portals > c_peak_portals) - { - c_peak_portals = c_active_portals; - } //end if - c_portalmemory += MemorySize(p); - } //end if - -// p->nextportal = portallist; -// portallist = p; - - return p; -} //end of the function AllocPortal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreePortal (portal_t *p) -{ - if (p->winding) FreeWinding(p->winding); - if (numthreads == 1) - { - c_active_portals--; - c_portalmemory -= MemorySize(p); - } //end if - FreeMemory(p); -} //end of the function FreePortal -//=========================================================================== -// Returns the single content bit of the -// strongest visible content present -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int VisibleContents (int contents) -{ - int i; - - for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1) - if (contents & i ) - return i; - - return 0; -} //end of the function VisibleContents -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int ClusterContents (node_t *node) -{ - int c1, c2, c; - - if (node->planenum == PLANENUM_LEAF) - return node->contents; - - c1 = ClusterContents(node->children[0]); - c2 = ClusterContents(node->children[1]); - c = c1|c2; - - // a cluster may include some solid detail areas, but - // still be seen into - if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) ) - c &= ~CONTENTS_SOLID; - return c; -} //end of the function ClusterContents - -//=========================================================================== -// Returns true if the portal is empty or translucent, allowing -// the PVS calculation to see through it. -// The nodes on either side of the portal may actually be clusters, -// not leaves, so all contents should be ored together -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean Portal_VisFlood (portal_t *p) -{ - int c1, c2; - - if (!p->onnode) - return false; // to global outsideleaf - - c1 = ClusterContents(p->nodes[0]); - c2 = ClusterContents(p->nodes[1]); - - if (!VisibleContents (c1^c2)) - return true; - - if (c1 & (CONTENTS_Q2TRANSLUCENT|CONTENTS_DETAIL)) - c1 = 0; - if (c2 & (CONTENTS_Q2TRANSLUCENT|CONTENTS_DETAIL)) - c2 = 0; - - if ( (c1|c2) & CONTENTS_SOLID ) - return false; // can't see through solid - - if (! (c1 ^ c2)) - return true; // identical on both sides - - if (!VisibleContents (c1^c2)) - return true; - return false; -} //end of the function Portal_VisFlood -//=========================================================================== -// The entity flood determines which areas are -// "outside" on the map, which are then filled in. -// Flowing from side s to side !s -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean Portal_EntityFlood (portal_t *p, int s) -{ - if (p->nodes[0]->planenum != PLANENUM_LEAF - || p->nodes[1]->planenum != PLANENUM_LEAF) - Error ("Portal_EntityFlood: not a leaf"); - - // can never cross to a solid - if ( (p->nodes[0]->contents & CONTENTS_SOLID) - || (p->nodes[1]->contents & CONTENTS_SOLID) ) - return false; - - // can flood through everything else - return true; -} - - -//============================================================================= - -int c_tinyportals; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void AddPortalToNodes (portal_t *p, node_t *front, node_t *back) -{ - if (p->nodes[0] || p->nodes[1]) - Error ("AddPortalToNode: allready included"); - - p->nodes[0] = front; - p->next[0] = front->portals; - front->portals = p; - - p->nodes[1] = back; - p->next[1] = back->portals; - back->portals = p; -} //end of the function AddPortalToNodes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void RemovePortalFromNode (portal_t *portal, node_t *l) -{ - portal_t **pp, *t; - - int s, i, n; - portal_t *p; - portal_t *portals[4096]; - -// remove reference to the current portal - pp = &l->portals; - while (1) - { - t = *pp; - if (!t) - Error ("RemovePortalFromNode: portal not in leaf"); - - if ( t == portal ) - break; - - if (t->nodes[0] == l) - pp = &t->next[0]; - else if (t->nodes[1] == l) - pp = &t->next[1]; - else - Error ("RemovePortalFromNode: portal not bounding leaf"); - } - - if (portal->nodes[0] == l) - { - *pp = portal->next[0]; - portal->nodes[0] = NULL; - } //end if - else if (portal->nodes[1] == l) - { - *pp = portal->next[1]; - portal->nodes[1] = NULL; - } //end else if - else - { - Error("RemovePortalFromNode: mislinked portal"); - } //end else -//#ifdef ME - n = 0; - for (p = l->portals; p; p = p->next[s]) - { - for (i = 0; i < n; i++) - { - if (p == portals[i]) Error("RemovePortalFromNode: circular linked\n"); - } //end for - if (p->nodes[0] != l && p->nodes[1] != l) - { - Error("RemovePortalFromNodes: portal does not belong to node\n"); - } //end if - portals[n] = p; - s = (p->nodes[1] == l); -// if (++n >= 4096) Error("RemovePortalFromNode: more than 4096 portals\n"); - } //end for -//#endif -} //end of the function RemovePortalFromNode -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintPortal (portal_t *p) -{ - int i; - winding_t *w; - - w = p->winding; - for (i=0 ; inumpoints ; i++) - printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0] - , w->p[i][1], w->p[i][2]); -} //end of the function PrintPortal -//=========================================================================== -// The created portals will face the global outside_node -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#define SIDESPACE 8 - -void MakeHeadnodePortals (tree_t *tree) -{ - vec3_t bounds[2]; - int i, j, n; - portal_t *p, *portals[6]; - plane_t bplanes[6], *pl; - node_t *node; - - node = tree->headnode; - -// pad with some space so there will never be null volume leaves - for (i=0 ; i<3 ; i++) - { - bounds[0][i] = tree->mins[i] - SIDESPACE; - bounds[1][i] = tree->maxs[i] + SIDESPACE; - if ( bounds[0][i] > bounds[1][i] ) { - Error("empty BSP tree"); - } - } - - tree->outside_node.planenum = PLANENUM_LEAF; - tree->outside_node.brushlist = NULL; - tree->outside_node.portals = NULL; - tree->outside_node.contents = 0; - - for (i=0 ; i<3 ; i++) - for (j=0 ; j<2 ; j++) - { - n = j*3 + i; - - p = AllocPortal (); - portals[n] = p; - - pl = &bplanes[n]; - memset (pl, 0, sizeof(*pl)); - if (j) - { - pl->normal[i] = -1; - pl->dist = -bounds[j][i]; - } - else - { - pl->normal[i] = 1; - pl->dist = bounds[j][i]; - } - p->plane = *pl; - p->winding = BaseWindingForPlane (pl->normal, pl->dist); - AddPortalToNodes (p, node, &tree->outside_node); - } - -// clip the basewindings by all the other planes - for (i=0 ; i<6 ; i++) - { - for (j=0 ; j<6 ; j++) - { - if (j == i) continue; - ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON); - } //end for - } //end for -} //end of the function MakeHeadNodePortals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#define BASE_WINDING_EPSILON 0.001 -#define SPLIT_WINDING_EPSILON 0.001 - -winding_t *BaseWindingForNode (node_t *node) -{ - winding_t *w; - node_t *n; - plane_t *plane; - vec3_t normal; - vec_t dist; - - w = BaseWindingForPlane (mapplanes[node->planenum].normal - , mapplanes[node->planenum].dist); - - // clip by all the parents - for (n=node->parent ; n && w ; ) - { - plane = &mapplanes[n->planenum]; - - if (n->children[0] == node) - { // take front - ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON); - } - else - { // take back - VectorSubtract (vec3_origin, plane->normal, normal); - dist = -plane->dist; - ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON); - } - node = n; - n = n->parent; - } - - return w; -} //end of the function BaseWindingForNode -//=========================================================================== -// create the new portal by taking the full plane winding for the cutting -// plane and clipping it by all of parents of this node -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WindingIsTiny (winding_t *w); - -void MakeNodePortal (node_t *node) -{ - portal_t *new_portal, *p; - winding_t *w; - vec3_t normal; - float dist; - int side; - - w = BaseWindingForNode (node); - - // clip the portal by all the other portals in the node - for (p = node->portals; p && w; p = p->next[side]) - { - if (p->nodes[0] == node) - { - side = 0; - VectorCopy (p->plane.normal, normal); - dist = p->plane.dist; - } //end if - else if (p->nodes[1] == node) - { - side = 1; - VectorSubtract (vec3_origin, p->plane.normal, normal); - dist = -p->plane.dist; - } //end else if - else - { - Error ("MakeNodePortal: mislinked portal"); - } //end else - ChopWindingInPlace (&w, normal, dist, 0.1); - } //end for - - if (!w) - { - return; - } //end if - - if (WindingIsTiny (w)) - { - c_tinyportals++; - FreeWinding(w); - return; - } //end if - -#ifdef DEBUG -/* //NOTE: don't use this winding ok check - // all the invalid windings only have a degenerate edge - if (WindingError(w)) - { - Log_Print("MakeNodePortal: %s\n", WindingErrorString()); - FreeWinding(w); - return; - } //end if*/ -#endif //DEBUG - - - new_portal = AllocPortal(); - new_portal->plane = mapplanes[node->planenum]; - -#ifdef ME - new_portal->planenum = node->planenum; -#endif //ME - - new_portal->onnode = node; - new_portal->winding = w; - AddPortalToNodes (new_portal, node->children[0], node->children[1]); -} //end of the function MakeNodePortal -//=========================================================================== -// Move or split the portals that bound node so that the node's -// children have portals instead of node. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void SplitNodePortals (node_t *node) -{ - portal_t *p, *next_portal, *new_portal; - node_t *f, *b, *other_node; - int side; - plane_t *plane; - winding_t *frontwinding, *backwinding; - - plane = &mapplanes[node->planenum]; - f = node->children[0]; - b = node->children[1]; - - for (p = node->portals ; p ; p = next_portal) - { - if (p->nodes[0] == node) side = 0; - else if (p->nodes[1] == node) side = 1; - else Error ("SplitNodePortals: mislinked portal"); - next_portal = p->next[side]; - - other_node = p->nodes[!side]; - RemovePortalFromNode (p, p->nodes[0]); - RemovePortalFromNode (p, p->nodes[1]); - -// -// cut the portal into two portals, one on each side of the cut plane -// - ClipWindingEpsilon (p->winding, plane->normal, plane->dist, - SPLIT_WINDING_EPSILON, &frontwinding, &backwinding); - - if (frontwinding && WindingIsTiny(frontwinding)) - { - FreeWinding (frontwinding); - frontwinding = NULL; - c_tinyportals++; - } //end if - - if (backwinding && WindingIsTiny(backwinding)) - { - FreeWinding (backwinding); - backwinding = NULL; - c_tinyportals++; - } //end if - -#ifdef DEBUG -/* //NOTE: don't use this winding ok check - // all the invalid windings only have a degenerate edge - if (frontwinding && WindingError(frontwinding)) - { - Log_Print("SplitNodePortals: front %s\n", WindingErrorString()); - FreeWinding(frontwinding); - frontwinding = NULL; - } //end if - if (backwinding && WindingError(backwinding)) - { - Log_Print("SplitNodePortals: back %s\n", WindingErrorString()); - FreeWinding(backwinding); - backwinding = NULL; - } //end if*/ -#endif //DEBUG - - if (!frontwinding && !backwinding) - { // tiny windings on both sides - continue; - } - - if (!frontwinding) - { - FreeWinding (backwinding); - if (side == 0) AddPortalToNodes (p, b, other_node); - else AddPortalToNodes (p, other_node, b); - continue; - } - if (!backwinding) - { - FreeWinding (frontwinding); - if (side == 0) AddPortalToNodes (p, f, other_node); - else AddPortalToNodes (p, other_node, f); - continue; - } - - // the winding is split - new_portal = AllocPortal(); - *new_portal = *p; - new_portal->winding = backwinding; - FreeWinding (p->winding); - p->winding = frontwinding; - - if (side == 0) - { - AddPortalToNodes (p, f, other_node); - AddPortalToNodes (new_portal, b, other_node); - } //end if - else - { - AddPortalToNodes (p, other_node, f); - AddPortalToNodes (new_portal, other_node, b); - } //end else - } - - node->portals = NULL; -} //end of the function SplitNodePortals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CalcNodeBounds (node_t *node) -{ - portal_t *p; - int s; - int i; - - // calc mins/maxs for both leaves and nodes - ClearBounds (node->mins, node->maxs); - for (p = node->portals ; p ; p = p->next[s]) - { - s = (p->nodes[1] == node); - for (i=0 ; iwinding->numpoints ; i++) - AddPointToBounds (p->winding->p[i], node->mins, node->maxs); - } -} //end of the function CalcNodeBounds -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int c_numportalizednodes; - -void MakeTreePortals_r (node_t *node) -{ - int i; - -#ifdef ME - qprintf("\r%6d", ++c_numportalizednodes); - if (cancelconversion) return; -#endif //ME - - CalcNodeBounds (node); - if (node->mins[0] >= node->maxs[0]) - { - Log_Print("WARNING: node without a volume\n"); - } - - for (i=0 ; i<3 ; i++) - { - if (node->mins[i] < -MAX_MAP_BOUNDS || node->maxs[i] > MAX_MAP_BOUNDS) - { - Log_Print("WARNING: node with unbounded volume\n"); - break; - } - } - if (node->planenum == PLANENUM_LEAF) - return; - - MakeNodePortal (node); - SplitNodePortals (node); - - MakeTreePortals_r (node->children[0]); - MakeTreePortals_r (node->children[1]); -} //end of the function MakeTreePortals_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void MakeTreePortals(tree_t *tree) -{ - -#ifdef ME - Log_Print("---- Node Portalization ----\n"); - c_numportalizednodes = 0; - c_portalmemory = 0; - qprintf("%6d nodes portalized", c_numportalizednodes); -#endif //ME - - MakeHeadnodePortals(tree); - MakeTreePortals_r(tree->headnode); - -#ifdef ME - qprintf("\n"); - Log_Write("%6d nodes portalized\r\n", c_numportalizednodes); - Log_Print("%6d tiny portals\r\n", c_tinyportals); - Log_Print("%6d KB of portal memory\r\n", c_portalmemory >> 10); - Log_Print("%6i KB of winding memory\r\n", WindingMemory() >> 10); -#endif //ME -} //end of the function MakeTreePortals - -/* -========================================================= - -FLOOD ENTITIES - -========================================================= -*/ -//#define P_NODESTACK - -node_t *p_firstnode; -node_t *p_lastnode; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#ifdef P_NODESTACK -void P_AddNodeToList(node_t *node) -{ - node->next = p_firstnode; - p_firstnode = node; - if (!p_lastnode) p_lastnode = node; -} //end of the function P_AddNodeToList -#else //it's a node queue -//add the node to the end of the node list -void P_AddNodeToList(node_t *node) -{ - node->next = NULL; - if (p_lastnode) p_lastnode->next = node; - else p_firstnode = node; - p_lastnode = node; -} //end of the function P_AddNodeToList -#endif //P_NODESTACK -//=========================================================================== -// get the first node from the front of the node list -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -node_t *P_NextNodeFromList(void) -{ - node_t *node; - - node = p_firstnode; - if (p_firstnode) p_firstnode = p_firstnode->next; - if (!p_firstnode) p_lastnode = NULL; - return node; -} //end of the function P_NextNodeFromList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FloodPortals(node_t *firstnode) -{ - node_t *node; - portal_t *p; - int s; - - firstnode->occupied = 1; - P_AddNodeToList(firstnode); - - for (node = P_NextNodeFromList(); node; node = P_NextNodeFromList()) - { - for (p = node->portals; p; p = p->next[s]) - { - s = (p->nodes[1] == node); - //if the node at the other side of the portal is occupied already - if (p->nodes[!s]->occupied) continue; - //if it isn't possible to flood through this portal - if (!Portal_EntityFlood(p, s)) continue; - // - p->nodes[!s]->occupied = node->occupied + 1; - // - P_AddNodeToList(p->nodes[!s]); - } //end for - } //end for -} //end of the function FloodPortals -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int numrec; - -void FloodPortals_r (node_t *node, int dist) -{ - portal_t *p; - int s; -// int i; - - Log_Print("\r%6d", ++numrec); - - if (node->occupied) Error("FloodPortals_r: node already occupied\n"); - if (!node) - { - Error("FloodPortals_r: NULL node\n"); - } //end if*/ - node->occupied = dist; - - for (p = node->portals; p; p = p->next[s]) - { - s = (p->nodes[1] == node); - //if the node at the other side of the portal is occupied already - if (p->nodes[!s]->occupied) continue; - //if it isn't possible to flood through this portal - if (!Portal_EntityFlood(p, s)) continue; - //flood recursively through the current portal - FloodPortals_r(p->nodes[!s], dist+1); - } //end for - Log_Print("\r%6d", --numrec); -} //end of the function FloodPortals_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant) -{ - node_t *node; - vec_t d; - plane_t *plane; - - //find the leaf to start in - node = headnode; - while(node->planenum != PLANENUM_LEAF) - { - if (node->planenum < 0 || node->planenum > nummapplanes) - { - Error("PlaceOccupant: invalid node->planenum\n"); - } //end if - plane = &mapplanes[node->planenum]; - d = DotProduct(origin, plane->normal) - plane->dist; - if (d >= 0) node = node->children[0]; - else node = node->children[1]; - if (!node) - { - Error("PlaceOccupant: invalid child %d\n", d < 0); - } //end if - } //end while - //don't start in solid -// if (node->contents == CONTENTS_SOLID) - //ME: replaced because in LeafNode in brushbsp.c - // some nodes have contents solid with other contents - if (node->contents & CONTENTS_SOLID) return false; - //if the node is already occupied - if (node->occupied) return false; - - //place the occupant in the first leaf - node->occupant = occupant; - - numrec = 0; -// Log_Print("%6d recurses", numrec); -// FloodPortals_r(node, 1); -// Log_Print("\n"); - FloodPortals(node); - - return true; -} //end of the function PlaceOccupant -//=========================================================================== -// Marks all nodes that can be reached by entites -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean FloodEntities (tree_t *tree) -{ - int i; - int x, y; - vec3_t origin; - char *cl; - qboolean inside; - node_t *headnode; - - headnode = tree->headnode; - Log_Print("------ FloodEntities -------\n"); - inside = false; - tree->outside_node.occupied = 0; - - //start at entity 1 not the world ( = 0) - for (i = 1; i < num_entities; i++) - { - GetVectorForKey(&entities[i], "origin", origin); - if (VectorCompare(origin, vec3_origin)) continue; - - cl = ValueForKey(&entities[i], "classname"); - origin[2] += 1; //so objects on floor are ok - -// Log_Print("flooding from entity %d: %s\n", i, cl); - //nudge playerstart around if needed so clipping hulls allways - //have a valid point - if (!strcmp(cl, "info_player_start")) - { - for (x = -16; x <= 16; x += 16) - { - for (y = -16; y <= 16; y += 16) - { - origin[0] += x; - origin[1] += y; - if (PlaceOccupant(headnode, origin, &entities[i])) - { - inside = true; - x = 999; //stop for this info_player_start - break; - } //end if - origin[0] -= x; - origin[1] -= y; - } //end for - } //end for - } //end if - else - { - if (PlaceOccupant(headnode, origin, &entities[i])) - { - inside = true; - } //end if - } //end else - } //end for - - if (!inside) - { - Log_Print("WARNING: no entities inside\n"); - } //end if - else if (tree->outside_node.occupied) - { - Log_Print("WARNING: entity reached from outside\n"); - } //end else if - - return (qboolean)(inside && !tree->outside_node.occupied); -} //end of the function FloodEntities - -/* -========================================================= - -FILL OUTSIDE - -========================================================= -*/ - -int c_outside; -int c_inside; -int c_solid; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FillOutside_r (node_t *node) -{ - if (node->planenum != PLANENUM_LEAF) - { - FillOutside_r (node->children[0]); - FillOutside_r (node->children[1]); - return; - } //end if - // anything not reachable by an entity - // can be filled away (by setting solid contents) - if (!node->occupied) - { - if (!(node->contents & CONTENTS_SOLID)) - { - c_outside++; - node->contents |= CONTENTS_SOLID; - } //end if - else - { - c_solid++; - } //end else - } //end if - else - { - c_inside++; - } //end else -} //end of the function FillOutside_r -//=========================================================================== -// Fill all nodes that can't be reached by entities -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FillOutside (node_t *headnode) -{ - c_outside = 0; - c_inside = 0; - c_solid = 0; - Log_Print("------- FillOutside --------\n"); - FillOutside_r (headnode); - Log_Print("%5i solid leaves\n", c_solid); - Log_Print("%5i leaves filled\n", c_outside); - Log_Print("%5i inside leaves\n", c_inside); -} //end of the function FillOutside - -/* -========================================================= - -FLOOD AREAS - -========================================================= -*/ - -int c_areas; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FloodAreas_r (node_t *node) -{ - portal_t *p; - int s; - bspbrush_t *b; - entity_t *e; - - if (node->contents == CONTENTS_AREAPORTAL) - { - // this node is part of an area portal - b = node->brushlist; - e = &entities[b->original->entitynum]; - - // if the current area has allready touched this - // portal, we are done - if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas) - return; - - // note the current area as bounding the portal - if (e->portalareas[1]) - { - Log_Print("WARNING: areaportal entity %i touches > 2 areas\n", b->original->entitynum); - return; - } - if (e->portalareas[0]) - e->portalareas[1] = c_areas; - else - e->portalareas[0] = c_areas; - - return; - } //end if - - if (node->area) - return; // allready got it - node->area = c_areas; - - for (p=node->portals ; p ; p = p->next[s]) - { - s = (p->nodes[1] == node); -#if 0 - if (p->nodes[!s]->occupied) - continue; -#endif - if (!Portal_EntityFlood (p, s)) - continue; - - FloodAreas_r (p->nodes[!s]); - } //end for -} //end of the function FloodAreas_r -//=========================================================================== -// Just decend the tree, and for each node that hasn't had an -// area set, flood fill out from there -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FindAreas_r (node_t *node) -{ - if (node->planenum != PLANENUM_LEAF) - { - FindAreas_r (node->children[0]); - FindAreas_r (node->children[1]); - return; - } - - if (node->area) - return; // allready got it - - if (node->contents & CONTENTS_SOLID) - return; - - if (!node->occupied) - return; // not reachable by entities - - // area portals are allways only flooded into, never - // out of - if (node->contents == CONTENTS_AREAPORTAL) - return; - - c_areas++; - FloodAreas_r (node); -} //end of the function FindAreas_r -//=========================================================================== -// Just decend the tree, and for each node that hasn't had an -// area set, flood fill out from there -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void SetAreaPortalAreas_r (node_t *node) -{ - bspbrush_t *b; - entity_t *e; - - if (node->planenum != PLANENUM_LEAF) - { - SetAreaPortalAreas_r (node->children[0]); - SetAreaPortalAreas_r (node->children[1]); - return; - } //end if - - if (node->contents == CONTENTS_AREAPORTAL) - { - if (node->area) - return; // allready set - - b = node->brushlist; - e = &entities[b->original->entitynum]; - node->area = e->portalareas[0]; - if (!e->portalareas[1]) - { - Log_Print("WARNING: areaportal entity %i doesn't touch two areas\n", b->original->entitynum); - return; - } //end if - } //end if -} //end of the function SetAreaPortalAreas_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -/* -void EmitAreaPortals(node_t *headnode) -{ - int i, j; - entity_t *e; - dareaportal_t *dp; - - if (c_areas > MAX_MAP_AREAS) - Error ("MAX_MAP_AREAS"); - numareas = c_areas+1; - numareaportals = 1; // leave 0 as an error - - for (i=1 ; i<=c_areas ; i++) - { - dareas[i].firstareaportal = numareaportals; - for (j=0 ; jareaportalnum) - continue; - dp = &dareaportals[numareaportals]; - if (e->portalareas[0] == i) - { - dp->portalnum = e->areaportalnum; - dp->otherarea = e->portalareas[1]; - numareaportals++; - } //end if - else if (e->portalareas[1] == i) - { - dp->portalnum = e->areaportalnum; - dp->otherarea = e->portalareas[0]; - numareaportals++; - } //end else if - } //end for - dareas[i].numareaportals = numareaportals - dareas[i].firstareaportal; - } //end for - - Log_Print("%5i numareas\n", numareas); - Log_Print("%5i numareaportals\n", numareaportals); -} //end of the function EmitAreaPortals -*/ -//=========================================================================== -// Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FloodAreas (tree_t *tree) -{ - Log_Print("--- FloodAreas ---\n"); - FindAreas_r (tree->headnode); - SetAreaPortalAreas_r (tree->headnode); - Log_Print("%5i areas\n", c_areas); -} //end of the function FloodAreas -//=========================================================================== -// Finds a brush side to use for texturing the given portal -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FindPortalSide (portal_t *p) -{ - int viscontents; - bspbrush_t *bb; - mapbrush_t *brush; - node_t *n; - int i,j; - int planenum; - side_t *side, *bestside; - float dot, bestdot; - plane_t *p1, *p2; - - // decide which content change is strongest - // solid > lava > water, etc - viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents); - if (!viscontents) - return; - - planenum = p->onnode->planenum; - bestside = NULL; - bestdot = 0; - - for (j=0 ; j<2 ; j++) - { - n = p->nodes[j]; - p1 = &mapplanes[p->onnode->planenum]; - for (bb=n->brushlist ; bb ; bb=bb->next) - { - brush = bb->original; - if ( !(brush->contents & viscontents) ) - continue; - for (i=0 ; inumsides ; i++) - { - side = &brush->original_sides[i]; - if (side->flags & SFL_BEVEL) - continue; - if (side->texinfo == TEXINFO_NODE) - continue; // non-visible - if ((side->planenum&~1) == planenum) - { // exact match - bestside = &brush->original_sides[i]; - goto gotit; - } //end if - // see how close the match is - p2 = &mapplanes[side->planenum&~1]; - dot = DotProduct (p1->normal, p2->normal); - if (dot > bestdot) - { - bestdot = dot; - bestside = side; - } //end if - } //end for - } //end for - } //end for - -gotit: - if (!bestside) - Log_Print("WARNING: side not found for portal\n"); - - p->sidefound = true; - p->side = bestside; -} //end of the function FindPortalSide -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void MarkVisibleSides_r (node_t *node) -{ - portal_t *p; - int s; - - if (node->planenum != PLANENUM_LEAF) - { - MarkVisibleSides_r (node->children[0]); - MarkVisibleSides_r (node->children[1]); - return; - } //end if - - // empty leaves are never boundary leaves - if (!node->contents) return; - - // see if there is a visible face - for (p=node->portals ; p ; p = p->next[!s]) - { - s = (p->nodes[0] == node); - if (!p->onnode) - continue; // edge of world - if (!p->sidefound) - FindPortalSide (p); - if (p->side) - p->side->flags |= SFL_VISIBLE; - } //end for -} //end of the function MarkVisibleSides_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void MarkVisibleSides(tree_t *tree, int startbrush, int endbrush) -{ - int i, j; - mapbrush_t *mb; - int numsides; - - Log_Print("--- MarkVisibleSides ---\n"); - - // clear all the visible flags - for (i=startbrush ; inumsides; - for (j=0 ; joriginal_sides[j].flags &= ~SFL_VISIBLE; - } - - // set visible flags on the sides that are used by portals - MarkVisibleSides_r (tree->headnode); -} //end of the function MarkVisibleSides - diff --git a/tools/quake3/bspc/prtfile.c b/tools/quake3/bspc/prtfile.c deleted file mode 100644 index 58d12065..00000000 --- a/tools/quake3/bspc/prtfile.c +++ /dev/null @@ -1,287 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" - -/* -============================================================================== - -PORTAL FILE GENERATION - -Save out name.prt for qvis to read -============================================================================== -*/ - - -#define PORTALFILE "PRT1" - -FILE *pf; -int num_visclusters; // clusters the player can be in -int num_visportals; - -void WriteFloat2 (FILE *f, vec_t v) -{ - if ( fabs(v - Q_rint(v)) < 0.001 ) - fprintf (f,"%i ",(int)Q_rint(v)); - else - fprintf (f,"%f ",v); -} - -/* -================= -WritePortalFile_r -================= -*/ -void WritePortalFile_r (node_t *node) -{ - int i, s; - portal_t *p; - winding_t *w; - vec3_t normal; - vec_t dist; - - // decision node - if (node->planenum != PLANENUM_LEAF && !node->detail_seperator) - { - WritePortalFile_r (node->children[0]); - WritePortalFile_r (node->children[1]); - return; - } - - if (node->contents & CONTENTS_SOLID) - return; - - for (p = node->portals ; p ; p=p->next[s]) - { - w = p->winding; - s = (p->nodes[1] == node); - if (w && p->nodes[0] == node) - { - if (!Portal_VisFlood (p)) - continue; - // write out to the file - - // sometimes planes get turned around when they are very near - // the changeover point between different axis. interpret the - // plane the same way vis will, and flip the side orders if needed - // FIXME: is this still relevent? - WindingPlane (w, normal, &dist); - if ( DotProduct (p->plane.normal, normal) < 0.99 ) - { // backwards... - fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster); - } - else - fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster); - for (i=0 ; inumpoints ; i++) - { - fprintf (pf,"("); - WriteFloat2 (pf, w->p[i][0]); - WriteFloat2 (pf, w->p[i][1]); - WriteFloat2 (pf, w->p[i][2]); - fprintf (pf,") "); - } - fprintf (pf,"\n"); - } - } - -} - -/* -================ -FillLeafNumbers_r - -All of the leafs under node will have the same cluster -================ -*/ -void FillLeafNumbers_r (node_t *node, int num) -{ - if (node->planenum == PLANENUM_LEAF) - { - if (node->contents & CONTENTS_SOLID) - node->cluster = -1; - else - node->cluster = num; - return; - } - node->cluster = num; - FillLeafNumbers_r (node->children[0], num); - FillLeafNumbers_r (node->children[1], num); -} - -/* -================ -NumberLeafs_r -================ -*/ -void NumberLeafs_r (node_t *node) -{ - portal_t *p; - - if (node->planenum != PLANENUM_LEAF && !node->detail_seperator) - { // decision node - node->cluster = -99; - NumberLeafs_r (node->children[0]); - NumberLeafs_r (node->children[1]); - return; - } - - // either a leaf or a detail cluster - - if ( node->contents & CONTENTS_SOLID ) - { // solid block, viewpoint never inside - node->cluster = -1; - return; - } - - FillLeafNumbers_r (node, num_visclusters); - num_visclusters++; - - // count the portals - for (p = node->portals ; p ; ) - { - if (p->nodes[0] == node) // only write out from first leaf - { - if (Portal_VisFlood (p)) - num_visportals++; - p = p->next[0]; - } - else - p = p->next[1]; - } - -} - - -/* -================ -CreateVisPortals_r -================ -*/ -void CreateVisPortals_r (node_t *node) -{ - // stop as soon as we get to a detail_seperator, which - // means that everything below is in a single cluster - if (node->planenum == PLANENUM_LEAF || node->detail_seperator ) - return; - - MakeNodePortal (node); - SplitNodePortals (node); - - CreateVisPortals_r (node->children[0]); - CreateVisPortals_r (node->children[1]); -} - -/* -================ -FinishVisPortals_r -================ -*/ -void FinishVisPortals2_r (node_t *node) -{ - if (node->planenum == PLANENUM_LEAF) - return; - - MakeNodePortal (node); - SplitNodePortals (node); - - FinishVisPortals2_r (node->children[0]); - FinishVisPortals2_r (node->children[1]); -} - -void FinishVisPortals_r (node_t *node) -{ - if (node->planenum == PLANENUM_LEAF) - return; - - if (node->detail_seperator) - { - FinishVisPortals2_r (node); - return; - } - - FinishVisPortals_r (node->children[0]); - FinishVisPortals_r (node->children[1]); -} - - -int clusterleaf; -void SaveClusters_r (node_t *node) -{ - if (node->planenum == PLANENUM_LEAF) - { - dleafs[clusterleaf++].cluster = node->cluster; - return; - } - SaveClusters_r (node->children[0]); - SaveClusters_r (node->children[1]); -} - -/* -================ -WritePortalFile -================ -*/ -void WritePortalFile (tree_t *tree) -{ - char filename[1024]; - node_t *headnode; - - qprintf ("--- WritePortalFile ---\n"); - - headnode = tree->headnode; - num_visclusters = 0; - num_visportals = 0; - - Tree_FreePortals_r (headnode); - - MakeHeadnodePortals (tree); - - CreateVisPortals_r (headnode); - -// set the cluster field in every leaf and count the total number of portals - - NumberLeafs_r (headnode); - -// write the file - sprintf (filename, "%s.prt", source); - printf ("writing %s\n", filename); - pf = fopen (filename, "w"); - if (!pf) - Error ("Error opening %s", filename); - - fprintf (pf, "%s\n", PORTALFILE); - fprintf (pf, "%i\n", num_visclusters); - fprintf (pf, "%i\n", num_visportals); - - qprintf ("%5i visclusters\n", num_visclusters); - qprintf ("%5i visportals\n", num_visportals); - - WritePortalFile_r (headnode); - - fclose (pf); - - // we need to store the clusters out now because ordering - // issues made us do this after writebsp... - clusterleaf = 1; - SaveClusters_r (headnode); -} - diff --git a/tools/quake3/bspc/q2files.h b/tools/quake3/bspc/q2files.h deleted file mode 100644 index 317e47cc..00000000 --- a/tools/quake3/bspc/q2files.h +++ /dev/null @@ -1,487 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -// -// qfiles.h: quake file formats -// This file must be identical in the quake and utils directories -// - -/* -======================================================================== - -The .pak files are just a linear collapse of a directory tree - -======================================================================== -*/ - -#define IDPAKHEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P') - -typedef struct -{ - char name[56]; - int filepos, filelen; -} dpackfile_t; - -typedef struct -{ - int ident; // == IDPAKHEADER - int dirofs; - int dirlen; -} dpackheader_t; - -#define MAX_FILES_IN_PACK 4096 - - -/* -======================================================================== - -PCX files are used for as many images as possible - -======================================================================== -*/ - -typedef struct -{ - char manufacturer; - char version; - char encoding; - char bits_per_pixel; - unsigned short xmin,ymin,xmax,ymax; - unsigned short hres,vres; - unsigned char palette[48]; - char reserved; - char color_planes; - unsigned short bytes_per_line; - unsigned short palette_type; - char filler[58]; - unsigned char data; // unbounded -} pcx_t; - - -/* -======================================================================== - -.MD2 triangle model file format - -======================================================================== -*/ - -#define IDALIASHEADER (('2'<<24)+('P'<<16)+('D'<<8)+'I') -#define ALIAS_VERSION 8 - -#define MAX_TRIANGLES 4096 -#define MAX_VERTS 2048 -#define MAX_FRAMES 512 -#define MAX_MD2SKINS 32 -#define MAX_SKINNAME 64 - -typedef struct -{ - short s; - short t; -} dstvert_t; - -typedef struct -{ - short index_xyz[3]; - short index_st[3]; -} dtriangle_t; - -typedef struct -{ - byte v[3]; // scaled byte to fit in frame mins/maxs - byte lightnormalindex; -} dtrivertx_t; - -#define DTRIVERTX_V0 0 -#define DTRIVERTX_V1 1 -#define DTRIVERTX_V2 2 -#define DTRIVERTX_LNI 3 -#define DTRIVERTX_SIZE 4 - -typedef struct -{ - float scale[3]; // multiply byte verts by this - float translate[3]; // then add this - char name[16]; // frame name from grabbing - dtrivertx_t verts[1]; // variable sized -} daliasframe_t; - - -// the glcmd format: -// a positive integer starts a tristrip command, followed by that many -// vertex structures. -// a negative integer starts a trifan command, followed by -x vertexes -// a zero indicates the end of the command list. -// a vertex consists of a floating point s, a floating point t, -// and an integer vertex index. - - -typedef struct -{ - int ident; - int version; - - int skinwidth; - int skinheight; - int framesize; // byte size of each frame - - int num_skins; - int num_xyz; - int num_st; // greater than num_xyz for seams - int num_tris; - int num_glcmds; // dwords in strip/fan command list - int num_frames; - - int ofs_skins; // each skin is a MAX_SKINNAME string - int ofs_st; // byte offset from start for stverts - int ofs_tris; // offset for dtriangles - int ofs_frames; // offset for first frame - int ofs_glcmds; - int ofs_end; // end of file - -} dmdl_t; - -/* -======================================================================== - -.SP2 sprite file format - -======================================================================== -*/ - -#define IDSPRITEHEADER (('2'<<24)+('S'<<16)+('D'<<8)+'I') - // little-endian "IDS2" -#define SPRITE_VERSION 2 - -typedef struct -{ - int width, height; - int origin_x, origin_y; // raster coordinates inside pic - char name[MAX_SKINNAME]; // name of pcx file -} dsprframe_t; - -typedef struct { - int ident; - int version; - int numframes; - dsprframe_t frames[1]; // variable sized -} dsprite_t; - -/* -============================================================================== - - .WAL texture file format - -============================================================================== -*/ - - -#define MIPLEVELS 4 -typedef struct miptex_s -{ - char name[32]; - unsigned width, height; - unsigned offsets[MIPLEVELS]; // four mip maps stored - char animname[32]; // next frame in animation chain - int flags; - int contents; - int value; -} miptex_t; - - - -/* -============================================================================== - - .BSP file format - -============================================================================== -*/ - -#define IDBSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I') - // little-endian "IBSP" - -#define BSPVERSION 38 - - -// upper design bounds -// leaffaces, leafbrushes, planes, and verts are still bounded by -// 16 bit short limits -#define MAX_MAP_MODELS 1024 -#define MAX_MAP_BRUSHES 8192 -#define MAX_MAP_ENTITIES 2048 -#define MAX_MAP_ENTSTRING 0x40000 -#define MAX_MAP_TEXINFO 8192 - -#define MAX_MAP_AREAS 256 -#define MAX_MAP_AREAPORTALS 1024 -#define MAX_MAP_PLANES 65536 -#define MAX_MAP_NODES 65536 -#define MAX_MAP_BRUSHSIDES 65536 -#define MAX_MAP_LEAFS 65536 -#define MAX_MAP_VERTS 65536 -#define MAX_MAP_FACES 65536 -#define MAX_MAP_LEAFFACES 65536 -#define MAX_MAP_LEAFBRUSHES 65536 -#define MAX_MAP_PORTALS 65536 -#define MAX_MAP_EDGES 128000 -#define MAX_MAP_SURFEDGES 256000 -#define MAX_MAP_LIGHTING 0x320000 -#define MAX_MAP_VISIBILITY 0x280000 - -// key / value pair sizes - -#define MAX_KEY 32 -#define MAX_VALUE 1024 - -//============================================================================= - -typedef struct -{ - int fileofs, filelen; -} lump_t; - -#define LUMP_ENTITIES 0 -#define LUMP_PLANES 1 -#define LUMP_VERTEXES 2 -#define LUMP_VISIBILITY 3 -#define LUMP_NODES 4 -#define LUMP_TEXINFO 5 -#define LUMP_FACES 6 -#define LUMP_LIGHTING 7 -#define LUMP_LEAFS 8 -#define LUMP_LEAFFACES 9 -#define LUMP_LEAFBRUSHES 10 -#define LUMP_EDGES 11 -#define LUMP_SURFEDGES 12 -#define LUMP_MODELS 13 -#define LUMP_BRUSHES 14 -#define LUMP_BRUSHSIDES 15 -#define LUMP_POP 16 -#define LUMP_AREAS 17 -#define LUMP_AREAPORTALS 18 -#define HEADER_LUMPS 19 - -typedef struct -{ - int ident; - int version; - lump_t lumps[HEADER_LUMPS]; -} dheader_t; - -typedef struct -{ - float mins[3], maxs[3]; - float origin[3]; // for sounds or lights - int headnode; - int firstface, numfaces; // submodels just draw faces - // without walking the bsp tree -} dmodel_t; - - -typedef struct -{ - float point[3]; -} dvertex_t; - - -// 0-2 are axial planes -#define PLANE_X 0 -#define PLANE_Y 1 -#define PLANE_Z 2 - -// 3-5 are non-axial planes snapped to the nearest -#define PLANE_ANYX 3 -#define PLANE_ANYY 4 -#define PLANE_ANYZ 5 - -// planes (x&~1) and (x&~1)+1 are allways opposites - -typedef struct -{ - float normal[3]; - float dist; - int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate -} dplane_t; - - -// contents flags are seperate bits -// a given brush can contribute multiple content bits -// multiple brushes can be in a single leaf - -// these definitions also need to be in q_shared.h! - -// lower bits are stronger, and will eat weaker brushes completely -#define CONTENTS_SOLID 1 // an eye is never valid in a solid -#define CONTENTS_WINDOW 2 // translucent, but not watery -#define CONTENTS_AUX 4 -#define CONTENTS_LAVA 8 -#define CONTENTS_SLIME 16 -#define CONTENTS_WATER 32 -#define CONTENTS_MIST 64 -#define LAST_VISIBLE_CONTENTS 64 - -// remaining contents are non-visible, and don't eat brushes - -#define CONTENTS_AREAPORTAL 0x8000 - -#define CONTENTS_PLAYERCLIP 0x10000 -#define CONTENTS_MONSTERCLIP 0x20000 - -// currents can be added to any other contents, and may be mixed -#define CONTENTS_CURRENT_0 0x40000 -#define CONTENTS_CURRENT_90 0x80000 -#define CONTENTS_CURRENT_180 0x100000 -#define CONTENTS_CURRENT_270 0x200000 -#define CONTENTS_CURRENT_UP 0x400000 -#define CONTENTS_CURRENT_DOWN 0x800000 - -#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity - -#define CONTENTS_MONSTER 0x2000000 // should never be on a brush, only in game -#define CONTENTS_DEADMONSTER 0x4000000 -#define CONTENTS_DETAIL 0x8000000 // brushes to be added after vis leafs -//renamed because it's in conflict with the Q3A translucent contents -#define CONTENTS_Q2TRANSLUCENT 0x10000000 // auto set if any surface has trans -#define CONTENTS_LADDER 0x20000000 - - - -#define SURF_LIGHT 0x1 // value will hold the light strength - -#define SURF_SLICK 0x2 // effects game physics - -#define SURF_SKY 0x4 // don't draw, but add to skybox -#define SURF_WARP 0x8 // turbulent water warp -#define SURF_TRANS33 0x10 -#define SURF_TRANS66 0x20 -#define SURF_FLOWING 0x40 // scroll towards angle -#define SURF_NODRAW 0x80 // don't bother referencing the texture - -#define SURF_HINT 0x100 // make a primary bsp splitter -#define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes - - - -typedef struct -{ - int planenum; - int children[2]; // negative numbers are -(leafs+1), not nodes - short mins[3]; // for frustom culling - short maxs[3]; - unsigned short firstface; - unsigned short numfaces; // counting both sides -} dnode_t; - - -typedef struct texinfo_s -{ - float vecs[2][4]; // [s/t][xyz offset] - int flags; // miptex flags + overrides - int value; // light emission, etc - char texture[32]; // texture name (textures/*.wal) - int nexttexinfo; // for animations, -1 = end of chain -} texinfo_t; - - -// note that edge 0 is never used, because negative edge nums are used for -// counterclockwise use of the edge in a face -typedef struct -{ - unsigned short v[2]; // vertex numbers -} dedge_t; - -#define MAXLIGHTMAPS 4 -typedef struct -{ - unsigned short planenum; - short side; - - int firstedge; // we must support > 64k edges - short numedges; - short texinfo; - -// lighting info - byte styles[MAXLIGHTMAPS]; - int lightofs; // start of [numstyles*surfsize] samples -} dface_t; - -typedef struct -{ - int contents; // OR of all brushes (not needed?) - - short cluster; - short area; - - short mins[3]; // for frustum culling - short maxs[3]; - - unsigned short firstleafface; - unsigned short numleaffaces; - - unsigned short firstleafbrush; - unsigned short numleafbrushes; -} dleaf_t; - -typedef struct -{ - unsigned short planenum; // facing out of the leaf - short texinfo; -} dbrushside_t; - -typedef struct -{ - int firstside; - int numsides; - int contents; -} dbrush_t; - -#define ANGLE_UP -1 -#define ANGLE_DOWN -2 - - -// the visibility lump consists of a header with a count, then -// byte offsets for the PVS and PHS of each cluster, then the raw -// compressed bit vectors -#define DVIS_PVS 0 -#define DVIS_PHS 1 -typedef struct -{ - int numclusters; - int bitofs[8][2]; // bitofs[numclusters][2] -} dvis_t; - -// each area has a list of portals that lead into other areas -// when portals are closed, other areas may not be visible or -// hearable even if the vis info says that it should be -typedef struct -{ - int portalnum; - int otherarea; -} dareaportal_t; - -typedef struct -{ - int numareaportals; - int firstareaportal; -} darea_t; diff --git a/tools/quake3/bspc/q3files.h b/tools/quake3/bspc/q3files.h deleted file mode 100644 index 35b5ee11..00000000 --- a/tools/quake3/bspc/q3files.h +++ /dev/null @@ -1,384 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#ifndef __QFILES_H__ -#define __QFILES_H__ - -// -// qfiles.h: quake file formats -// This file must be identical in the quake and utils directories -// - -// surface geometry should not exceed these limits -#define SHADER_MAX_VERTEXES 1000 -#define SHADER_MAX_INDEXES (6*SHADER_MAX_VERTEXES) - - -// the maximum size of game reletive pathnames -#define MAX_QPATH 64 - - -#if 0 -/* -======================================================================== - -PCX files are used for 8 bit images - -======================================================================== -*/ - -typedef struct { - char manufacturer; - char version; - char encoding; - char bits_per_pixel; - unsigned short xmin,ymin,xmax,ymax; - unsigned short hres,vres; - unsigned char palette[48]; - char reserved; - char color_planes; - unsigned short bytes_per_line; - unsigned short palette_type; - char filler[58]; - unsigned char data; // unbounded -} pcx_t; - - -/* -======================================================================== - -TGA files are used for 24/32 bit images - -======================================================================== -*/ - -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; - -#endif // #if 0 - - -/* -======================================================================== - -.MD3 triangle model file format - -======================================================================== -*/ - -#define MD3_IDENT (('3'<<24)+('P'<<16)+('D'<<8)+'I') -#define MD3_VERSION 15 - -// limits -#define MD3_MAX_LODS 4 -#define MD3_MAX_TRIANGLES 8192 // per surface -#define MD3_MAX_VERTS 4096 // per surface -#define MD3_MAX_SHADERS 256 // per surface -#define MD3_MAX_FRAMES 1024 // per model -#define MD3_MAX_SURFACES 32 // per model -#define MD3_MAX_TAGS 16 // per frame - -// vertex scales -#define MD3_XYZ_SCALE (1.0/64) - -typedef struct md3Frame_s { - vec3_t bounds[2]; - vec3_t localOrigin; - float radius; - char name[16]; -} md3Frame_t; - -typedef struct md3Tag_s { - char name[MAX_QPATH]; // tag name - vec3_t origin; - vec3_t axis[3]; -} md3Tag_t; - -/* -** md3Surface_t -** -** CHUNK SIZE -** header sizeof( md3Surface_t ) -** shaders sizeof( md3Shader_t ) * numShaders -** triangles[0] sizeof( md3Triangle_t ) * numTriangles -** st sizeof( md3St_t ) * numVerts -** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numFrames -*/ - -typedef struct { - int ident; // - - char name[MAX_QPATH]; // polyset name - - int flags; - int numFrames; // all surfaces in a model should have the same - - int numShaders; // all surfaces in a model should have the same - int numVerts; - - int numTriangles; - int ofsTriangles; - - int ofsShaders; // offset from start of md3Surface_t - int ofsSt; // texture coords are common for all frames - int ofsXyzNormals; // numVerts * numFrames - - int ofsEnd; // next surface follows -} md3Surface_t; - -typedef struct { - char name[MAX_QPATH]; - int shaderIndex; // for in-game use -} md3Shader_t; - -typedef struct { - int indexes[3]; -} md3Triangle_t; - -typedef struct { - float st[2]; -} md3St_t; - -typedef struct { - short xyz[3]; - short normal; -} md3XyzNormal_t; - -typedef struct { - int ident; - int version; - - char name[MAX_QPATH]; // model name - - int flags; - - int numFrames; - int numTags; - int numSurfaces; - - int numSkins; - - int ofsFrames; // offset for first frame - int ofsTags; // numFrames * numTags - int ofsSurfaces; // first surface, others follow - - int ofsEnd; // end of file -} md3Header_t; - - - -/* -============================================================================== - - .BSP file format - -============================================================================== -*/ - - -#define Q3_BSP_IDENT (('P'<<24)+('S'<<16)+('B'<<8)+'I') - // little-endian "IBSP" - -#define Q3_BSP_VERSION 46 - -// quick fix for creating aas files for ql bsp's. -// (later this will probably need to be put in seperate header files e.g q2,q3,ql etc) -#define QL_BSP_IDENT (('P'<<24)+('S'<<16)+('B'<<8)+'I') - // little-endian "IBSP" - -#define QL_BSP_VERSION 47 -// *********************************************************** - - - -// there shouldn't be any problem with increasing these values at the -// expense of more memory allocation in the utilities -#define Q3_MAX_MAP_MODELS 0x400 -#define Q3_MAX_MAP_BRUSHES 0x8000 -#define Q3_MAX_MAP_ENTITIES 0x800 -#define Q3_MAX_MAP_ENTSTRING 0x10000 -#define Q3_MAX_MAP_SHADERS 0x400 - -#define Q3_MAX_MAP_AREAS 0x100 // MAX_MAP_AREA_BYTES in q_shared must match! -#define Q3_MAX_MAP_FOGS 0x100 -#define Q3_MAX_MAP_PLANES 0x10000 -#define Q3_MAX_MAP_NODES 0x10000 -#define Q3_MAX_MAP_BRUSHSIDES 0x10000 -#define Q3_MAX_MAP_LEAFS 0x10000 -#define Q3_MAX_MAP_LEAFFACES 0x10000 -#define Q3_MAX_MAP_LEAFBRUSHES 0x10000 -#define Q3_MAX_MAP_PORTALS 0x10000 -#define Q3_MAX_MAP_LIGHTING 0x400000 -#define Q3_MAX_MAP_LIGHTGRID 0x400000 -#define Q3_MAX_MAP_VISIBILITY 0x200000 - -#define Q3_MAX_MAP_DRAW_SURFS 0x20000 -#define Q3_MAX_MAP_DRAW_VERTS 0x80000 -#define Q3_MAX_MAP_DRAW_INDEXES 0x80000 - - -// key / value pair sizes in the entities lump -#define Q3_MAX_KEY 32 -#define Q3_MAX_VALUE 1024 - -// the editor uses these predefined yaw angles to orient entities up or down -#define ANGLE_UP -1 -#define ANGLE_DOWN -2 - -#define LIGHTMAP_WIDTH 128 -#define LIGHTMAP_HEIGHT 128 - - -//============================================================================= - - -typedef struct { - int fileofs, filelen; -} q3_lump_t; - -#define Q3_LUMP_ENTITIES 0 -#define Q3_LUMP_SHADERS 1 -#define Q3_LUMP_PLANES 2 -#define Q3_LUMP_NODES 3 -#define Q3_LUMP_LEAFS 4 -#define Q3_LUMP_LEAFSURFACES 5 -#define Q3_LUMP_LEAFBRUSHES 6 -#define Q3_LUMP_MODELS 7 -#define Q3_LUMP_BRUSHES 8 -#define Q3_LUMP_BRUSHSIDES 9 -#define Q3_LUMP_DRAWVERTS 10 -#define Q3_LUMP_DRAWINDEXES 11 -#define Q3_LUMP_FOGS 12 -#define Q3_LUMP_SURFACES 13 -#define Q3_LUMP_LIGHTMAPS 14 -#define Q3_LUMP_LIGHTGRID 15 -#define Q3_LUMP_VISIBILITY 16 -#define Q3_HEADER_LUMPS 17 - -typedef struct { - int ident; - int version; - - q3_lump_t lumps[Q3_HEADER_LUMPS]; -} q3_dheader_t; - -typedef struct { - float mins[3], maxs[3]; - int firstSurface, numSurfaces; - int firstBrush, numBrushes; -} q3_dmodel_t; - -typedef struct { - char shader[MAX_QPATH]; - int surfaceFlags; - int contentFlags; -} q3_dshader_t; - -// planes (x&~1) and (x&~1)+1 are allways opposites - -typedef struct { - float normal[3]; - float dist; -} q3_dplane_t; - -typedef struct { - int planeNum; - int children[2]; // negative numbers are -(leafs+1), not nodes - int mins[3]; // for frustom culling - int maxs[3]; -} q3_dnode_t; - -typedef struct { - int cluster; // -1 = opaque cluster (do I still store these?) - int area; - - int mins[3]; // for frustum culling - int maxs[3]; - - int firstLeafSurface; - int numLeafSurfaces; - - int firstLeafBrush; - int numLeafBrushes; -} q3_dleaf_t; - -typedef struct { - int planeNum; // positive plane side faces out of the leaf - int shaderNum; -} q3_dbrushside_t; - -typedef struct { - int firstSide; - int numSides; - int shaderNum; // the shader that determines the contents flags -} q3_dbrush_t; - -typedef struct { - char shader[MAX_QPATH]; - int brushNum; - int visibleSide; // the brush side that ray tests need to clip against (-1 == none) -} q3_dfog_t; - -typedef struct { - vec3_t xyz; - float st[2]; - float lightmap[2]; - vec3_t normal; - byte color[4]; -} q3_drawVert_t; - -typedef enum { - MST_BAD, - MST_PLANAR, - MST_PATCH, - MST_TRIANGLE_SOUP, - MST_FLARE -} q3_mapSurfaceType_t; - -typedef struct { - int shaderNum; - int fogNum; - int surfaceType; - - int firstVert; - int numVerts; - - int firstIndex; - int numIndexes; - - int lightmapNum; - int lightmapX, lightmapY; - int lightmapWidth, lightmapHeight; - - vec3_t lightmapOrigin; - vec3_t lightmapVecs[3]; // for patches, [0] and [1] are lodbounds - - int patchWidth; - int patchHeight; -} q3_dsurface_t; - - -#endif diff --git a/tools/quake3/bspc/qbsp.h b/tools/quake3/bspc/qbsp.h deleted file mode 100644 index 4050e590..00000000 --- a/tools/quake3/bspc/qbsp.h +++ /dev/null @@ -1,477 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - - -#if defined(WIN32) || defined(_WIN32) -#include -#endif -#include -#include "l_cmd.h" -#include "l_math.h" -#include "l_poly.h" -#include "l_threads.h" -#include "deps/botlib/l_script.h" -#include "l_bsp_ent.h" -#include "q2files.h" -#include "l_mem.h" -#include "l_utils.h" -#include "l_log.h" -#include "l_qfiles.h" - -#define BSPC_VERSION "2.1h" - -#define ME -#define DEBUG -#define NODELIST -#define SIN - -#define MAX_BRUSH_SIDES 128 //maximum number of sides per brush -#define CLIP_EPSILON 0.1 -#define MAX_MAP_BOUNDS 65535 -#define BOGUS_RANGE (MAX_MAP_BOUNDS+128) //somewhere outside the map -#define TEXINFO_NODE -1 //side is allready on a node -#define PLANENUM_LEAF -1 //used for leaf nodes -#define MAXEDGES 20 //maximum number of face edges -#define MAX_NODE_BRUSHES 8 //maximum brushes in a node -//side flags -#define SFL_TESTED 1 -#define SFL_VISIBLE 2 -#define SFL_BEVEL 4 -#define SFL_TEXTURED 8 -#define SFL_CURVE 16 - -//map plane -typedef struct plane_s -{ - vec3_t normal; - vec_t dist; - int type; - int signbits; - struct plane_s *hash_chain; -} plane_t; -//brush texture -typedef struct -{ - vec_t shift[2]; - vec_t rotate; - vec_t scale[2]; - char name[32]; - int flags; - int value; -} brush_texture_t; -//brush side -typedef struct side_s -{ - int planenum; // map plane this side is in - int texinfo; // texture reference - winding_t *winding; // winding of this side - struct side_s *original; // bspbrush_t sides will reference the mapbrush_t sides - int lightinfo; // for SIN only - int contents; // from miptex - int surf; // from miptex - unsigned short flags; // side flags -} side_t; //sizeof(side_t) = 36 -//map brush -typedef struct mapbrush_s -{ - int entitynum; - int brushnum; - - int contents; -#ifdef ME - int expansionbbox; //bbox used for expansion of the brush - int leafnum; - int modelnum; -#endif - - vec3_t mins, maxs; - - int numsides; - side_t *original_sides; -} mapbrush_t; -//bsp face -typedef struct face_s -{ - struct face_s *next; // on node - - // the chain of faces off of a node can be merged or split, - // but each face_t along the way will remain in the chain - // until the entire tree is freed - struct face_s *merged; // if set, this face isn't valid anymore - struct face_s *split[2]; // if set, this face isn't valid anymore - - struct portal_s *portal; - int texinfo; -#ifdef SIN - int lightinfo; -#endif - int planenum; - int contents; // faces in different contents can't merge - int outputnumber; - winding_t *w; - int numpoints; - qboolean badstartvert; // tjunctions cannot be fixed without a midpoint vertex - int vertexnums[MAXEDGES]; -} face_t; -//bsp brush -typedef struct bspbrush_s -{ - struct bspbrush_s *next; - vec3_t mins, maxs; - int side, testside; // side of node during construction - mapbrush_t *original; - int numsides; - side_t sides[6]; // variably sized -} bspbrush_t; //sizeof(bspbrush_t) = 44 + numsides * sizeof(side_t) -//bsp node -typedef struct node_s -{ - //both leafs and nodes - int planenum; // -1 = leaf node - struct node_s *parent; - vec3_t mins, maxs; // valid after portalization - bspbrush_t *volume; // one for each leaf/node - - // nodes only - qboolean detail_seperator; // a detail brush caused the split - side_t *side; // the side that created the node - struct node_s *children[2]; - face_t *faces; - - // leafs only - bspbrush_t *brushlist; // fragments of all brushes in this leaf - int contents; // OR of all brush contents - int occupied; // 1 or greater can reach entity - entity_t *occupant; // for leak file testing - int cluster; // for portalfile writing - int area; // for areaportals - struct portal_s *portals; // also on nodes during construction -#ifdef NODELIST - struct node_s *next; //next node in the nodelist -#endif -#ifdef ME - int expansionbboxes; //OR of all bboxes used for expansion of the brushes - int modelnum; -#endif -} node_t; //sizeof(node_t) = 80 bytes -//bsp portal -typedef struct portal_s -{ - plane_t plane; - node_t *onnode; // NULL = outside box - node_t *nodes[2]; // [0] = front side of plane - struct portal_s *next[2]; - winding_t *winding; - - qboolean sidefound; // false if ->side hasn't been checked - side_t *side; // NULL = non-visible - face_t *face[2]; // output face in bsp file -#ifdef ME - struct tmp_face_s *tmpface; //pointer to the tmpface created for this portal - int planenum; //number of the map plane used by the portal -#endif -} portal_t; -//bsp tree -typedef struct -{ - node_t *headnode; - node_t outside_node; - vec3_t mins, maxs; -} tree_t; - -//============================================================================= -// bspc.c -//============================================================================= - -extern qboolean noprune; -extern qboolean nodetail; -extern qboolean fulldetail; -extern qboolean nomerge; -extern qboolean nosubdiv; -extern qboolean nowater; -extern qboolean noweld; -extern qboolean noshare; -extern qboolean notjunc; -extern qboolean onlyents; -#ifdef ME -extern qboolean nocsg; -extern qboolean create_aas; -extern qboolean freetree; -extern qboolean lessbrushes; -extern qboolean nobrushmerge; -extern qboolean cancelconversion; -extern qboolean noliquids; -extern qboolean capsule_collision; -#endif //ME - -extern float subdivide_size; -extern vec_t microvolume; - -extern char outbase[32]; -extern char source[1024]; - -//============================================================================= -// map.c -//============================================================================= - -#define MAX_MAPFILE_PLANES 256000 -#define MAX_MAPFILE_BRUSHES 65535 -#define MAX_MAPFILE_BRUSHSIDES (MAX_MAPFILE_BRUSHES*8) -#define MAX_MAPFILE_TEXINFO 8192 - -extern int entity_num; - -extern plane_t mapplanes[MAX_MAPFILE_PLANES]; -extern int nummapplanes; -extern int mapplaneusers[MAX_MAPFILE_PLANES]; - -extern int nummapbrushes; -extern mapbrush_t mapbrushes[MAX_MAPFILE_BRUSHES]; - -extern vec3_t map_mins, map_maxs; - -extern int nummapbrushsides; -extern side_t brushsides[MAX_MAPFILE_BRUSHSIDES]; -extern brush_texture_t side_brushtextures[MAX_MAPFILE_BRUSHSIDES]; - -#ifdef ME - -typedef struct -{ - float vecs[2][4]; // [s/t][xyz offset] - int flags; // miptex flags + overrides - int value; - char texture[64]; // texture name (textures/*.wal) - int nexttexinfo; // for animations, -1 = end of chain -} map_texinfo_t; - -extern map_texinfo_t map_texinfo[MAX_MAPFILE_TEXINFO]; -extern int map_numtexinfo; -#define NODESTACKSIZE 1024 - -#define MAPTYPE_QUAKE1 1 -#define MAPTYPE_QUAKE2 2 -#define MAPTYPE_QUAKE3 3 -#define MAPTYPE_HALFLIFE 4 -#define MAPTYPE_SIN 5 - -extern int nodestack[NODESTACKSIZE]; -extern int *nodestackptr; -extern int nodestacksize; -extern int brushmodelnumbers[MAX_MAPFILE_BRUSHES]; -extern int dbrushleafnums[MAX_MAPFILE_BRUSHES]; -extern int dplanes2mapplanes[MAX_MAPFILE_PLANES]; - -extern int loadedmaptype; -#endif //ME - -extern int c_boxbevels; -extern int c_edgebevels; -extern int c_areaportals; -extern int c_clipbrushes; -extern int c_squattbrushes; - -//finds a float plane for the given normal and distance -int FindFloatPlane(vec3_t normal, vec_t dist); -//returns the plane type for the given normal -int PlaneTypeForNormal(vec3_t normal); -//returns the plane defined by the three given points -int PlaneFromPoints(int *p0, int *p1, int *p2); -//add bevels to the map brush -void AddBrushBevels(mapbrush_t *b); -//makes brush side windings for the brush -qboolean MakeBrushWindings(mapbrush_t *ob); -//marks brush bevels of the brush as bevel -void MarkBrushBevels(mapbrush_t *brush); -//returns true if the map brush already exists -int BrushExists(mapbrush_t *brush); -//loads a map from a bsp file -int LoadMapFromBSP(struct quakefile_s *qf); -//resets map loading -void ResetMapLoading(void); -//print some map info -void PrintMapInfo(void); -//writes a map file (type depending on loaded map type) -void WriteMapFile(char *filename); - -//============================================================================= -// map_q2.c -//============================================================================= - -void Q2_ResetMapLoading(void); -//loads a Quake2 map file -void Q2_LoadMapFile(char *filename); -//loads a map from a Quake2 bsp file -void Q2_LoadMapFromBSP(char *filename, int offset, int length); - -//============================================================================= -// map_q1.c -//============================================================================= - -void Q1_ResetMapLoading(void); -//loads a Quake2 map file -void Q1_LoadMapFile(char *filename); -//loads a map from a Quake1 bsp file -void Q1_LoadMapFromBSP(char *filename, int offset, int length); - -//============================================================================= -// map_q3.c -//============================================================================= -void Q3_ResetMapLoading(void); -//loads a map from a Quake3 bsp file -void Q3_LoadMapFromBSP(struct quakefile_s *qf); - -//============================================================================= -// map_sin.c -//============================================================================= - -void Sin_ResetMapLoading(void); -//loads a Sin map file -void Sin_LoadMapFile(char *filename); -//loads a map from a Sin bsp file -void Sin_LoadMapFromBSP(char *filename, int offset, int length); - -//============================================================================= -// map_hl.c -//============================================================================= - -void HL_ResetMapLoading(void); -//loads a Half-Life map file -void HL_LoadMapFile(char *filename); -//loads a map from a Half-Life bsp file -void HL_LoadMapFromBSP(char *filename, int offset, int length); - -//============================================================================= -// textures.c -//============================================================================= - -typedef struct -{ - char name[64]; - int flags; - int value; - int contents; - char animname[64]; -} textureref_t; - -#define MAX_MAP_TEXTURES 1024 - -extern textureref_t textureref[MAX_MAP_TEXTURES]; - -int FindMiptex(char *name); -int TexinfoForBrushTexture(plane_t *plane, brush_texture_t *bt, vec3_t origin); -void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv); - -//============================================================================= -// csg -//============================================================================= - -bspbrush_t *MakeBspBrushList(int startbrush, int endbrush, vec3_t clipmins, vec3_t clipmaxs); -bspbrush_t *ChopBrushes(bspbrush_t *head); -bspbrush_t *InitialBrushList(bspbrush_t *list); -bspbrush_t *OptimizedBrushList(bspbrush_t *list); -void WriteBrushMap(char *name, bspbrush_t *list); -void CheckBSPBrush(bspbrush_t *brush); -void BSPBrushWindings(bspbrush_t *brush); -bspbrush_t *TryMergeBrushes(bspbrush_t *brush1, bspbrush_t *brush2); -tree_t *ProcessWorldBrushes(int brush_start, int brush_end); - -//============================================================================= -// brushbsp -//============================================================================= - -#define PSIDE_FRONT 1 -#define PSIDE_BACK 2 -#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK) -#define PSIDE_FACING 4 - -void WriteBrushList(char *name, bspbrush_t *brush, qboolean onlyvis); -bspbrush_t *CopyBrush(bspbrush_t *brush); -void SplitBrush(bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back); -node_t *AllocNode(void); -bspbrush_t *AllocBrush(int numsides); -int CountBrushList(bspbrush_t *brushes); -void FreeBrush(bspbrush_t *brushes); -vec_t BrushVolume(bspbrush_t *brush); -void BoundBrush(bspbrush_t *brush); -void FreeBrushList(bspbrush_t *brushes); -tree_t *BrushBSP(bspbrush_t *brushlist, vec3_t mins, vec3_t maxs); -bspbrush_t *BrushFromBounds(vec3_t mins, vec3_t maxs); -int BrushMostlyOnSide(bspbrush_t *brush, plane_t *plane); -qboolean WindingIsHuge(winding_t *w); -qboolean WindingIsTiny(winding_t *w); -void ResetBrushBSP(void); - -//============================================================================= -// portals.c -//============================================================================= - -int VisibleContents (int contents); -void MakeHeadnodePortals (tree_t *tree); -void MakeNodePortal (node_t *node); -void SplitNodePortals (node_t *node); -qboolean Portal_VisFlood (portal_t *p); -qboolean FloodEntities (tree_t *tree); -void FillOutside (node_t *headnode); -void FloodAreas (tree_t *tree); -void MarkVisibleSides (tree_t *tree, int start, int end); -void FreePortal (portal_t *p); -void EmitAreaPortals (node_t *headnode); -void MakeTreePortals (tree_t *tree); - -//============================================================================= -// glfile.c -//============================================================================= - -void OutputWinding(winding_t *w, FILE *glview); -void WriteGLView(tree_t *tree, char *source); - -//============================================================================= -// gldraw.c -//============================================================================= - -extern vec3_t draw_mins, draw_maxs; -extern qboolean drawflag; - -void Draw_ClearWindow (void); -void DrawWinding (winding_t *w); -void GLS_BeginScene (void); -void GLS_Winding (winding_t *w, int code); -void GLS_EndScene (void); - -//============================================================================= -// leakfile.c -//============================================================================= - -void LeakFile (tree_t *tree); - -//============================================================================= -// tree.c -//============================================================================= - -tree_t *Tree_Alloc(void); -void Tree_Free(tree_t *tree); -void Tree_Free_r(node_t *node); -void Tree_Print_r(node_t *node, int depth); -void Tree_FreePortals_r(node_t *node); -void Tree_PruneNodes_r(node_t *node); -void Tree_PruneNodes(node_t *node); diff --git a/tools/quake3/bspc/qfiles.h b/tools/quake3/bspc/qfiles.h deleted file mode 100644 index 317e47cc..00000000 --- a/tools/quake3/bspc/qfiles.h +++ /dev/null @@ -1,487 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -// -// qfiles.h: quake file formats -// This file must be identical in the quake and utils directories -// - -/* -======================================================================== - -The .pak files are just a linear collapse of a directory tree - -======================================================================== -*/ - -#define IDPAKHEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P') - -typedef struct -{ - char name[56]; - int filepos, filelen; -} dpackfile_t; - -typedef struct -{ - int ident; // == IDPAKHEADER - int dirofs; - int dirlen; -} dpackheader_t; - -#define MAX_FILES_IN_PACK 4096 - - -/* -======================================================================== - -PCX files are used for as many images as possible - -======================================================================== -*/ - -typedef struct -{ - char manufacturer; - char version; - char encoding; - char bits_per_pixel; - unsigned short xmin,ymin,xmax,ymax; - unsigned short hres,vres; - unsigned char palette[48]; - char reserved; - char color_planes; - unsigned short bytes_per_line; - unsigned short palette_type; - char filler[58]; - unsigned char data; // unbounded -} pcx_t; - - -/* -======================================================================== - -.MD2 triangle model file format - -======================================================================== -*/ - -#define IDALIASHEADER (('2'<<24)+('P'<<16)+('D'<<8)+'I') -#define ALIAS_VERSION 8 - -#define MAX_TRIANGLES 4096 -#define MAX_VERTS 2048 -#define MAX_FRAMES 512 -#define MAX_MD2SKINS 32 -#define MAX_SKINNAME 64 - -typedef struct -{ - short s; - short t; -} dstvert_t; - -typedef struct -{ - short index_xyz[3]; - short index_st[3]; -} dtriangle_t; - -typedef struct -{ - byte v[3]; // scaled byte to fit in frame mins/maxs - byte lightnormalindex; -} dtrivertx_t; - -#define DTRIVERTX_V0 0 -#define DTRIVERTX_V1 1 -#define DTRIVERTX_V2 2 -#define DTRIVERTX_LNI 3 -#define DTRIVERTX_SIZE 4 - -typedef struct -{ - float scale[3]; // multiply byte verts by this - float translate[3]; // then add this - char name[16]; // frame name from grabbing - dtrivertx_t verts[1]; // variable sized -} daliasframe_t; - - -// the glcmd format: -// a positive integer starts a tristrip command, followed by that many -// vertex structures. -// a negative integer starts a trifan command, followed by -x vertexes -// a zero indicates the end of the command list. -// a vertex consists of a floating point s, a floating point t, -// and an integer vertex index. - - -typedef struct -{ - int ident; - int version; - - int skinwidth; - int skinheight; - int framesize; // byte size of each frame - - int num_skins; - int num_xyz; - int num_st; // greater than num_xyz for seams - int num_tris; - int num_glcmds; // dwords in strip/fan command list - int num_frames; - - int ofs_skins; // each skin is a MAX_SKINNAME string - int ofs_st; // byte offset from start for stverts - int ofs_tris; // offset for dtriangles - int ofs_frames; // offset for first frame - int ofs_glcmds; - int ofs_end; // end of file - -} dmdl_t; - -/* -======================================================================== - -.SP2 sprite file format - -======================================================================== -*/ - -#define IDSPRITEHEADER (('2'<<24)+('S'<<16)+('D'<<8)+'I') - // little-endian "IDS2" -#define SPRITE_VERSION 2 - -typedef struct -{ - int width, height; - int origin_x, origin_y; // raster coordinates inside pic - char name[MAX_SKINNAME]; // name of pcx file -} dsprframe_t; - -typedef struct { - int ident; - int version; - int numframes; - dsprframe_t frames[1]; // variable sized -} dsprite_t; - -/* -============================================================================== - - .WAL texture file format - -============================================================================== -*/ - - -#define MIPLEVELS 4 -typedef struct miptex_s -{ - char name[32]; - unsigned width, height; - unsigned offsets[MIPLEVELS]; // four mip maps stored - char animname[32]; // next frame in animation chain - int flags; - int contents; - int value; -} miptex_t; - - - -/* -============================================================================== - - .BSP file format - -============================================================================== -*/ - -#define IDBSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I') - // little-endian "IBSP" - -#define BSPVERSION 38 - - -// upper design bounds -// leaffaces, leafbrushes, planes, and verts are still bounded by -// 16 bit short limits -#define MAX_MAP_MODELS 1024 -#define MAX_MAP_BRUSHES 8192 -#define MAX_MAP_ENTITIES 2048 -#define MAX_MAP_ENTSTRING 0x40000 -#define MAX_MAP_TEXINFO 8192 - -#define MAX_MAP_AREAS 256 -#define MAX_MAP_AREAPORTALS 1024 -#define MAX_MAP_PLANES 65536 -#define MAX_MAP_NODES 65536 -#define MAX_MAP_BRUSHSIDES 65536 -#define MAX_MAP_LEAFS 65536 -#define MAX_MAP_VERTS 65536 -#define MAX_MAP_FACES 65536 -#define MAX_MAP_LEAFFACES 65536 -#define MAX_MAP_LEAFBRUSHES 65536 -#define MAX_MAP_PORTALS 65536 -#define MAX_MAP_EDGES 128000 -#define MAX_MAP_SURFEDGES 256000 -#define MAX_MAP_LIGHTING 0x320000 -#define MAX_MAP_VISIBILITY 0x280000 - -// key / value pair sizes - -#define MAX_KEY 32 -#define MAX_VALUE 1024 - -//============================================================================= - -typedef struct -{ - int fileofs, filelen; -} lump_t; - -#define LUMP_ENTITIES 0 -#define LUMP_PLANES 1 -#define LUMP_VERTEXES 2 -#define LUMP_VISIBILITY 3 -#define LUMP_NODES 4 -#define LUMP_TEXINFO 5 -#define LUMP_FACES 6 -#define LUMP_LIGHTING 7 -#define LUMP_LEAFS 8 -#define LUMP_LEAFFACES 9 -#define LUMP_LEAFBRUSHES 10 -#define LUMP_EDGES 11 -#define LUMP_SURFEDGES 12 -#define LUMP_MODELS 13 -#define LUMP_BRUSHES 14 -#define LUMP_BRUSHSIDES 15 -#define LUMP_POP 16 -#define LUMP_AREAS 17 -#define LUMP_AREAPORTALS 18 -#define HEADER_LUMPS 19 - -typedef struct -{ - int ident; - int version; - lump_t lumps[HEADER_LUMPS]; -} dheader_t; - -typedef struct -{ - float mins[3], maxs[3]; - float origin[3]; // for sounds or lights - int headnode; - int firstface, numfaces; // submodels just draw faces - // without walking the bsp tree -} dmodel_t; - - -typedef struct -{ - float point[3]; -} dvertex_t; - - -// 0-2 are axial planes -#define PLANE_X 0 -#define PLANE_Y 1 -#define PLANE_Z 2 - -// 3-5 are non-axial planes snapped to the nearest -#define PLANE_ANYX 3 -#define PLANE_ANYY 4 -#define PLANE_ANYZ 5 - -// planes (x&~1) and (x&~1)+1 are allways opposites - -typedef struct -{ - float normal[3]; - float dist; - int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate -} dplane_t; - - -// contents flags are seperate bits -// a given brush can contribute multiple content bits -// multiple brushes can be in a single leaf - -// these definitions also need to be in q_shared.h! - -// lower bits are stronger, and will eat weaker brushes completely -#define CONTENTS_SOLID 1 // an eye is never valid in a solid -#define CONTENTS_WINDOW 2 // translucent, but not watery -#define CONTENTS_AUX 4 -#define CONTENTS_LAVA 8 -#define CONTENTS_SLIME 16 -#define CONTENTS_WATER 32 -#define CONTENTS_MIST 64 -#define LAST_VISIBLE_CONTENTS 64 - -// remaining contents are non-visible, and don't eat brushes - -#define CONTENTS_AREAPORTAL 0x8000 - -#define CONTENTS_PLAYERCLIP 0x10000 -#define CONTENTS_MONSTERCLIP 0x20000 - -// currents can be added to any other contents, and may be mixed -#define CONTENTS_CURRENT_0 0x40000 -#define CONTENTS_CURRENT_90 0x80000 -#define CONTENTS_CURRENT_180 0x100000 -#define CONTENTS_CURRENT_270 0x200000 -#define CONTENTS_CURRENT_UP 0x400000 -#define CONTENTS_CURRENT_DOWN 0x800000 - -#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity - -#define CONTENTS_MONSTER 0x2000000 // should never be on a brush, only in game -#define CONTENTS_DEADMONSTER 0x4000000 -#define CONTENTS_DETAIL 0x8000000 // brushes to be added after vis leafs -//renamed because it's in conflict with the Q3A translucent contents -#define CONTENTS_Q2TRANSLUCENT 0x10000000 // auto set if any surface has trans -#define CONTENTS_LADDER 0x20000000 - - - -#define SURF_LIGHT 0x1 // value will hold the light strength - -#define SURF_SLICK 0x2 // effects game physics - -#define SURF_SKY 0x4 // don't draw, but add to skybox -#define SURF_WARP 0x8 // turbulent water warp -#define SURF_TRANS33 0x10 -#define SURF_TRANS66 0x20 -#define SURF_FLOWING 0x40 // scroll towards angle -#define SURF_NODRAW 0x80 // don't bother referencing the texture - -#define SURF_HINT 0x100 // make a primary bsp splitter -#define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes - - - -typedef struct -{ - int planenum; - int children[2]; // negative numbers are -(leafs+1), not nodes - short mins[3]; // for frustom culling - short maxs[3]; - unsigned short firstface; - unsigned short numfaces; // counting both sides -} dnode_t; - - -typedef struct texinfo_s -{ - float vecs[2][4]; // [s/t][xyz offset] - int flags; // miptex flags + overrides - int value; // light emission, etc - char texture[32]; // texture name (textures/*.wal) - int nexttexinfo; // for animations, -1 = end of chain -} texinfo_t; - - -// note that edge 0 is never used, because negative edge nums are used for -// counterclockwise use of the edge in a face -typedef struct -{ - unsigned short v[2]; // vertex numbers -} dedge_t; - -#define MAXLIGHTMAPS 4 -typedef struct -{ - unsigned short planenum; - short side; - - int firstedge; // we must support > 64k edges - short numedges; - short texinfo; - -// lighting info - byte styles[MAXLIGHTMAPS]; - int lightofs; // start of [numstyles*surfsize] samples -} dface_t; - -typedef struct -{ - int contents; // OR of all brushes (not needed?) - - short cluster; - short area; - - short mins[3]; // for frustum culling - short maxs[3]; - - unsigned short firstleafface; - unsigned short numleaffaces; - - unsigned short firstleafbrush; - unsigned short numleafbrushes; -} dleaf_t; - -typedef struct -{ - unsigned short planenum; // facing out of the leaf - short texinfo; -} dbrushside_t; - -typedef struct -{ - int firstside; - int numsides; - int contents; -} dbrush_t; - -#define ANGLE_UP -1 -#define ANGLE_DOWN -2 - - -// the visibility lump consists of a header with a count, then -// byte offsets for the PVS and PHS of each cluster, then the raw -// compressed bit vectors -#define DVIS_PVS 0 -#define DVIS_PHS 1 -typedef struct -{ - int numclusters; - int bitofs[8][2]; // bitofs[numclusters][2] -} dvis_t; - -// each area has a list of portals that lead into other areas -// when portals are closed, other areas may not be visible or -// hearable even if the vis info says that it should be -typedef struct -{ - int portalnum; - int otherarea; -} dareaportal_t; - -typedef struct -{ - int numareaportals; - int firstareaportal; -} darea_t; diff --git a/tools/quake3/bspc/sinfiles.h b/tools/quake3/bspc/sinfiles.h deleted file mode 100644 index bcb5ee68..00000000 --- a/tools/quake3/bspc/sinfiles.h +++ /dev/null @@ -1,365 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/* -============================================================================== - - .BSP file format - -============================================================================== -*/ - -#define SIN - -#define SINBSPVERSION 41 - -// upper design bounds -// leaffaces, leafbrushes, planes, and verts are still bounded by -// 16 bit short limits -#define SIN_MAX_MAP_MODELS 1024 -#define SIN_MAX_MAP_BRUSHES 8192 -#define SIN_MAX_MAP_ENTITIES 2048 -#define SIN_MAX_MAP_ENTSTRING 0x40000 -#define SIN_MAX_MAP_TEXINFO 8192 - -#define SIN_MAX_MAP_AREAS 256 -#define SIN_MAX_MAP_AREAPORTALS 1024 -#define SIN_MAX_MAP_PLANES 65536 -#define SIN_MAX_MAP_NODES 65536 -#define SIN_MAX_MAP_BRUSHSIDES 65536 -#define SIN_MAX_MAP_LEAFS 65536 -#define SIN_MAX_MAP_VERTS 65536 -#define SIN_MAX_MAP_FACES 65536 -#define SIN_MAX_MAP_LEAFFACES 65536 -#define SIN_MAX_MAP_LEAFBRUSHES 65536 -#define SIN_MAX_MAP_PORTALS 65536 -#define SIN_MAX_MAP_EDGES 128000 -#define SIN_MAX_MAP_SURFEDGES 256000 -#define SIN_MAX_MAP_LIGHTING 0x320000 -#define SIN_MAX_MAP_VISIBILITY 0x280000 - -#ifdef SIN -#define SIN_MAX_MAP_LIGHTINFO 8192 -#endif - -#ifdef SIN -#undef SIN_MAX_MAP_LIGHTING //undef the Quake2 bsp version -#define SIN_MAX_MAP_LIGHTING 0x300000 -#endif - -#ifdef SIN -#undef SIN_MAX_MAP_VISIBILITY //undef the Quake2 bsp version -#define SIN_MAX_MAP_VISIBILITY 0x280000 -#endif - -//============================================================================= - -typedef struct -{ - int fileofs, filelen; -} sin_lump_t; - -#define SIN_LUMP_ENTITIES 0 -#define SIN_LUMP_PLANES 1 -#define SIN_LUMP_VERTEXES 2 -#define SIN_LUMP_VISIBILITY 3 -#define SIN_LUMP_NODES 4 -#define SIN_LUMP_TEXINFO 5 -#define SIN_LUMP_FACES 6 -#define SIN_LUMP_LIGHTING 7 -#define SIN_LUMP_LEAFS 8 -#define SIN_LUMP_LEAFFACES 9 -#define SIN_LUMP_LEAFBRUSHES 10 -#define SIN_LUMP_EDGES 11 -#define SIN_LUMP_SURFEDGES 12 -#define SIN_LUMP_MODELS 13 -#define SIN_LUMP_BRUSHES 14 -#define SIN_LUMP_BRUSHSIDES 15 -#define SIN_LUMP_POP 16 -#define SIN_LUMP_AREAS 17 -#define SIN_LUMP_AREAPORTALS 18 - -#ifdef SIN -#define SIN_LUMP_LIGHTINFO 19 -#define SINHEADER_LUMPS 20 -#endif - -typedef struct -{ - int ident; - int version; - sin_lump_t lumps[SINHEADER_LUMPS]; -} sin_dheader_t; - -typedef struct -{ - float mins[3], maxs[3]; - float origin[3]; // for sounds or lights - int headnode; - int firstface, numfaces; // submodels just draw faces - // without walking the bsp tree -} sin_dmodel_t; - -typedef struct -{ - float point[3]; -} sin_dvertex_t; - - -// 0-2 are axial planes -#define PLANE_X 0 -#define PLANE_Y 1 -#define PLANE_Z 2 - -// 3-5 are non-axial planes snapped to the nearest -#define PLANE_ANYX 3 -#define PLANE_ANYY 4 -#define PLANE_ANYZ 5 - -// planes (x&~1) and (x&~1)+1 are allways opposites - -typedef struct -{ - float normal[3]; - float dist; - int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate -} sin_dplane_t; - - -// contents flags are seperate bits -// a given brush can contribute multiple content bits -// multiple brushes can be in a single leaf - -// these definitions also need to be in q_shared.h! - -// lower bits are stronger, and will eat weaker brushes completely -#ifdef SIN -#define CONTENTS_FENCE 4 -#endif -// remaining contents are non-visible, and don't eat brushes - -#ifdef SIN -#define CONTENTS_DUMMYFENCE 0x1000 -#endif - -#ifdef SIN -#define SURF_MASKED 0x2 // surface texture is masked -#endif - -#define SURF_SKY 0x4 // don't draw, but add to skybox -#define SURF_WARP 0x8 // turbulent water warp - -#ifdef SIN -#define SURF_NONLIT 0x10 // surface is not lit -#define SURF_NOFILTER 0x20 // surface is not bi-linear filtered -#endif - -#define SURF_FLOWING 0x40 // scroll towards angle -#define SURF_NODRAW 0x80 // don't bother referencing the texture - -#define SURF_HINT 0x100 // make a primary bsp splitter -#define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes - -#ifdef SIN -#define SURF_CONVEYOR 0x40 // surface is not lit -#endif - -#ifdef SIN -#define SURF_WAVY 0x400 // surface has waves -#define SURF_RICOCHET 0x800 // projectiles bounce literally bounce off this surface -#define SURF_PRELIT 0x1000 // surface has intensity information for pre-lighting -#define SURF_MIRROR 0x2000 // surface is a mirror -#define SURF_CONSOLE 0x4000 // surface is a console -#define SURF_USECOLOR 0x8000 // surface is lit with non-lit * color -#define SURF_HARDWAREONLY 0x10000 // surface has been damaged -#define SURF_DAMAGE 0x20000 // surface can be damaged -#define SURF_WEAK 0x40000 // surface has weak hit points -#define SURF_NORMAL 0x80000 // surface has normal hit points -#define SURF_ADD 0x100000 // surface will be additive -#define SURF_ENVMAPPED 0x200000 // surface is envmapped -#define SURF_RANDOMANIMATE 0x400000 // surface start animating on a random frame -#define SURF_ANIMATE 0x800000 // surface animates -#define SURF_RNDTIME 0x1000000 // time between animations is random -#define SURF_TRANSLATE 0x2000000 // surface translates -#define SURF_NOMERGE 0x4000000 // surface is not merged in csg phase -#define SURF_TYPE_BIT0 0x8000000 // 0 bit of surface type -#define SURF_TYPE_BIT1 0x10000000 // 1 bit of surface type -#define SURF_TYPE_BIT2 0x20000000 // 2 bit of surface type -#define SURF_TYPE_BIT3 0x40000000 // 3 bit of surface type - -#define SURF_START_BIT 27 -#define SURFACETYPE_FROM_FLAGS( x ) ( ( x >> (SURF_START_BIT) ) & 0xf ) - - -#define SURF_TYPE_SHIFT(x) ( (x) << (SURF_START_BIT) ) // macro for getting proper bit mask - -#define SURF_TYPE_NONE SURF_TYPE_SHIFT(0) -#define SURF_TYPE_WOOD SURF_TYPE_SHIFT(1) -#define SURF_TYPE_METAL SURF_TYPE_SHIFT(2) -#define SURF_TYPE_STONE SURF_TYPE_SHIFT(3) -#define SURF_TYPE_CONCRETE SURF_TYPE_SHIFT(4) -#define SURF_TYPE_DIRT SURF_TYPE_SHIFT(5) -#define SURF_TYPE_FLESH SURF_TYPE_SHIFT(6) -#define SURF_TYPE_GRILL SURF_TYPE_SHIFT(7) -#define SURF_TYPE_GLASS SURF_TYPE_SHIFT(8) -#define SURF_TYPE_FABRIC SURF_TYPE_SHIFT(9) -#define SURF_TYPE_MONITOR SURF_TYPE_SHIFT(10) -#define SURF_TYPE_GRAVEL SURF_TYPE_SHIFT(11) -#define SURF_TYPE_VEGETATION SURF_TYPE_SHIFT(12) -#define SURF_TYPE_PAPER SURF_TYPE_SHIFT(13) -#define SURF_TYPE_DUCT SURF_TYPE_SHIFT(14) -#define SURF_TYPE_WATER SURF_TYPE_SHIFT(15) -#endif - - -typedef struct -{ - int planenum; - int children[2]; // negative numbers are -(leafs+1), not nodes - short mins[3]; // for frustom culling - short maxs[3]; - unsigned short firstface; - unsigned short numfaces; // counting both sides -} sin_dnode_t; - -#ifdef SIN - -typedef struct sin_lightvalue_s -{ - int value; // light emission, etc - vec3_t color; - float direct; - float directangle; - float directstyle; - char directstylename[32]; -} sin_lightvalue_t; - -typedef struct sin_texinfo_s -{ - float vecs[2][4]; // [s/t][xyz offset] - int flags; // miptex flags + overrides - char texture[64]; // texture name (textures/*.wal) - int nexttexinfo; // for animations, -1 = end of chain - float trans_mag; - int trans_angle; - int base_angle; - float animtime; - float nonlit; - float translucence; - float friction; - float restitution; - vec3_t color; - char groupname[32]; -} sin_texinfo_t; - -#endif //SIN - -// 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 -} sin_dedge_t; - -#ifdef MAXLIGHTMAPS -#undef MAXLIGHTMAPS -#endif -#define MAXLIGHTMAPS 16 -typedef struct -{ - unsigned short planenum; - short side; - - int firstedge; // we must support > 64k edges - short numedges; - short texinfo; - -// lighting info - byte styles[MAXLIGHTMAPS]; - int lightofs; // start of [numstyles*surfsize] samples -#ifdef SIN - int lightinfo; -#endif -} sin_dface_t; - -typedef struct -{ - int contents; // OR of all brushes (not needed?) - - short cluster; - short area; - - short mins[3]; // for frustum culling - short maxs[3]; - - unsigned short firstleafface; - unsigned short numleaffaces; - - unsigned short firstleafbrush; - unsigned short numleafbrushes; -} sin_dleaf_t; - -typedef struct -{ - unsigned short planenum; // facing out of the leaf - short texinfo; -#ifdef SIN - int lightinfo; -#endif -} sin_dbrushside_t; - -typedef struct -{ - int firstside; - int numsides; - int contents; -} sin_dbrush_t; - -#define ANGLE_UP -1 -#define ANGLE_DOWN -2 - - -// the visibility lump consists of a header with a count, then -// byte offsets for the PVS and PHS of each cluster, then the raw -// compressed bit vectors -#define DVIS_PVS 0 -#define DVIS_PHS 1 -typedef struct -{ - int numclusters; - int bitofs[8][2]; // bitofs[numclusters][2] -} sin_dvis_t; - -// each area has a list of portals that lead into other areas -// when portals are closed, other areas may not be visible or -// hearable even if the vis info says that it should be -typedef struct -{ - int portalnum; - int otherarea; -} sin_dareaportal_t; - -typedef struct -{ - int numareaportals; - int firstareaportal; -} sin_darea_t; diff --git a/tools/quake3/bspc/tetrahedron.c b/tools/quake3/bspc/tetrahedron.c deleted file mode 100644 index 51a0c3a1..00000000 --- a/tools/quake3/bspc/tetrahedron.c +++ /dev/null @@ -1,1389 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "l_mem.h" -#include "botlib/aasfile.h" -#include "aas_store.h" -#include "aas_cfg.h" -#include "aas_file.h" - -// -// creating tetrahedrons from a arbitrary world bounded by triangles -// -// a triangle has 3 corners and 3 edges -// a tetrahedron is build out of 4 triangles -// a tetrahedron has 6 edges -// we start with a world bounded by triangles, a side of a triangle facing -// towards the oudside of the world is marked as part of tetrahedron -1 -// -// a tetrahedron is defined by two non-coplanar triangles with a shared edge -// -// a tetrahedron is defined by one triangle and a vertex not in the triangle plane -// -// if all triangles using a specific vertex have tetrahedrons -// at both sides then this vertex will never be part of a new tetrahedron -// -// if all triangles using a specific edge have tetrahedrons -// at both sides then this vertex will never be part of a new tetrahedron -// -// each triangle can only be shared by two tetrahedrons -// when all triangles have tetrahedrons at both sides then we're done -// -// if we cannot create any new tetrahedrons and there is at least one triangle -// which has a tetrahedron only at one side then the world leaks -// - -#define Sign(x) (x < 0 ? 1 : 0) - -#define MAX_TH_VERTEXES 128000 -#define MAX_TH_PLANES 128000 -#define MAX_TH_EDGES 512000 -#define MAX_TH_TRIANGLES 51200 -#define MAX_TH_TETRAHEDRONS 12800 - -#define PLANEHASH_SIZE 1024 -#define EDGEHASH_SIZE 1024 -#define TRIANGLEHASH_SIZE 1024 -#define VERTEXHASH_SHIFT 7 -#define VERTEXHASH_SIZE ((MAX_MAP_BOUNDS>>(VERTEXHASH_SHIFT-1))+1) //was 64 - -#define NORMAL_EPSILON 0.0001 -#define DIST_EPSILON 0.1 -#define VERTEX_EPSILON 0.01 -#define INTEGRAL_EPSILON 0.01 - - -//plane -typedef struct th_plane_s -{ - vec3_t normal; - float dist; - int type; - int signbits; - struct th_plane_s *hashnext; //next plane in hash -} th_plane_t; - -//vertex -typedef struct th_vertex_s -{ - vec3_t v; - int usercount; //2x the number of to be processed - //triangles using this vertex - struct th_vertex_s *hashnext; //next vertex in hash -} th_vertex_t; - -//edge -typedef struct th_edge_s -{ - int v[2]; //vertex indexes - int usercount; //number of to be processed - //triangles using this edge - struct th_edge_s *hashnext; //next edge in hash -} th_edge_t; - -//triangle -typedef struct th_triangle_s -{ - int edges[3]; //negative if edge is flipped - th_plane_t planes[3]; //triangle bounding planes - int planenum; //plane the triangle is in - int front; //tetrahedron at the front - int back; //tetrahedron at the back - vec3_t mins, maxs; //triangle bounding box - struct th_triangle_s *prev, *next; //links in linked triangle lists - struct th_triangle_s *hashnext; //next triangle in hash -} th_triangle_t; - -//tetrahedron -typedef struct th_tetrahedron_s -{ - int triangles[4]; //negative if at backside of triangle - float volume; //tetrahedron volume -} th_tetrahedron_t; - -typedef struct th_s -{ - //vertexes - int numvertexes; - th_vertex_t *vertexes; - th_vertex_t *vertexhash[VERTEXHASH_SIZE * VERTEXHASH_SIZE]; - //planes - int numplanes; - th_plane_t *planes; - th_plane_t *planehash[PLANEHASH_SIZE]; - //edges - int numedges; - th_edge_t *edges; - th_edge_t *edgehash[EDGEHASH_SIZE]; - //triangles - int numtriangles; - th_triangle_t *triangles; - th_triangle_t *trianglehash[TRIANGLEHASH_SIZE]; - //tetrahedrons - int numtetrahedrons; - th_tetrahedron_t *tetrahedrons; -} th_t; - -th_t thworld; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_InitMaxTH(void) -{ - //get memory for the tetrahedron data - thworld.vertexes = (th_vertex_t *) GetClearedMemory(MAX_TH_VERTEXES * sizeof(th_vertex_t)); - thworld.planes = (th_plane_t *) GetClearedMemory(MAX_TH_PLANES * sizeof(th_plane_t)); - thworld.edges = (th_edge_t *) GetClearedMemory(MAX_TH_EDGES * sizeof(th_edge_t)); - thworld.triangles = (th_triangle_t *) GetClearedMemory(MAX_TH_TRIANGLES * sizeof(th_triangle_t)); - thworld.tetrahedrons = (th_tetrahedron_t *) GetClearedMemory(MAX_TH_TETRAHEDRONS * sizeof(th_tetrahedron_t)); - //reset the hash tables - memset(thworld.vertexhash, 0, VERTEXHASH_SIZE * sizeof(th_vertex_t *)); - memset(thworld.planehash, 0, PLANEHASH_SIZE * sizeof(th_plane_t *)); - memset(thworld.edgehash, 0, EDGEHASH_SIZE * sizeof(th_edge_t *)); - memset(thworld.trianglehash, 0, TRIANGLEHASH_SIZE * sizeof(th_triangle_t *)); -} //end of the function TH_InitMaxTH -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_FreeMaxTH(void) -{ - if (thworld.vertexes) FreeMemory(thworld.vertexes); - thworld.vertexes = NULL; - thworld.numvertexes = 0; - if (thworld.planes) FreeMemory(thworld.planes); - thworld.planes = NULL; - thworld.numplanes = 0; - if (thworld.edges) FreeMemory(thworld.edges); - thworld.edges = NULL; - thworld.numedges = 0; - if (thworld.triangles) FreeMemory(thworld.triangles); - thworld.triangles = NULL; - thworld.numtriangles = 0; - if (thworld.tetrahedrons) FreeMemory(thworld.tetrahedrons); - thworld.tetrahedrons = NULL; - thworld.numtetrahedrons = 0; -} //end of the function TH_FreeMaxTH -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float TH_TriangleArea(th_triangle_t *tri) -{ - return 0; -} //end of the function TH_TriangleArea -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -float TH_TetrahedronVolume(th_tetrahedron_t *tetrahedron) -{ - int edgenum, verts[3], i, j, v2; - float volume, d; - th_triangle_t *tri, *tri2; - th_plane_t *plane; - - tri = &thworld.triangles[abs(tetrahedron->triangles[0])]; - for (i = 0; i < 3; i++) - { - edgenum = tri->edges[i]; - if (edgenum < 0) verts[i] = thworld.edges[abs(edgenum)].v[1]; - else verts[i] = thworld.edges[edgenum].v[0]; - } //end for - // - tri2 = &thworld.triangles[abs(tetrahedron->triangles[1])]; - for (j = 0; j < 3; j++) - { - edgenum = tri2->edges[i]; - if (edgenum < 0) v2 = thworld.edges[abs(edgenum)].v[1]; - else v2 = thworld.edges[edgenum].v[0]; - if (v2 != verts[0] && - v2 != verts[1] && - v2 != verts[2]) break; - } //end for - - plane = &thworld.planes[tri->planenum]; - d = -(DotProduct (thworld.vertexes[v2].v, plane->normal) - plane->dist); - volume = TH_TriangleArea(tri) * d / 3; - return volume; -} //end of the function TH_TetrahedronVolume -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_PlaneSignBits(vec3_t normal) -{ - int i, signbits; - - signbits = 0; - for (i = 2; i >= 0; i--) - { - signbits = (signbits << 1) + Sign(normal[i]); - } //end for - return signbits; -} //end of the function TH_PlaneSignBits -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_PlaneTypeForNormal(vec3_t normal) -{ - vec_t ax, ay, az; - -// NOTE: should these have an epsilon around 1.0? - if (normal[0] == 1.0 || normal[0] == -1.0) - return PLANE_X; - if (normal[1] == 1.0 || normal[1] == -1.0) - return PLANE_Y; - if (normal[2] == 1.0 || normal[2] == -1.0) - return PLANE_Z; - - ax = fabs(normal[0]); - ay = fabs(normal[1]); - az = fabs(normal[2]); - - if (ax >= ay && ax >= az) - return PLANE_ANYX; - if (ay >= ax && ay >= az) - return PLANE_ANYY; - return PLANE_ANYZ; -} //end of the function TH_PlaneTypeForNormal -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean TH_PlaneEqual(th_plane_t *p, vec3_t normal, vec_t dist) -{ - if ( - fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON - && fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON - && fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON - && fabs(p->dist - dist) < DIST_EPSILON ) - return true; - return false; -} //end of the function TH_PlaneEqual -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_AddPlaneToHash(th_plane_t *p) -{ - int hash; - - hash = (int)fabs(p->dist) / 8; - hash &= (PLANEHASH_SIZE-1); - - p->hashnext = thworld.planehash[hash]; - thworld.planehash[hash] = p; -} //end of the function TH_AddPlaneToHash -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_CreateFloatPlane(vec3_t normal, vec_t dist) -{ - th_plane_t *p, temp; - - if (VectorLength(normal) < 0.5) - Error ("FloatPlane: bad normal"); - // create a new plane - if (thworld.numplanes+2 > MAX_TH_PLANES) - Error ("MAX_TH_PLANES"); - - p = &thworld.planes[thworld.numplanes]; - VectorCopy (normal, p->normal); - p->dist = dist; - p->type = (p+1)->type = TH_PlaneTypeForNormal (p->normal); - p->signbits = TH_PlaneSignBits(p->normal); - - VectorSubtract (vec3_origin, normal, (p+1)->normal); - (p+1)->dist = -dist; - (p+1)->signbits = TH_PlaneSignBits((p+1)->normal); - - thworld.numplanes += 2; - - // allways put axial planes facing positive first - if (p->type < 3) - { - if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0) - { - // flip order - temp = *p; - *p = *(p+1); - *(p+1) = temp; - - TH_AddPlaneToHash(p); - TH_AddPlaneToHash(p+1); - return thworld.numplanes - 1; - } //end if - } //end if - - TH_AddPlaneToHash(p); - TH_AddPlaneToHash(p+1); - return thworld.numplanes - 2; -} //end of the function TH_CreateFloatPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_SnapVector(vec3_t normal) -{ - int i; - - for (i = 0; i < 3; i++) - { - if ( fabs(normal[i] - 1) < NORMAL_EPSILON ) - { - VectorClear (normal); - normal[i] = 1; - break; - } //end if - if ( fabs(normal[i] - -1) < NORMAL_EPSILON ) - { - VectorClear (normal); - normal[i] = -1; - break; - } //end if - } //end for -} //end of the function TH_SnapVector -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_SnapPlane(vec3_t normal, vec_t *dist) -{ - TH_SnapVector(normal); - - if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON) - *dist = Q_rint(*dist); -} //end of the function TH_SnapPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_FindFloatPlane(vec3_t normal, vec_t dist) -{ - int i; - th_plane_t *p; - int hash, h; - - TH_SnapPlane (normal, &dist); - hash = (int)fabs(dist) / 8; - hash &= (PLANEHASH_SIZE-1); - - // search the border bins as well - for (i = -1; i <= 1; i++) - { - h = (hash+i)&(PLANEHASH_SIZE-1); - for (p = thworld.planehash[h]; p; p = p->hashnext) - { - if (TH_PlaneEqual(p, normal, dist)) - { - return p - thworld.planes; - } //end if - } //end for - } //end for - return TH_CreateFloatPlane(normal, dist); -} //end of the function TH_FindFloatPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_PlaneFromPoints(int v1, int v2, int v3) -{ - vec3_t t1, t2, normal; - vec_t dist; - float *p0, *p1, *p2; - - p0 = thworld.vertexes[v1].v; - p1 = thworld.vertexes[v2].v; - p2 = thworld.vertexes[v3].v; - - VectorSubtract(p0, p1, t1); - VectorSubtract(p2, p1, t2); - CrossProduct(t1, t2, normal); - VectorNormalize(normal); - - dist = DotProduct(p0, normal); - - return TH_FindFloatPlane(normal, dist); -} //end of the function TH_PlaneFromPoints -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_AddEdgeUser(int edgenum) -{ - th_edge_t *edge; - - edge = &thworld.edges[abs(edgenum)]; - //increase edge user count - edge->usercount++; - //increase vertex user count as well - thworld.vertexes[edge->v[0]].usercount++; - thworld.vertexes[edge->v[1]].usercount++; -} //end of the function TH_AddEdgeUser -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_RemoveEdgeUser(int edgenum) -{ - th_edge_t *edge; - - edge = &thworld.edges[abs(edgenum)]; - //decrease edge user count - edge->usercount--; - //decrease vertex user count as well - thworld.vertexes[edge->v[0]].usercount--; - thworld.vertexes[edge->v[1]].usercount--; -} //end of the function TH_RemoveEdgeUser -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_FreeTriangleEdges(th_triangle_t *tri) -{ - int i; - - for (i = 0; i < 3; i++) - { - TH_RemoveEdgeUser(abs(tri->edges[i])); - } //end for -} //end of the function TH_FreeTriangleEdges -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -unsigned TH_HashVec(vec3_t vec) -{ - int x, y; - - x = (MAX_MAP_BOUNDS + (int)(vec[0]+0.5)) >> VERTEXHASH_SHIFT; - y = (MAX_MAP_BOUNDS + (int)(vec[1]+0.5)) >> VERTEXHASH_SHIFT; - - if (x < 0 || x >= VERTEXHASH_SIZE || y < 0 || y >= VERTEXHASH_SIZE) - Error("HashVec: point %f %f %f outside valid range", vec[0], vec[1], vec[2]); - - return y*VERTEXHASH_SIZE + x; -} //end of the function TH_HashVec -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_FindVertex(vec3_t v) -{ - int i, h; - th_vertex_t *vertex; - vec3_t vert; - - for (i = 0; i < 3; i++) - { - if ( fabs(v[i] - Q_rint(v[i])) < INTEGRAL_EPSILON) - vert[i] = Q_rint(v[i]); - else - vert[i] = v[i]; - } //end for - - h = TH_HashVec(vert); - - for (vertex = thworld.vertexhash[h]; vertex; vertex = vertex->hashnext) - { - if (fabs(vertex->v[0] - vert[0]) < VERTEX_EPSILON && - fabs(vertex->v[1] - vert[1]) < VERTEX_EPSILON && - fabs(vertex->v[2] - vert[2]) < VERTEX_EPSILON) - { - return vertex - thworld.vertexes; - } //end if - } //end for - return 0; -} //end of the function TH_FindVertex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_AddVertexToHash(th_vertex_t *vertex) -{ - int hashvalue; - - hashvalue = TH_HashVec(vertex->v); - vertex->hashnext = thworld.vertexhash[hashvalue]; - thworld.vertexhash[hashvalue] = vertex; -} //end of the function TH_AddVertexToHash -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_CreateVertex(vec3_t v) -{ - if (thworld.numvertexes == 0) thworld.numvertexes = 1; - if (thworld.numvertexes >= MAX_TH_VERTEXES) - Error("MAX_TH_VERTEXES"); - VectorCopy(v, thworld.vertexes[thworld.numvertexes].v); - thworld.vertexes[thworld.numvertexes].usercount = 0; - TH_AddVertexToHash(&thworld.vertexes[thworld.numvertexes]); - thworld.numvertexes++; - return thworld.numvertexes-1; -} //end of the function TH_CreateVertex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_FindOrCreateVertex(vec3_t v) -{ - int vertexnum; - - vertexnum = TH_FindVertex(v); - if (!vertexnum) vertexnum = TH_CreateVertex(v); - return vertexnum; -} //end of the function TH_FindOrCreateVertex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_FindEdge(int v1, int v2) -{ - int hashvalue; - th_edge_t *edge; - - hashvalue = (v1 + v2) & (EDGEHASH_SIZE-1); - - for (edge = thworld.edgehash[hashvalue]; edge; edge = edge->hashnext) - { - if (edge->v[0] == v1 && edge->v[1] == v2) return edge - thworld.edges; - if (edge->v[1] == v1 && edge->v[0] == v2) return -(edge - thworld.edges); - } //end for - return 0; -} //end of the function TH_FindEdge -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_AddEdgeToHash(th_edge_t *edge) -{ - int hashvalue; - - hashvalue = (edge->v[0] + edge->v[1]) & (EDGEHASH_SIZE-1); - edge->hashnext = thworld.edgehash[hashvalue]; - thworld.edgehash[hashvalue] = edge; -} //end of the function TH_AddEdgeToHash -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_CreateEdge(int v1, int v2) -{ - th_edge_t *edge; - - if (thworld.numedges == 0) thworld.numedges = 1; - if (thworld.numedges >= MAX_TH_EDGES) - Error("MAX_TH_EDGES"); - edge = &thworld.edges[thworld.numedges++]; - edge->v[0] = v1; - edge->v[1] = v2; - TH_AddEdgeToHash(edge); - return thworld.numedges-1; -} //end of the function TH_CreateEdge -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_FindOrCreateEdge(int v1, int v2) -{ - int edgenum; - - edgenum = TH_FindEdge(v1, v2); - if (!edgenum) edgenum = TH_CreateEdge(v1, v2); - return edgenum; -} //end of the function TH_FindOrCreateEdge -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_FindTriangle(int verts[3]) -{ - int i, hashvalue, edges[3]; - th_triangle_t *tri; - - for (i = 0; i < 3; i++) - { - edges[i] = TH_FindEdge(verts[i], verts[(i+1)%3]); - if (!edges[i]) return false; - } //end for - hashvalue = (abs(edges[0]) + abs(edges[1]) + abs(edges[2])) & (TRIANGLEHASH_SIZE-1); - for (tri = thworld.trianglehash[hashvalue]; tri; tri = tri->next) - { - for (i = 0; i < 3; i++) - { - if (abs(tri->edges[i]) != abs(edges[0]) && - abs(tri->edges[i]) != abs(edges[1]) && - abs(tri->edges[i]) != abs(edges[2])) break; - } //end for - if (i >= 3) return tri - thworld.triangles; - } //end for - return 0; -} //end of the function TH_FindTriangle -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_AddTriangleToHash(th_triangle_t *tri) -{ - int hashvalue; - - hashvalue = (abs(tri->edges[0]) + abs(tri->edges[1]) + abs(tri->edges[2])) & (TRIANGLEHASH_SIZE-1); - tri->hashnext = thworld.trianglehash[hashvalue]; - thworld.trianglehash[hashvalue] = tri; -} //end of the function TH_AddTriangleToHash -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_CreateTrianglePlanes(int verts[3], th_plane_t *triplane, th_plane_t *planes) -{ - int i; - vec3_t dir; - - for (i = 0; i < 3; i++) - { - VectorSubtract(thworld.vertexes[verts[(i+1)%3]].v, thworld.vertexes[verts[i]].v, dir); - CrossProduct(dir, triplane->normal, planes[i].normal); - VectorNormalize(planes[i].normal); - planes[i].dist = DotProduct(thworld.vertexes[verts[i]].v, planes[i].normal); - } //end for -} //end of the function TH_CreateTrianglePlanes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_CreateTriangle(int verts[3]) -{ - th_triangle_t *tri; - int i; - - if (thworld.numtriangles == 0) thworld.numtriangles = 1; - if (thworld.numtriangles >= MAX_TH_TRIANGLES) - Error("MAX_TH_TRIANGLES"); - tri = &thworld.triangles[thworld.numtriangles++]; - for (i = 0; i < 3; i++) - { - tri->edges[i] = TH_FindOrCreateEdge(verts[i], verts[(i+1)%3]); - TH_AddEdgeUser(abs(tri->edges[i])); - } //end for - tri->front = 0; - tri->back = 0; - tri->planenum = TH_PlaneFromPoints(verts[0], verts[1], verts[2]); - tri->prev = NULL; - tri->next = NULL; - tri->hashnext = NULL; - TH_CreateTrianglePlanes(verts, &thworld.planes[tri->planenum], tri->planes); - TH_AddTriangleToHash(tri); - ClearBounds(tri->mins, tri->maxs); - for (i = 0; i < 3; i++) - { - AddPointToBounds(thworld.vertexes[verts[i]].v, tri->mins, tri->maxs); - } //end for - return thworld.numtriangles-1; -} //end of the function TH_CreateTriangle -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_CreateTetrahedron(int triangles[4]) -{ - th_tetrahedron_t *tetrahedron; - int i; - - if (thworld.numtetrahedrons == 0) thworld.numtetrahedrons = 1; - if (thworld.numtetrahedrons >= MAX_TH_TETRAHEDRONS) - Error("MAX_TH_TETRAHEDRONS"); - tetrahedron = &thworld.tetrahedrons[thworld.numtetrahedrons++]; - for (i = 0; i < 4; i++) - { - tetrahedron->triangles[i] = triangles[i]; - if (thworld.triangles[abs(triangles[i])].front) - { - thworld.triangles[abs(triangles[i])].back = thworld.numtetrahedrons-1; - } //end if - else - { - thworld.triangles[abs(triangles[i])].front = thworld.numtetrahedrons-1; - } //end else - } //end for - tetrahedron->volume = 0; - return thworld.numtetrahedrons-1; -} //end of the function TH_CreateTetrahedron -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_IntersectTrianglePlanes(int v1, int v2, th_plane_t *triplane, th_plane_t *planes) -{ - float *p1, *p2, front, back, frac, d; - int i, side, lastside; - vec3_t mid; - - p1 = thworld.vertexes[v1].v; - p2 = thworld.vertexes[v2].v; - - front = DotProduct(p1, triplane->normal) - triplane->dist; - back = DotProduct(p2, triplane->normal) - triplane->dist; - //if both points at the same side of the plane - if (front < 0.1 && back < 0.1) return false; - if (front > -0.1 && back > -0.1) return false; - // - frac = front/(front-back); - mid[0] = p1[0] + (p2[0] - p1[0]) * frac; - mid[1] = p1[1] + (p2[1] - p1[1]) * frac; - mid[2] = p1[2] + (p2[2] - p1[2]) * frac; - //if the mid point is at the same side of all the tri bounding planes - lastside = 0; - for (i = 0; i < 3; i++) - { - d = DotProduct(mid, planes[i].normal) - planes[i].dist; - side = d < 0; - if (i && side != lastside) return false; - lastside = side; - } //end for - return true; -} //end of the function TH_IntersectTrianglePlanes -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_OutsideBoundingBox(int v1, int v2, vec3_t mins, vec3_t maxs) -{ - float *p1, *p2; - int i; - - p1 = thworld.vertexes[v1].v; - p2 = thworld.vertexes[v2].v; - //if both points are at the outer side of one of the bounding box planes - for (i = 0; i < 3; i++) - { - if (p1[i] < mins[i] && p2[i] < mins[i]) return true; - if (p1[i] > maxs[i] && p2[i] > maxs[i]) return true; - } //end for - return false; -} //end of the function TH_OutsideBoundingBox -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_TryEdge(int v1, int v2) -{ - int i, j, v; - th_plane_t *plane; - th_triangle_t *tri; - - //if the edge already exists it must be valid - if (TH_FindEdge(v1, v2)) return true; - //test the edge with all existing triangles - for (i = 1; i < thworld.numtriangles; i++) - { - tri = &thworld.triangles[i]; - //if triangle is enclosed by two tetrahedrons we don't have to test it - //because the edge always has to go through another triangle of those - //tetrahedrons first to reach the enclosed triangle - if (tri->front && tri->back) continue; - //if the edges is totally outside the triangle bounding box - if (TH_OutsideBoundingBox(v1, v2, tri->mins, tri->maxs)) continue; - //if one of the edge vertexes is used by this triangle - for (j = 0; j < 3; j++) - { - v = thworld.edges[abs(tri->edges[j])].v[tri->edges[j] < 0]; - if (v == v1 || v == v2) break; - } //end for - if (j < 3) continue; - //get the triangle plane - plane = &thworld.planes[tri->planenum]; - //if the edge intersects with a triangle then it's not valid - if (TH_IntersectTrianglePlanes(v1, v2, plane, tri->planes)) return false; - } //end for - return true; -} //end of the function TH_TryEdge -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_TryTriangle(int verts[3]) -{ - th_plane_t planes[3], triplane; - vec3_t t1, t2; - float *p0, *p1, *p2; - int i, j; - - p0 = thworld.vertexes[verts[0]].v; - p1 = thworld.vertexes[verts[1]].v; - p2 = thworld.vertexes[verts[2]].v; - - VectorSubtract(p0, p1, t1); - VectorSubtract(p2, p1, t2); - CrossProduct(t1, t2, triplane.normal); - VectorNormalize(triplane.normal); - triplane.dist = DotProduct(p0, triplane.normal); - // - TH_CreateTrianglePlanes(verts, &triplane, planes); - //test if any existing edge intersects with this triangle - for (i = 1; i < thworld.numedges; i++) - { - //if the edge is only used by triangles with tetrahedrons at both sides - if (!thworld.edges[i].usercount) continue; - //if one of the triangle vertexes is used by this edge - for (j = 0; j < 3; j++) - { - if (verts[j] == thworld.edges[j].v[0] || - verts[j] == thworld.edges[j].v[1]) break; - } //end for - if (j < 3) continue; - //if this edge intersects with the triangle - if (TH_IntersectTrianglePlanes(thworld.edges[i].v[0], thworld.edges[i].v[1], &triplane, planes)) return false; - } //end for - return true; -} //end of the function TH_TryTriangle -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_AddTriangleToList(th_triangle_t **trianglelist, th_triangle_t *tri) -{ - tri->prev = NULL; - tri->next = *trianglelist; - if (*trianglelist) (*trianglelist)->prev = tri; - *trianglelist = tri; -} //end of the function TH_AddTriangleToList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_RemoveTriangleFromList(th_triangle_t **trianglelist, th_triangle_t *tri) -{ - if (tri->next) tri->next->prev = tri->prev; - if (tri->prev) tri->prev->next = tri->next; - else *trianglelist = tri->next; -} //end of the function TH_RemoveTriangleFromList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_FindTetrahedron1(th_triangle_t *tri, int *triangles) -{ - int i, j, edgenum, side, v1, v2, v3, v4; - int verts1[3], verts2[3]; - th_triangle_t *tri2; - - //find another triangle with a shared edge - for (tri2 = tri->next; tri2; tri2 = tri2->next) - { - //if the triangles are in the same plane - if ((tri->planenum & ~1) == (tri2->planenum & ~1)) continue; - //try to find a shared edge - for (i = 0; i < 3; i++) - { - edgenum = abs(tri->edges[i]); - for (j = 0; j < 3; j++) - { - if (edgenum == abs(tri2->edges[j])) break; - } //end for - if (j < 3) break; - } //end for - //if the triangles have a shared edge - if (i < 3) - { - edgenum = tri->edges[(i+1)%3]; - if (edgenum < 0) v1 = thworld.edges[abs(edgenum)].v[0]; - else v1 = thworld.edges[edgenum].v[1]; - edgenum = tri2->edges[(j+1)%3]; - if (edgenum < 0) v2 = thworld.edges[abs(edgenum)].v[0]; - else v2 = thworld.edges[edgenum].v[1]; - //try the new edge - if (TH_TryEdge(v1, v2)) - { - edgenum = tri->edges[i]; - side = edgenum < 0; - //get the vertexes of the shared edge - v3 = thworld.edges[abs(edgenum)].v[side]; - v4 = thworld.edges[abs(edgenum)].v[!side]; - //try the two new triangles - verts1[0] = v1; - verts1[1] = v2; - verts1[2] = v3; - triangles[2] = TH_FindTriangle(verts1); - if (triangles[2] || TH_TryTriangle(verts1)) - { - verts2[0] = v2; - verts2[1] = v1; - verts2[2] = v4; - triangles[3] = TH_FindTriangle(verts2); - if (triangles[3] || TH_TryTriangle(verts2)) - { - triangles[0] = tri - thworld.triangles; - triangles[1] = tri2 - thworld.triangles; - if (!triangles[2]) triangles[2] = TH_CreateTriangle(verts1); - if (!triangles[3]) triangles[3] = TH_CreateTriangle(verts2); - return true; - } //end if - } //end if - } //end if - } //end if - } //end for - return false; -} //end of the function TH_FindTetrahedron -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_FindTetrahedron2(th_triangle_t *tri, int *triangles) -{ - int i, edgenum, v1, verts[3], triverts[3]; - float d; - th_plane_t *plane; - - //get the verts of this triangle - for (i = 0; i < 3; i++) - { - edgenum = tri->edges[i]; - if (edgenum < 0) verts[i] = thworld.edges[abs(edgenum)].v[1]; - else verts[i] = thworld.edges[edgenum].v[0]; - } //end for - // - plane = &thworld.planes[tri->planenum]; - for (v1 = 0; v1 < thworld.numvertexes; v1++) - { - //if the vertex is only used by triangles with tetrahedrons at both sides - if (!thworld.vertexes[v1].usercount) continue; - //check if the vertex is not coplanar with the triangle - d = DotProduct(thworld.vertexes[v1].v, plane->normal) - plane->dist; - if (fabs(d) < 1) continue; - //check if we can create edges from the triangle towards this new vertex - for (i = 0; i < 3; i++) - { - if (v1 == verts[i]) break; - if (!TH_TryEdge(v1, verts[i])) break; - } //end for - if (i < 3) continue; - //check if the triangles are valid - for (i = 0; i < 3; i++) - { - triverts[0] = v1; - triverts[1] = verts[i]; - triverts[2] = verts[(i+1)%3]; - //if the triangle already exists then it is valid - triangles[i] = TH_FindTriangle(triverts); - if (!triangles[i]) - { - if (!TH_TryTriangle(triverts)) break; - } //end if - } //end for - if (i < 3) continue; - //create the tetrahedron triangles using the new vertex - for (i = 0; i < 3; i++) - { - if (!triangles[i]) - { - triverts[0] = v1; - triverts[1] = verts[i]; - triverts[2] = verts[(i+1)%3]; - triangles[i] = TH_CreateTriangle(triverts); - } //end if - } //end for - //add the existing triangle - triangles[3] = tri - thworld.triangles; - // - return true; - } //end for - return false; -} //end of the function TH_FindTetrahedron2 -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_TetrahedralDecomposition(th_triangle_t *triangles) -{ - int i, thtriangles[4], numtriangles; - th_triangle_t *donetriangles, *tri; - - donetriangles = NULL; - - /* - numtriangles = 0; - qprintf("%6d triangles", numtriangles); - for (tri = triangles; tri; tri = triangles) - { - qprintf("\r%6d", numtriangles++); - if (!TH_FindTetrahedron1(tri, thtriangles)) - { -// if (!TH_FindTetrahedron2(tri, thtriangles)) - { -// Error("triangle without tetrahedron"); - TH_RemoveTriangleFromList(&triangles, tri); - continue; - } //end if - } //end if - //create a tetrahedron from the triangles - TH_CreateTetrahedron(thtriangles); - // - for (i = 0; i < 4; i++) - { - if (thworld.triangles[abs(thtriangles[i])].front && - thworld.triangles[abs(thtriangles[i])].back) - { - TH_RemoveTriangleFromList(&triangles, &thworld.triangles[abs(thtriangles[i])]); - TH_AddTriangleToList(&donetriangles, &thworld.triangles[abs(thtriangles[i])]); - TH_FreeTriangleEdges(&thworld.triangles[abs(thtriangles[i])]); - } //end if - else - { - TH_AddTriangleToList(&triangles, &thworld.triangles[abs(thtriangles[i])]); - } //end else - } //end for - } //end for*/ - qprintf("%6d tetrahedrons", thworld.numtetrahedrons); - do - { - do - { - numtriangles = 0; - for (i = 1; i < thworld.numtriangles; i++) - { - tri = &thworld.triangles[i]; - if (tri->front && tri->back) continue; - //qprintf("\r%6d", numtriangles++); - if (!TH_FindTetrahedron1(tri, thtriangles)) - { -// if (!TH_FindTetrahedron2(tri, thtriangles)) - { - continue; - } //end if - } //end if - numtriangles++; - //create a tetrahedron from the triangles - TH_CreateTetrahedron(thtriangles); - qprintf("\r%6d", thworld.numtetrahedrons); - } //end for - } while(numtriangles); - for (i = 1; i < thworld.numtriangles; i++) - { - tri = &thworld.triangles[i]; - if (tri->front && tri->back) continue; - //qprintf("\r%6d", numtriangles++); -// if (!TH_FindTetrahedron1(tri, thtriangles)) - { - if (!TH_FindTetrahedron2(tri, thtriangles)) - { - continue; - } //end if - } //end if - numtriangles++; - //create a tetrahedron from the triangles - TH_CreateTetrahedron(thtriangles); - qprintf("\r%6d", thworld.numtetrahedrons); - } //end for - } while(numtriangles); - // - numtriangles = 0; - for (i = 1; i < thworld.numtriangles; i++) - { - tri = &thworld.triangles[i]; - if (!tri->front && !tri->back) numtriangles++; - } //end for - Log_Print("\r%6d triangles with front only\n", numtriangles); - Log_Print("\r%6d tetrahedrons\n", thworld.numtetrahedrons-1); -} //end of the function TH_TetrahedralDecomposition -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_AASFaceVertex(aas_face_t *face, int index, vec3_t vertex) -{ - int edgenum, side; - - edgenum = aasworld.edgeindex[face->firstedge + index]; - side = edgenum < 0; - VectorCopy(aasworld.vertexes[aasworld.edges[abs(edgenum)].v[side]], vertex); -} //end of the function TH_AASFaceVertex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TH_Colinear(float *v0, float *v1, float *v2) -{ - vec3_t t1, t2, vcross; - float d; - - VectorSubtract(v1, v0, t1); - VectorSubtract(v2, v0, t2); - CrossProduct (t1, t2, vcross); - d = VectorLength( vcross ); - - // if cross product is zero point is colinear - if (d < 10) - { - return true; - } //end if - return false; -} //end of the function TH_Colinear -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_FaceCenter(aas_face_t *face, vec3_t center) -{ - int i, edgenum, side; - aas_edge_t *edge; - - VectorClear(center); - for (i = 0; i < face->numedges; i++) - { - edgenum = abs(aasworld.edgeindex[face->firstedge + i]); - side = edgenum < 0; - edge = &aasworld.edges[abs(edgenum)]; - VectorAdd(aasworld.vertexes[edge->v[side]], center, center); - } //end for - VectorScale(center, 1.0 / face->numedges, center); -} //end of the function TH_FaceCenter -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -th_triangle_t *TH_CreateAASFaceTriangles(aas_face_t *face) -{ - int i, first, verts[3], trinum; - vec3_t p0, p1, p2, p3, p4, center; - th_triangle_t *tri, *triangles; - - triangles = NULL; - //find three points that are not colinear - for (i = 0; i < face->numedges; i++) - { - TH_AASFaceVertex(face, (face->numedges + i-2)%face->numedges, p0); - TH_AASFaceVertex(face, (face->numedges + i-1)%face->numedges, p1); - TH_AASFaceVertex(face, (i )%face->numedges, p2); - if (TH_Colinear(p2, p0, p1)) continue; - TH_AASFaceVertex(face, (i+1)%face->numedges, p3); - TH_AASFaceVertex(face, (i+2)%face->numedges, p4); - if (TH_Colinear(p2, p3, p4)) continue; - break; - } //end for - //if there are three points that are not colinear - if (i < face->numedges) - { - //normal triangulation - first = i; //left and right most point of three non-colinear points - TH_AASFaceVertex(face, first, p0); - verts[0] = TH_FindOrCreateVertex(p0); - for (i = 1; i < face->numedges-1; i++) - { - TH_AASFaceVertex(face, (first+i )%face->numedges, p1); - TH_AASFaceVertex(face, (first+i+1)%face->numedges, p2); - verts[1] = TH_FindOrCreateVertex(p1); - verts[2] = TH_FindOrCreateVertex(p2); - trinum = TH_CreateTriangle(verts); - tri = &thworld.triangles[trinum]; - tri->front = -1; - TH_AddTriangleToList(&triangles, tri); - } //end for - } //end if - else - { - //fan triangulation - TH_FaceCenter(face, center); - // - verts[0] = TH_FindOrCreateVertex(center); - for (i = 0; i < face->numedges; i++) - { - TH_AASFaceVertex(face, (i )%face->numedges, p1); - TH_AASFaceVertex(face, (i+1)%face->numedges, p2); - if (TH_Colinear(center, p1, p2)) continue; - verts[1] = TH_FindOrCreateVertex(p1); - verts[2] = TH_FindOrCreateVertex(p2); - trinum = TH_CreateTriangle(verts); - tri = &thworld.triangles[trinum]; - tri->front = -1; - TH_AddTriangleToList(&triangles, tri); - } //end for - } //end else - return triangles; -} //end of the function TH_CreateAASFaceTriangles -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -th_triangle_t *TH_AASToTriangleMesh(void) -{ - int i, j, facenum, otherareanum; - aas_face_t *face; - th_triangle_t *tri, *nexttri, *triangles; - - triangles = NULL; - for (i = 1; i < aasworld.numareas; i++) - { - //if (!(aasworld.areasettings[i].presencetype & PRESENCE_NORMAL)) continue; - for (j = 0; j < aasworld.areas[i].numfaces; j++) - { - facenum = abs(aasworld.faceindex[aasworld.areas[i].firstface + j]); - face = &aasworld.faces[facenum]; - //only convert solid faces into triangles - if (!(face->faceflags & FACE_SOLID)) - { - /* - if (face->frontarea == i) otherareanum = face->backarea; - else otherareanum = face->frontarea; - if (aasworld.areasettings[otherareanum].presencetype & PRESENCE_NORMAL) continue; - */ - continue; - } //end if - // - tri = TH_CreateAASFaceTriangles(face); - for (; tri; tri = nexttri) - { - nexttri = tri->next; - TH_AddTriangleToList(&triangles, tri); - } //end for - } //end if - } //end for - return triangles; -} //end of the function TH_AASToTriangleMesh -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void TH_AASToTetrahedrons(char *filename) -{ - th_triangle_t *triangles, *tri, *lasttri; - int cnt; - - if (!AAS_LoadAASFile(filename, 0, 0)) - Error("couldn't load %s\n", filename); - - // - TH_InitMaxTH(); - //create a triangle mesh from the solid faces in the AAS file - triangles = TH_AASToTriangleMesh(); - // - cnt = 0; - lasttri = NULL; - for (tri = triangles; tri; tri = tri->next) - { - cnt++; - if (tri->prev != lasttri) Log_Print("BAH\n"); - lasttri = tri; - } //end for - Log_Print("%6d triangles\n", cnt); - //create a tetrahedral decomposition of the world bounded by triangles - TH_TetrahedralDecomposition(triangles); - // - TH_FreeMaxTH(); -} //end of the function TH_AASToTetrahedrons diff --git a/tools/quake3/bspc/tetrahedron.h b/tools/quake3/bspc/tetrahedron.h deleted file mode 100644 index 2c6293d5..00000000 --- a/tools/quake3/bspc/tetrahedron.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -void TH_AASToTetrahedrons(char *filename); - diff --git a/tools/quake3/bspc/textures.c b/tools/quake3/bspc/textures.c deleted file mode 100644 index 76cc0fd1..00000000 --- a/tools/quake3/bspc/textures.c +++ /dev/null @@ -1,228 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "l_bsp_q2.h" - -int nummiptex; -textureref_t textureref[MAX_MAP_TEXTURES]; - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int FindMiptex (char *name) -{ - int i; - char path[1024]; - miptex_t *mt; - - for (i = 0; i < nummiptex; i++) - { - if (!strcmp (name, textureref[i].name)) - { - return i; - } //end if - } //end for - if (nummiptex == MAX_MAP_TEXTURES) - Error ("MAX_MAP_TEXTURES"); - strcpy (textureref[i].name, name); - - // load the miptex to get the flags and values - sprintf (path, "%stextures/%s.wal", gamedir, name); - if (TryLoadFile (path, (void **)&mt) != -1) - { - textureref[i].value = LittleLong (mt->value); - textureref[i].flags = LittleLong (mt->flags); - textureref[i].contents = LittleLong (mt->contents); - strcpy (textureref[i].animname, mt->animname); - FreeMemory(mt); - } //end if - nummiptex++; - - if (textureref[i].animname[0]) - FindMiptex (textureref[i].animname); - - return i; -} //end of the function FindMipTex -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -vec3_t baseaxis[18] = -{ -{0,0,1}, {1,0,0}, {0,-1,0}, // floor -{0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling -{1,0,0}, {0,1,0}, {0,0,-1}, // west wall -{-1,0,0}, {0,1,0}, {0,0,-1}, // east wall -{0,1,0}, {1,0,0}, {0,0,-1}, // south wall -{0,-1,0}, {1,0,0}, {0,0,-1} // north wall -}; - -void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv) -{ - int bestaxis; - vec_t dot,best; - int i; - - best = 0; - bestaxis = 0; - - for (i=0 ; i<6 ; i++) - { - dot = DotProduct (pln->normal, baseaxis[i*3]); - if (dot > best) - { - best = dot; - bestaxis = i; - } - } - - VectorCopy (baseaxis[bestaxis*3+1], xv); - VectorCopy (baseaxis[bestaxis*3+2], yv); -} //end of the function TextureAxisFromPlane -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TexinfoForBrushTexture(plane_t *plane, brush_texture_t *bt, vec3_t origin) -{ - vec3_t vecs[2]; - int sv, tv; - vec_t ang, sinv, cosv; - vec_t ns, nt; - texinfo_t tx, *tc; - int i, j, k; - float shift[2]; - brush_texture_t anim; - int mt; - - if (!bt->name[0]) - return 0; - - memset (&tx, 0, sizeof(tx)); - strcpy (tx.texture, bt->name); - - TextureAxisFromPlane(plane, vecs[0], vecs[1]); - - shift[0] = DotProduct (origin, vecs[0]); - shift[1] = DotProduct (origin, vecs[1]); - - if (!bt->scale[0]) - bt->scale[0] = 1; - if (!bt->scale[1]) - bt->scale[1] = 1; - - -// rotate axis - if (bt->rotate == 0) - { sinv = 0 ; cosv = 1; } - else if (bt->rotate == 90) - { sinv = 1 ; cosv = 0; } - else if (bt->rotate == 180) - { sinv = 0 ; cosv = -1; } - else if (bt->rotate == 270) - { sinv = -1 ; cosv = 0; } - else - { - ang = bt->rotate / 180 * Q_PI; - sinv = sin(ang); - cosv = cos(ang); - } - - if (vecs[0][0]) - sv = 0; - else if (vecs[0][1]) - sv = 1; - else - sv = 2; - - if (vecs[1][0]) - tv = 0; - else if (vecs[1][1]) - tv = 1; - else - tv = 2; - - for (i=0 ; i<2 ; i++) - { - ns = cosv * vecs[i][sv] - sinv * vecs[i][tv]; - nt = sinv * vecs[i][sv] + cosv * vecs[i][tv]; - vecs[i][sv] = ns; - vecs[i][tv] = nt; - } - - for (i=0 ; i<2 ; i++) - for (j=0 ; j<3 ; j++) - tx.vecs[i][j] = vecs[i][j] / bt->scale[i]; - - tx.vecs[0][3] = bt->shift[0] + shift[0]; - tx.vecs[1][3] = bt->shift[1] + shift[1]; - tx.flags = bt->flags; - tx.value = bt->value; - - // - // find the texinfo - // - tc = texinfo; - for (i=0 ; iflags != tx.flags) - continue; - if (tc->value != tx.value) - continue; - for (j=0 ; j<2 ; j++) - { - if (strcmp (tc->texture, tx.texture)) - goto skip; - for (k=0 ; k<4 ; k++) - { - if (tc->vecs[j][k] != tx.vecs[j][k]) - goto skip; - } - } - return i; -skip:; - } - *tc = tx; - numtexinfo++; - - // load the next animation - mt = FindMiptex (bt->name); - if (textureref[mt].animname[0]) - { - anim = *bt; - strcpy (anim.name, textureref[mt].animname); - tc->nexttexinfo = TexinfoForBrushTexture (plane, &anim, origin); - } - else - tc->nexttexinfo = -1; - - - return i; -} //end of the function TexinfoForBrushTexture diff --git a/tools/quake3/bspc/tree.c b/tools/quake3/bspc/tree.c deleted file mode 100644 index bc3cb8e7..00000000 --- a/tools/quake3/bspc/tree.c +++ /dev/null @@ -1,283 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" - -extern int c_nodes; -int c_pruned; -int freedtreemem = 0; - -void RemovePortalFromNode (portal_t *portal, node_t *l); - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -node_t *NodeForPoint (node_t *node, vec3_t origin) -{ - plane_t *plane; - vec_t d; - - while (node->planenum != PLANENUM_LEAF) - { - plane = &mapplanes[node->planenum]; - d = DotProduct (origin, plane->normal) - plane->dist; - if (d >= 0) - node = node->children[0]; - else - node = node->children[1]; - } - return node; -} //end of the function NodeForPoint -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Tree_FreePortals_r (node_t *node) -{ - portal_t *p, *nextp; - int s; - - // free children - if (node->planenum != PLANENUM_LEAF) - { - Tree_FreePortals_r(node->children[0]); - Tree_FreePortals_r(node->children[1]); - } - - // free portals - for (p = node->portals; p; p = nextp) - { - s = (p->nodes[1] == node); - nextp = p->next[s]; - - RemovePortalFromNode (p, p->nodes[!s]); -#ifdef ME - if (p->winding) freedtreemem += MemorySize(p->winding); - freedtreemem += MemorySize(p); -#endif //ME - FreePortal(p); - } - node->portals = NULL; -} //end of the function Tree_FreePortals_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Tree_Free_r (node_t *node) -{ -// face_t *f, *nextf; - bspbrush_t *brush, *nextbrush; - - //free children - if (node->planenum != PLANENUM_LEAF) - { - Tree_Free_r (node->children[0]); - Tree_Free_r (node->children[1]); - } //end if - //free bspbrushes -// FreeBrushList (node->brushlist); - for (brush = node->brushlist; brush; brush = nextbrush) - { - nextbrush = brush->next; -#ifdef ME - freedtreemem += MemorySize(brush); -#endif //ME - FreeBrush(brush); - } //end for - node->brushlist = NULL; - - /* - NOTE: only used when creating Q2 bsp - // free faces - for (f = node->faces; f; f = nextf) - { - nextf = f->next; -#ifdef ME - if (f->w) freedtreemem += MemorySize(f->w); - freedtreemem += sizeof(face_t); -#endif //ME - FreeFace(f); - } //end for - */ - - // free the node - if (node->volume) - { -#ifdef ME - freedtreemem += MemorySize(node->volume); -#endif //ME - FreeBrush (node->volume); - } //end if - - if (numthreads == 1) c_nodes--; -#ifdef ME - freedtreemem += MemorySize(node); -#endif //ME - FreeMemory(node); -} //end of the function Tree_Free_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Tree_Free(tree_t *tree) -{ - //if no tree just return - if (!tree) return; - // - freedtreemem = 0; - // - Tree_FreePortals_r(tree->headnode); - Tree_Free_r(tree->headnode); -#ifdef ME - freedtreemem += MemorySize(tree); -#endif //ME - FreeMemory(tree); -#ifdef ME - Log_Print("freed "); - PrintMemorySize(freedtreemem); - Log_Print(" of tree memory\n"); -#endif //ME -} //end of the function Tree_Free -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tree_t *Tree_Alloc(void) -{ - tree_t *tree; - - tree = GetMemory(sizeof(*tree)); - memset (tree, 0, sizeof(*tree)); - ClearBounds (tree->mins, tree->maxs); - - return tree; -} //end of the function Tree_Alloc -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Tree_Print_r (node_t *node, int depth) -{ - int i; - plane_t *plane; - bspbrush_t *bb; - - for (i=0 ; iplanenum == PLANENUM_LEAF) - { - if (!node->brushlist) - printf ("NULL\n"); - else - { - for (bb=node->brushlist ; bb ; bb=bb->next) - printf ("%i ", bb->original->brushnum); - printf ("\n"); - } - return; - } - - plane = &mapplanes[node->planenum]; - printf ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum, - plane->normal[0], plane->normal[1], plane->normal[2], - plane->dist); - Tree_Print_r (node->children[0], depth+1); - Tree_Print_r (node->children[1], depth+1); -} //end of the function Tree_Print_r -//=========================================================================== -// NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Tree_PruneNodes_r (node_t *node) -{ - bspbrush_t *b, *next; - - if (node->planenum == PLANENUM_LEAF) return; - - Tree_PruneNodes_r (node->children[0]); - Tree_PruneNodes_r (node->children[1]); - - if (create_aas) - { - if ((node->children[0]->contents & CONTENTS_LADDER) || - (node->children[1]->contents & CONTENTS_LADDER)) return; - } - - if ((node->children[0]->contents & CONTENTS_SOLID) - && (node->children[1]->contents & CONTENTS_SOLID)) - { - if (node->faces) - Error ("node->faces seperating CONTENTS_SOLID"); - if (node->children[0]->faces || node->children[1]->faces) - Error ("!node->faces with children"); - // FIXME: free stuff - node->planenum = PLANENUM_LEAF; - node->contents = CONTENTS_SOLID; - node->detail_seperator = false; - - if (node->brushlist) - Error ("PruneNodes: node->brushlist"); - // combine brush lists - node->brushlist = node->children[1]->brushlist; - - for (b = node->children[0]->brushlist; b; b = next) - { - next = b->next; - b->next = node->brushlist; - node->brushlist = b; - } //end for - //free the child nodes - FreeMemory(node->children[0]); - FreeMemory(node->children[1]); - //two nodes are cut away - c_pruned += 2; - } //end if -} //end of the function Tree_PruneNodes_r -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void Tree_PruneNodes(node_t *node) -{ - Log_Print("------- Prune Nodes --------\n"); - c_pruned = 0; - Tree_PruneNodes_r(node); - Log_Print("%5i pruned nodes\n", c_pruned); -} //end of the function Tree_PruneNodes diff --git a/tools/quake3/bspc/writebsp.c b/tools/quake3/bspc/writebsp.c deleted file mode 100644 index b6cf4e4a..00000000 --- a/tools/quake3/bspc/writebsp.c +++ /dev/null @@ -1,595 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "qbsp.h" - -int c_nofaces; -int c_facenodes; - - -/* -========================================================= - -ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES - -========================================================= -*/ - -int planeused[MAX_MAP_PLANES]; - -/* -============ -EmitPlanes - -There is no oportunity to discard planes, because all of the original -brushes will be saved in the map. -============ -*/ -void EmitPlanes (void) -{ - int i; - dplane_t *dp; - plane_t *mp; - //ME: this causes a crash?? -// int planetranslate[MAX_MAP_PLANES]; - - mp = mapplanes; - for (i=0 ; inormal, dp->normal); - dp->dist = mp->dist; - dp->type = mp->type; - numplanes++; - if (numplanes >= MAX_MAP_PLANES) - Error("MAX_MAP_PLANES"); - } -} - - -//======================================================== - -void EmitMarkFace (dleaf_t *leaf_p, face_t *f) -{ - int i; - int facenum; - - while (f->merged) - f = f->merged; - - if (f->split[0]) - { - EmitMarkFace (leaf_p, f->split[0]); - EmitMarkFace (leaf_p, f->split[1]); - return; - } - - facenum = f->outputnumber; - if (facenum == -1) - return; // degenerate face - - if (facenum < 0 || facenum >= numfaces) - Error ("Bad leafface"); - for (i=leaf_p->firstleafface ; i= MAX_MAP_LEAFFACES) - Error ("MAX_MAP_LEAFFACES"); - - dleaffaces[numleaffaces] = facenum; - numleaffaces++; - } - -} - - -/* -================== -EmitLeaf -================== -*/ -void EmitLeaf (node_t *node) -{ - dleaf_t *leaf_p; - portal_t *p; - int s; - face_t *f; - bspbrush_t *b; - int i; - int brushnum; - - // emit a leaf - if (numleafs >= MAX_MAP_LEAFS) - Error ("MAX_MAP_LEAFS"); - - leaf_p = &dleafs[numleafs]; - numleafs++; - - leaf_p->contents = node->contents; - leaf_p->cluster = node->cluster; - leaf_p->area = node->area; - - // - // write bounding box info - // - VectorCopy (node->mins, leaf_p->mins); - VectorCopy (node->maxs, leaf_p->maxs); - - // - // write the leafbrushes - // - leaf_p->firstleafbrush = numleafbrushes; - for (b=node->brushlist ; b ; b=b->next) - { - if (numleafbrushes >= MAX_MAP_LEAFBRUSHES) - Error ("MAX_MAP_LEAFBRUSHES"); - - brushnum = b->original - mapbrushes; - for (i=leaf_p->firstleafbrush ; inumleafbrushes = numleafbrushes - leaf_p->firstleafbrush; - - // - // write the leaffaces - // - if (leaf_p->contents & CONTENTS_SOLID) - return; // no leaffaces in solids - - leaf_p->firstleafface = numleaffaces; - - for (p = node->portals ; p ; p = p->next[s]) - { - s = (p->nodes[1] == node); - f = p->face[s]; - if (!f) - continue; // not a visible portal - - EmitMarkFace (leaf_p, f); - } - - leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface; -} - - -/* -================== -EmitFace -================== -*/ -void EmitFace (face_t *f) -{ - dface_t *df; - int i; - int e; - - f->outputnumber = -1; - - if (f->numpoints < 3) - { - return; // degenerated - } - if (f->merged || f->split[0] || f->split[1]) - { - return; // not a final face - } - - // save output number so leaffaces can use - f->outputnumber = numfaces; - - if (numfaces >= MAX_MAP_FACES) - Error ("numfaces == MAX_MAP_FACES"); - df = &dfaces[numfaces]; - numfaces++; - - // planenum is used by qlight, but not quake - df->planenum = f->planenum & (~1); - df->side = f->planenum & 1; - - df->firstedge = numsurfedges; - df->numedges = f->numpoints; - df->texinfo = f->texinfo; - for (i=0 ; inumpoints ; i++) - { -// e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f); - e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f); - if (numsurfedges >= MAX_MAP_SURFEDGES) - Error ("numsurfedges == MAX_MAP_SURFEDGES"); - dsurfedges[numsurfedges] = e; - numsurfedges++; - } -} - -/* -============ -EmitDrawingNode_r -============ -*/ -int EmitDrawNode_r (node_t *node) -{ - dnode_t *n; - face_t *f; - int i; - - if (node->planenum == PLANENUM_LEAF) - { - EmitLeaf (node); - return -numleafs; - } - - // emit a node - if (numnodes == MAX_MAP_NODES) - Error ("MAX_MAP_NODES"); - n = &dnodes[numnodes]; - numnodes++; - - VectorCopy (node->mins, n->mins); - VectorCopy (node->maxs, n->maxs); - - planeused[node->planenum]++; - planeused[node->planenum^1]++; - - if (node->planenum & 1) - Error ("WriteDrawNodes_r: odd planenum"); - n->planenum = node->planenum; - n->firstface = numfaces; - - if (!node->faces) - c_nofaces++; - else - c_facenodes++; - - for (f=node->faces ; f ; f=f->next) - EmitFace (f); - - n->numfaces = numfaces - n->firstface; - - - // - // recursively output the other nodes - // - for (i=0 ; i<2 ; i++) - { - if (node->children[i]->planenum == PLANENUM_LEAF) - { - n->children[i] = -(numleafs + 1); - EmitLeaf (node->children[i]); - } - else - { - n->children[i] = numnodes; - EmitDrawNode_r (node->children[i]); - } - } - - return n - dnodes; -} - -//========================================================= - - -/* -============ -WriteBSP -============ -*/ -void WriteBSP (node_t *headnode) -{ - int oldfaces; - - c_nofaces = 0; - c_facenodes = 0; - - qprintf ("--- WriteBSP ---\n"); - - oldfaces = numfaces; - dmodels[nummodels].headnode = EmitDrawNode_r (headnode); - EmitAreaPortals (headnode); - - qprintf ("%5i nodes with faces\n", c_facenodes); - qprintf ("%5i nodes without faces\n", c_nofaces); - qprintf ("%5i faces\n", numfaces-oldfaces); -} - -//=========================================================== - -/* -============ -SetModelNumbers -============ -*/ -void SetModelNumbers (void) -{ - int i; - int models; - char value[10]; - - models = 1; - for (i=1 ; icontents = b->contents; - db->firstside = numbrushsides; - db->numsides = b->numsides; - for (j=0 ; jnumsides ; j++) - { - if (numbrushsides == MAX_MAP_BRUSHSIDES) - Error ("MAX_MAP_BRUSHSIDES"); - cp = &dbrushsides[numbrushsides]; - numbrushsides++; - cp->planenum = b->original_sides[j].planenum; - cp->texinfo = b->original_sides[j].texinfo; - } - -#ifdef ME - //for collision detection, bounding boxes are axial :) - //brushes are convex so just add dot or line touching planes on the sides of - //the brush parallell to the axis planes -#endif - // add any axis planes not contained in the brush to bevel off corners - for (x=0 ; x<3 ; x++) - for (s=-1 ; s<=1 ; s+=2) - { - // add the plane - VectorCopy (vec3_origin, normal); - normal[x] = s; - if (s == -1) - dist = -b->mins[x]; - else - dist = b->maxs[x]; - planenum = FindFloatPlane (normal, dist); - for (i=0 ; inumsides ; i++) - if (b->original_sides[i].planenum == planenum) - break; - if (i == b->numsides) - { - if (numbrushsides >= MAX_MAP_BRUSHSIDES) - Error ("MAX_MAP_BRUSHSIDES"); - - dbrushsides[numbrushsides].planenum = planenum; - dbrushsides[numbrushsides].texinfo = - dbrushsides[numbrushsides-1].texinfo; - numbrushsides++; - db->numsides++; - } - } - - } - -} - -//=========================================================== - -/* -================== -BeginBSPFile -================== -*/ -void BeginBSPFile (void) -{ - // these values may actually be initialized - // if the file existed when loaded, so clear them explicitly - nummodels = 0; - numfaces = 0; - numnodes = 0; - numbrushsides = 0; - numvertexes = 0; - numleaffaces = 0; - numleafbrushes = 0; - numsurfedges = 0; - - // edge 0 is not used, because 0 can't be negated - numedges = 1; - - // leave vertex 0 as an error - numvertexes = 1; - - // leave leaf 0 as an error - numleafs = 1; - dleafs[0].contents = CONTENTS_SOLID; -} - - -/* -============ -EndBSPFile -============ -*/ -void EndBSPFile (void) -{ -#if 0 - char path[1024]; - int len; - byte *buf; -#endif - - - EmitBrushes (); - EmitPlanes (); - Q2_UnparseEntities (); - - // load the pop -#if 0 - sprintf (path, "%s/pics/pop.lmp", gamedir); - len = LoadFile (path, &buf); - memcpy (dpop, buf, sizeof(dpop)); - FreeMemory(buf); -#endif -} - - -/* -================== -BeginModel -================== -*/ -int firstmodleaf; -extern int firstmodeledge; -extern int firstmodelface; -void BeginModel (void) -{ - dmodel_t *mod; - int start, end; - mapbrush_t *b; - int j; - entity_t *e; - vec3_t mins, maxs; - - if (nummodels == MAX_MAP_MODELS) - Error ("MAX_MAP_MODELS"); - mod = &dmodels[nummodels]; - - mod->firstface = numfaces; - - firstmodleaf = numleafs; - firstmodeledge = numedges; - firstmodelface = numfaces; - - // - // bound the brushes - // - e = &entities[entity_num]; - - start = e->firstbrush; - end = start + e->numbrushes; - ClearBounds (mins, maxs); - - for (j=start ; jnumsides) - continue; // not a real brush (origin brush) - AddPointToBounds (b->mins, mins, maxs); - AddPointToBounds (b->maxs, mins, maxs); - } - - VectorCopy (mins, mod->mins); - VectorCopy (maxs, mod->maxs); -} - - -/* -================== -EndModel -================== -*/ -void EndModel (void) -{ - dmodel_t *mod; - - mod = &dmodels[nummodels]; - - mod->numfaces = numfaces - mod->firstface; - - nummodels++; -} - diff --git a/tools/quake3/q3data/q3data.vcproj.out-of-date b/tools/quake3/q3data/q3data.vcproj similarity index 100% rename from tools/quake3/q3data/q3data.vcproj.out-of-date rename to tools/quake3/q3data/q3data.vcproj From 4b1c195962c4ce1666300a1f460fb5c0df5bf2ca Mon Sep 17 00:00:00 2001 From: Timothee 'TTimo' Besset Date: Sun, 7 Oct 2012 13:37:16 -0500 Subject: [PATCH 2/9] add q3data to the build. messed up prev commit --- SConscript.module | 32 -------------------------------- config.py | 22 ++++++++++++++++++++-- utils.py | 2 +- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/SConscript.module b/SConscript.module index 3ef56c8c..c37253f2 100644 --- a/SConscript.module +++ b/SConscript.module @@ -11,38 +11,6 @@ Import( [ 'utils', 'config', 'settings', 'project', 'shlib_objects' ] ) libname = os.path.splitext( libname )[0] env = Environment() -# useJPEG = False -# useGtk = False -# useZ = False -# usePNG = False -# if ( libname == 'image' ): -# useJPEG = True -# if ( libname == 'surface' ): -# useGtk = True -# if ( libname == 'surface_ufoai' ): -# useGtk = True -# if ( libname == 'surface_quake2' ): -# useGtk = True -# if ( libname == 'surface_heretic2' ): -# useGtk = True -# if ( libname == 'bkgrnd2d' ): -# useGtk = True -# if ( libname == 'gtkgensurf' ): -# useGtk = True -# if ( libname == 'bobtoolz' ): -# useGtk = True -# if ( libname == 'camera' ): -# useGtk = True -# if ( libname == 'prtview' ): -# useGtk = True -# if ( libname == 'spritemodel' ): -# useGtk = True -# if ( libname == 'model' ): -# useGtk = True -# if ( libname == 'textool' ): -# useGtk = True -# if ( libname == 'imagepng' ): -# usePNG = True useJPEG = True useGtk = True diff --git a/config.py b/config.py index 2cb26fa2..085d881e 100644 --- a/config.py +++ b/config.py @@ -13,7 +13,7 @@ import utils, urllib2, zipfile, shutil, pprint, subprocess, re class Config: # not used atm, but useful to keep a list in mind # may use them eventually for the 'all' and other aliases expansions? - target_choices = utils.Enum( 'radiant', 'q3map2', 'setup' ) + target_choices = utils.Enum( 'radiant', 'q3map2', 'q3data', 'setup' ) config_choices = utils.Enum( 'debug', 'release' ) # aliases @@ -22,7 +22,7 @@ class Config: def __init__( self ): # initialize defaults - self.target_selected = [ 'radiant', 'q3map2' ] + self.target_selected = [ 'radiant', 'q3map2', 'q3data', 'setup' ] self.config_selected = [ 'release' ] # those are global to each config self.platform = platform.system() @@ -175,6 +175,22 @@ class Config: q3map2 = SConscript( os.path.join( build_dir, sconscript_name ) ) Default( InstallAs( os.path.join( self.install_directory, compiler_name ), q3map2 ) ) + def emit_q3data( self ): + settings = self + for config_name in self.config_selected: + config = {} + config['name'] = config_name + config['shared'] = False + Export( 'utils', 'settings', 'config' ) + build_dir = os.path.join( 'build', config_name, 'q3data' ) + VariantDir( build_dir, '.', duplicate = 0 ) + lib_objects = [] + for project in [ 'libs/mathlib/mathlib.vcproj', 'libs/l_net/l_net.vcproj', 'libs/ddslib/ddslib.vcproj' ]: + Export( 'project' ) + lib_objects += SConscript( os.path.join( build_dir, 'SConscript.lib' ) ) + Export( 'lib_objects' ) + q3data = SConscript( os.path.join( build_dir, 'SConscript.q3data' ) ) + Default( InstallAs( os.path.join( self.install_directory, 'q3data' ), q3data ) ) def emit( self ): if 'radiant' in self.target_selected: @@ -182,6 +198,8 @@ class Config: if 'q3map2' in self.target_selected: self.emit_q3map2( urt = False ) self.emit_q3map2( urt = True ) + if 'q3data' in self.target_selected: + self.emit_q3data() if 'setup' in self.target_selected: self.Setup() diff --git a/utils.py b/utils.py index 05a37006..ca70ceb9 100644 --- a/utils.py +++ b/utils.py @@ -75,7 +75,7 @@ def CheckUnresolved( source, target, env ): return 1 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/413486 - +# OH_GOD, I used to think code like that was cool and useful def Enum(*names): ##assert names, "Empty enums are not supported" # <- Don't like empty enums? Uncomment! From 05e48b2d4e4afb396c9896e1363e7e1792d5e0d6 Mon Sep 17 00:00:00 2001 From: Timothee 'TTimo' Besset Date: Sun, 7 Oct 2012 14:22:42 -0500 Subject: [PATCH 3/9] add q3data to the windows project --- radiant.sln | 17 +- tools/quake3/q3data/q3data.vcproj | 306 +++++++++++++++++++++++------- 2 files changed, 250 insertions(+), 73 deletions(-) diff --git a/radiant.sln b/radiant.sln index 0835d99e..be2acbb2 100644 --- a/radiant.sln +++ b/radiant.sln @@ -1,6 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 +# Visual C++ Express 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "radiant", "radiant\radiant.vcproj", "{65D02375-63EE-4A8A-9F8E-504B1D5A1D02}" ProjectSection(ProjectDependencies) = postProject {B957BA35-F807-4C84-85A2-C1F9AC56713B} = {B957BA35-F807-4C84-85A2-C1F9AC56713B} @@ -188,7 +188,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vfsqlpk3", "plugins\vfsqlpk EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "q3map2_urt", "tools\urt\tools\quake3\q3map2\q3map2_urt.vcproj", "{7AF7537E-94C3-4680-8F5E-C1CE30DC2041}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bspc", "tools\quake3\bspc\bspc.vcproj", "{4E4EBC16-F345-4667-84E1-86633BAFDAE6}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "q3data", "tools\quake3\q3data\q3data.vcproj", "{8DFCF3C0-5EC6-4E28-9D38-23FBE5F214FB}" + ProjectSection(ProjectDependencies) = postProject + {B957BA35-F807-4C84-85A2-C1F9AC56713B} = {B957BA35-F807-4C84-85A2-C1F9AC56713B} + {B99A58B4-E5AB-42F6-A28F-D7ACB80E0B06} = {B99A58B4-E5AB-42F6-A28F-D7ACB80E0B06} + {14734EBB-B167-48D9-9B93-2277F645925F} = {14734EBB-B167-48D9-9B93-2277F645925F} + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -356,10 +361,10 @@ Global {7AF7537E-94C3-4680-8F5E-C1CE30DC2041}.Debug|Win32.Build.0 = Debug|Win32 {7AF7537E-94C3-4680-8F5E-C1CE30DC2041}.Release|Win32.ActiveCfg = Release|Win32 {7AF7537E-94C3-4680-8F5E-C1CE30DC2041}.Release|Win32.Build.0 = Release|Win32 - {4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Debug|Win32.ActiveCfg = Debug|Win32 - {4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Debug|Win32.Build.0 = Debug|Win32 - {4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Release|Win32.ActiveCfg = Release|Win32 - {4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Release|Win32.Build.0 = Release|Win32 + {8DFCF3C0-5EC6-4E28-9D38-23FBE5F214FB}.Debug|Win32.ActiveCfg = Debug|Win32 + {8DFCF3C0-5EC6-4E28-9D38-23FBE5F214FB}.Debug|Win32.Build.0 = Debug|Win32 + {8DFCF3C0-5EC6-4E28-9D38-23FBE5F214FB}.Release|Win32.ActiveCfg = Release|Win32 + {8DFCF3C0-5EC6-4E28-9D38-23FBE5F214FB}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tools/quake3/q3data/q3data.vcproj b/tools/quake3/q3data/q3data.vcproj index 80aaa9bb..f09923a2 100644 --- a/tools/quake3/q3data/q3data.vcproj +++ b/tools/quake3/q3data/q3data.vcproj @@ -1,107 +1,279 @@ - + + - + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - \ No newline at end of file + From db442af1346400a8ba09eb835d01b9cdfe3c390f Mon Sep 17 00:00:00 2001 From: freemancw Date: Mon, 17 Dec 2012 02:35:45 -0500 Subject: [PATCH 4/9] made prefs restart radiant popup centered with fancy icon --- radiant/mainframe.cpp | 76 +++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/radiant/mainframe.cpp b/radiant/mainframe.cpp index c4e46151..21d208b2 100644 --- a/radiant/mainframe.cpp +++ b/radiant/mainframe.cpp @@ -4559,45 +4559,51 @@ void MainFrame::OnEditSaveprefab(){ } } -void MainFrame::OnPrefs(){ - int nView = g_PrefsDlg.m_nView; - bool bToolbar = g_PrefsDlg.m_bWideToolbar; - bool bPluginToolbar = g_PrefsDlg.m_bPluginToolbar; - int nShader = g_PrefsDlg.m_nShader; - int nTextureQuality = g_PrefsDlg.m_nTextureQuality; -// int nLightRadiuses = g_PrefsDlg.m_nLightRadiuses; - g_PrefsDlg.LoadPrefs(); +void MainFrame::OnPrefs() { + int nView = g_PrefsDlg.m_nView; + int nShader = g_PrefsDlg.m_nShader; + int nTextureQuality = g_PrefsDlg.m_nTextureQuality; + bool bToolbar = g_PrefsDlg.m_bWideToolbar; + bool bPluginToolbar = g_PrefsDlg.m_bPluginToolbar; + bool bDetachableMenus = g_PrefsDlg.m_bDetachableMenus; + bool bFloatingZ = g_PrefsDlg.m_bFloatingZ; - if ( g_PrefsDlg.DoModal() == IDOK ) { - if ( ( g_PrefsDlg.m_nLatchedView != nView ) || - ( g_PrefsDlg.m_bLatchedDetachableMenus != g_PrefsDlg.m_bDetachableMenus ) || - ( g_PrefsDlg.m_bLatchedWideToolbar != bToolbar ) || - ( g_PrefsDlg.m_bLatchedPatchToolbar != bToolbar ) || - ( g_PrefsDlg.m_bLatchedPluginToolbar != bPluginToolbar ) || - ( g_PrefsDlg.m_nLatchedShader != nShader ) || - ( g_PrefsDlg.m_nLatchedTextureQuality != nTextureQuality ) - || ( g_PrefsDlg.m_bLatchedFloatingZ != g_PrefsDlg.m_bFloatingZ ) - ) { - gtk_MessageBox( m_pWidget, _( "You must restart Radiant for the changes to take effect." ) ); - } + g_PrefsDlg.LoadPrefs(); - // if the view mode was switched to floating, set the Z window on by default - // this was originally intended as a bug fix, but the fix is elsewhere .. anyway making sure we force Z on each time is good - // (and we simply hope there will be a SavePrefs before we die) - if ( ( g_PrefsDlg.m_nView != nView ) && ( (EViewStyle)g_PrefsDlg.m_nView == (EViewStyle)eFloating ) ) { - g_PrefsDlg.m_bZVis = true; - } + if(g_PrefsDlg.DoModal() == IDOK) { + if((g_PrefsDlg.m_nLatchedView != nView ) || + (g_PrefsDlg.m_bLatchedDetachableMenus != bDetachableMenus ) || + (g_PrefsDlg.m_bLatchedWideToolbar != bToolbar ) || + (g_PrefsDlg.m_bLatchedPatchToolbar != bToolbar ) || + (g_PrefsDlg.m_bLatchedPluginToolbar != bPluginToolbar ) || + (g_PrefsDlg.m_nLatchedShader != nShader ) || + (g_PrefsDlg.m_nLatchedTextureQuality != nTextureQuality ) || + (g_PrefsDlg.m_bLatchedFloatingZ != bFloatingZ)) { + gtk_MessageBoxNew(m_pWidget, "You must restart Radiant for the " + "changes to take effect.", "Restart Radiant", + MB_OK | MB_ICONINFORMATION); + } - if ( m_pTexWnd ) { - m_pTexWnd->UpdatePrefs(); - } + // if the view mode was switched to floating, set the Z window on by + // default. this was originally intended as a bug fix, but the fix is + // elsewhere .. anyway making sure we force Z on each time is good + // (and we simply hope there will be a SavePrefs before we die) + if((g_PrefsDlg.m_nView != nView) && + ((EViewStyle)g_PrefsDlg.m_nView == (EViewStyle)eFloating)) { + g_PrefsDlg.m_bZVis = true; + } - GtkWidget *item = GTK_WIDGET( g_object_get_data( G_OBJECT( m_pWidget ), "menu_snaptogrid" ) ); - g_bIgnoreCommands++; - gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( item ), - ( g_PrefsDlg.m_bSnap ) ? TRUE : FALSE ); - g_bIgnoreCommands--; - } + if(m_pTexWnd) { + m_pTexWnd->UpdatePrefs(); + } + + GtkWidget *item = GTK_WIDGET(g_object_get_data(G_OBJECT(m_pWidget), + "menu_snaptogrid")); + g_bIgnoreCommands++; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), + (g_PrefsDlg.m_bSnap) ? TRUE : FALSE); + g_bIgnoreCommands--; + } } void MainFrame::OnTogglecamera(){ From 7cf0bccc1944de8537b1be57e6b9342e6c745943 Mon Sep 17 00:00:00 2001 From: freemancw Date: Mon, 17 Dec 2012 05:16:52 -0500 Subject: [PATCH 5/9] changed title of save dialog to make more sense --- radiant/qe3.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radiant/qe3.cpp b/radiant/qe3.cpp index 6fd43020..28fd7ec8 100644 --- a/radiant/qe3.cpp +++ b/radiant/qe3.cpp @@ -1453,8 +1453,7 @@ qboolean ConfirmModified() { int saveChoice = gtk_MessageBoxNew( g_pParentWnd->m_pWidget, "The current map has changed since it was last saved.\n" - "Would you like to save before continuing?", - "Exit Radiant", + "Would you like to save before continuing?", "Radiant", MB_YESNOCANCEL | MB_ICONQUESTION ); switch( saveChoice ) { From 6762f7c9a9bb066437696840b53a94fb68a700f7 Mon Sep 17 00:00:00 2001 From: freemancw Date: Mon, 17 Dec 2012 05:40:43 -0500 Subject: [PATCH 6/9] changed scaling restriction button tooltips to reflect what they're actually doing --- radiant/mainframe.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radiant/mainframe.cpp b/radiant/mainframe.cpp index 21d208b2..f276bbda 100644 --- a/radiant/mainframe.cpp +++ b/radiant/mainframe.cpp @@ -1812,15 +1812,15 @@ void MainFrame::create_main_toolbar( GtkWidget *window, GtkWidget *vbox ){ GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SELECT_MOUSESCALE ) ); g_object_set_data( G_OBJECT( window ), "tb_select_mousescale", w ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Scale X" ), "", new_pixmap( window, "scalelockx.bmp" ), + "", _( "Disable Scaling Along X" ), "", new_pixmap( window, "scalelockx.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SCALELOCKX ) ); g_object_set_data( G_OBJECT( window ), "tb_scalelockx", w ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Scale Y" ), "", new_pixmap( window, "scalelocky.bmp" ), + "", _( "Disable Scaling Along Y" ), "", new_pixmap( window, "scalelocky.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SCALELOCKY ) ); g_object_set_data( G_OBJECT( window ), "tb_scalelocky", w ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Scale Z" ), "", new_pixmap( window, "scalelockz.bmp" ), + "", _( "Disable Scaling Along Z" ), "", new_pixmap( window, "scalelockz.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SCALELOCKZ ) ); g_object_set_data( G_OBJECT( window ), "tb_scalelockz", w ); From 7b38b02742e75c1e1507da89cfb9e973d3dd52a7 Mon Sep 17 00:00:00 2001 From: freemancw Date: Tue, 18 Dec 2012 21:42:33 -0500 Subject: [PATCH 7/9] changed splash screen, swapped from bmp toolbar icons to png --- install/bitmaps/logo.bmp | Bin 47936 -> 0 bytes install/bitmaps/logo.png | Bin 24747 -> 43805 bytes install/bitmaps/splash.bmp | Bin 299934 -> 0 bytes radiant/gtkmisc.cpp | 18 +++++++++ radiant/gtkmisc.h | 1 + radiant/main.cpp | 71 ++++++++++----------------------- radiant/mainframe.cpp | 78 ++++++++++++++++++------------------- 7 files changed, 78 insertions(+), 90 deletions(-) delete mode 100644 install/bitmaps/logo.bmp delete mode 100644 install/bitmaps/splash.bmp diff --git a/install/bitmaps/logo.bmp b/install/bitmaps/logo.bmp deleted file mode 100644 index 759b87cbfa56446b28f29c0f86b298fd10edf435..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47936 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 diff --git a/install/bitmaps/splash.bmp b/install/bitmaps/splash.bmp deleted file mode 100644 index f9e7cc1dcdc0bda5f06e3fa07a5bda781ab8fb9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299934 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 z 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ℑrExG12tvjfEti{pS)=J(H znm@poLZ5HWT4Epg^NRpJZ^6*`naKUBl4Ca-i4Jgq%VgwkaO;$iC_Yc2I7wCj%we0{imcKK8lhz)M_B_ob&% z&Dn<9E4H3?g81~Q|f>x(CjfqDV_qesr4I9_+*f~Hl4S!cId@#u84sY&oN_VxAj^_d(l8&cls zf&jkWV%3{1daDiKLk6oIyep8m^_p#3lU0z>29fZu(OcDeYa7$Dj5bR8^8+8?x=-DAn6DtJkhi- zmW6_eJn%>EhrXh6I5Uta4rc~Ob3^0#;i>%a3|YRV@%h1-)zZ{TvND|-oXL$X3@_ds zNAus<9bMZgEpBCQtVJeP0d;I@ODfHR2>`aMn+aiFEdVs{Ik_)_GK7%Qdxr3#yU>~GQB_H6kXi?mKQsI~4 zSjv6W0O8W-Q|JaQO?8d+P(xEA1J=NPhpG+IsD1?d#0)?X_WjqCd@15Vy<`Fs^;GC9 zin6HAKaZ(YbDGS)mgYu8KaiJdKMP6x5D3&G`i1m2;}YNCIf<^4DyfA2<5ALwSd&4-xX-4# zI$`+p&=tK#O8l=j1q_d3L{X2pzhNYQ5YZwWeHSk3y49VR&!XhZemgBEjx`@Y)_CG* zBN6=LM;T??boyNd_@bFMz(?FG-8GM02K8T&aPLd!9w)D!dPi9LM+NYOlmFN;#&vbJ zwK?qeUR^JlpN-eA>$J1YC zMB;;blQU#;1T9XQ_Q!xcV7C(JcLwdgs5_WM;U@}U55yh8xHFh=hg1GoR!}@2NEQO< z|LGzW$qvT)2U7#X>B>krE6xPdrAU4#QJRRCC&PuY{Mcf4e4#M2T$ov!T)#E8zB90V zb7l)6{vdHjk$6H$Tm2djS zZu=%SODj)GOD|?OpKNXIFoRclaQm)Wp7l5Xags+YOoN)U3~a&@r;SEFnJA=E`FI@o zvw#njvYCPYT#@dPiC8WXk9gg2zo^Mg28AygJ^=Q?JDU?}#a5KkN{S@8V_AntZgJV% zZeM4hh>=T_-%GnSMaIc(%xCfSk+hFY#J>j?@SLlV_e$U%!QH5!9u2>Wed*puxC3)+ zd{D0hw?!@=qF=xt(GTRoJ_Yx{juv%C3&(cOG}kvG2Jx8@ z-2>^6bRbkwkD!;32m5l(5n^uG*_xB*&z-KjdYQs}P>jTfKU@veP7o!09oDMvMqy;jy2aHR#pPR{j=4I7M!b{)}!M6!7`bi%4dEI>FVuk@9t%KPLJNC z)mxyRK2x{e$V;eOXX@%TcJ>-uwe;UJwa|@UZ&T@M;BW6VIeMrAq5{b2F_8n{4Je_* zkjo#kkl<$zzyXXlzuw|CkOpjWV+B~;5eq!Q9&q^+PJoZSA52K>vv38Iu3*X=$$BDL zcO>gW4ak=80QeF)t3N_yK}(q-ZM`iHZ?X_Cjbr#H2PodTF|m4UaAAFfJpZk`Q=4~& z*0u+ix5jSn6_&Ru>-UD%ALVCn53fF&-~C`_vuogc=(yOu;?#s zxhs3V$-VgW?SbXjr5pEm@4w9E`bCXi2lAnm!$i zW@54aM52^V<|iBgyi3S)WHYlH*`HD>0c!Em zhXc^<4W`ATLo5%q8ugX@LoJUxu!od_?Dc-$Zz z)B}HxWD?4RB20{4jfO59(u4Um==1Wc{cvcF_#kp(;O?9|)hycS3mYHQ3*aB$ABrd7 zFIwyh;P0FH!mEFv>K|ag3Vexu;4ig(0e=PfU>{%K*%QZVPMkPLtLEd!1pLpQZE93? z;XO53^%fgPtsa4H>NS{kMypl`cLDP%(ltGL7==PQ)?mL&XCgnKrCYSvZ8KOjCR>-u z4iYH@*5mvqIso=Pemga1B6A>QA@j!`&;xv|ew!cc_ZXa=eRh-qu+K}B0f!$yK-3Ws z@W<6Jm_PuAAmHx|q`$?+M_l8#Q+}L?Oy7s6v#}vQEgR}SYBb&1~AKiZT z$=crg4?p;+k(K9x(x$t->mPmS8``cczZ_nDGkx>roqNxK2zIHA(Nv$TRPTkXKX}A2 z2aT%DW48y~E>dn{!2r^~Kbb71Qj)UKC_*Ic^#(j{9Bw$P*;EJ@QqU3l03W%}CIsV% zBnT8sfevSYcnfG#$Le=DdZNP}#hri`CqIqOg@<46UOvUgN!%qd24_Agy67z;6HjsD z?}J@UquI8$_O|v8b(_Kgq{1(;kK7jxIuziO&jb8X6|nT-7)?a~n;XSP73?1v`$F^I zKcguBovrP>1&<+}NA~0g@-nnn#XhP9YbhWl_6Y>g>WqOx4CR9uDF880h{C6~<|g1Q zML#-#NpS!IZ88@26@GmFR>3Nf`|t|v1y~;t<{Fd)un%#DOsAlz1`>76+%B?!z`oS4 zxM^~M`2e~J6M%hWJ&8jo005szBIn(BP)VyWnY%S4*Yog240E3jxWN8?P+Ir%3%N}A z`k1QISl3f`rA;*1dx!QrOw4H{NT*E9kqJCtUxf07#6Lot%>%DK2EG*fLeCef{(;aJ zDLsdPFR?G>zVP!OJ9_2JnN~`?X>&^NzDt+d)S51>5y%VF_nA7i26d-SrO~!^={o4? zr89Qxj2f+gJXKk39gt3?);70xwWzz>G`*bHXpJ4cW_7Q*!(ax9z4ZLC+57DH{9R_J z*Fvz5s1)1N8+3RD_7VDeJJ|Ok<&E}0ugR_HrA2_J&*Ft4^jQcFl2*i zn;qX7oZ1N&7F~s#p3*(vz}@uZ%O`l-S3`$$K}&re(rhZ5L_1F$nW zO6?R-%a^nbJq*RKkf zKHzO>0{a5;ogHdTds~OPgQFV8MwDKp^hoSWRLVYn68i{!SO*uVnA_5-qU8wCR^+}~ z!n{Kz;LjRcmMFibwWG7OL(|euRWLV)n5VpP9MJ(d0(QBO(w`%3I7K%;nsH+OD>H@Y z(|@k!)VZ^Q1Mmrohj3J4zW{vmLF`X>`!V^IBdR#$v052+PuhYT{Gy-RtW^a7CIBmd zA$W)a*zMdmvI4lhuv3Sz5A3smfgrYg4rhQ1+#)py6^lelt_S#}RiHe`5(AzfiV|N; z{6JaG|4r=kyTwnZ?=H;}&T7>y)aGF3HwoZlHH|IAbF><2|1k#)mTr@^8~wp<*E?K#yT?Sa zj}!8n1QYO^oq;~9*Wd{BT0Fh@W}E>Ge-wVL!P%v=QNDyefC<0|Kd^82$AG^(m~i@I z?r_o@OL=G*9!t4G35pHvA|WvD3a5IAFPQAjn!X0LR;4vq0#v(=qV~s%6Pdx;f$5du z`OTrZ^~u$p8*94)`%}x6#qF7mdu#Vz&Is(kSh@Rp?fysew_cIPKePG#T6Fe2g0bn8{E)`K)?j*TxJ z``Gv_l=L2v;ZqIvaT(u2+V~RqGIKymf6lOg01w#5&kyz`3HYBm3GfB>@c^7TjohTB zuDPk9ucuq07$8GH8>gR3N%I4q5ZG_1uOl4KQ$PXZU3sNEN)I6}0)PynCC}t2^(7Q7 zV`khL$_DHrf0FZ%;I6c}QvM4*4(eqM6eAZ=i`W2v1@@)i56z&tsaD%mD;sNzwz|iT z2-Gt>Lp0ZwQ*o6+*a&^$-}^DDz9RJhD&SY;{yz4P9;w0g_pUJZFW1yurAxq-D@{!; zZJM4=Vm@R9^%;dGuQhPg^cW=efxldEtTF)@0!#oMK-JcxYS*ed``UZx=VPHTP-{Wq z7sLt&Hmdu~CSNp|t;C9B;rwW< zFp2aZncuA7`JY_MO)U1$uB9fIhL(5cZa&z&_j3L2tNHEsrZyfe?!MZ7{Kf3{`?H&` zo__d~AAIpMw;q2Hs@&3rru@Yj(`6(M!qaal2^8<#X9696&Kg(2H0+nRrU~46w`^XoG!3yc&TI z?ltX)6@K2r-pBvHkA0GD0PTUoFXcWy0kGexZtp^0ID~zP^lEw!wLCK3*Qp}z-_h2h zf&-D@4+oHjKMMhNg@8XdRG9e(a{nv%pE(KYMR##D|FdLPBUXh`K!FX7M*y-2bdtS* zrH?D`pst7PKGXxo6}W-6%L(yB5Z0suawVS6Dx*M2z*VAX{vz#18vbFo!yWDKOiuee z#J?4@OPCU70;wGN2v!EzCya>c(WmVp3lT56+y+wf=eLP89!dK?e>%aR(`q|37i$pu zhbMoYs+S+!lwioOh44C=7=m5PYYyrS73HVE@>HsXHN}%q517Iff zwRY-Nbo1*rb@h?wL;gR&_dEP}?*n#H`n_SZGia~{^wxlm+$$HZ{y?|EX>j<#yVV`E zxr0t`m@(NFr=MATHU{{j0C?!v5%Y$VG!^kD(w=C-pU8Q_8Fz@DKPjU8;Y`6F&xX@w z3J-hHGz9hsk^>Wo(qytc9WPCkrGt{78S+Rl}bRDK&qvFXPd?CDH&w{+@xI%ZatN&c3JzXOuhS$J1}= z)U-8<=HIoKsX?JGMs(c~EqCAmU|#|sDG%y_KMNE9;E&LUAqWQ`uDpgiE=CXoQsLo; z6DaAsDEwFr%`MIFD}WE8yVM$vQrydoKAFvn(EkeitxCS1y0h_m9iA>Q4i~EG{Ja!w zRPpldyZZO-eIfdV4xso0x!)|59!Y{lNJ59^%Yr^YjR8)i11S#!cM`%MG*vBO~`Jv0^uJpilj&6zLE(nL_fesp@Ow?pTp^a_K6pc{B+Rk4pKnj5-~yP zej%Cw&jI@a{z|?CN(jM-%Ty{U`vLf)#NZbas=gBAlRuI1H@fcj)>9|&?}7c6Q|~qs z;}czWPe|+&&Qrv_NZun~S4rSGjQxF8zpC(m6Z-=4RZ?_*UZVCxrw`Fd-l{ppbMGVm zV=`(D2Bbg6eygSr>s}CgewU%6#|Xg+Kt04la)as_(JC}w_uW;^)# zZGKYviR{w^*bj4n@a^}SJ*fIDZ~*q2Tw0n18SLGCHmra?Y&C-Zo~RQz$1>h4wOX89@s_7T zgL5SKjj!FVEN-sedHUY#Z{2+G!Tk1f9Q#`jKiPfq#oC=WE4!~(cVF*5`Brx1rZqC{ z%dW)Ao4Jv#Sa~Hjyq%kSoSA#Cvh-x?#`;jHa^<2Jye>Gra+G|4+I*Ng?tP<9XTkF< z#SmWN1LYs9pLieczEm{U(#T}ROJsS3JZ^gRP-TX!my#V^gKoHi)chqAK=exv09%l1 ze;}Gv_ivwV>FQ`}i+bqX-oDt1IMXEe?@Z#dDC!|kMuerHh3F@KnW6MkR9?6bT&$0U;p|^^C z<$`WrvS@1({m>-#r9lCr1*i|OFXaA#CO~{2;6rj+l8g)@N4aKyWDgfBF@kHCFHwbr z^atbkPKEL#SgzmHr$_7(jNm*b82mx8?p?T5fjl`668re@g$M5-6B79I6f#JXY`$yl z=QOu|!&_XA`UBkFs8^Iw0e{i_)+n5)$bB4-xF2yUVqpSVRCVT*=F)`*=H)3~eSv*3GEWrg{EevkQsF;*JaY*9r;Z&t#R$&Y zYdu=69{3w^{h2V~RT|;e6V%?@+S%9ErB`?9CGZbppEZU6EP`vmez)EZ@GZd5O=*B| z@4EwG2=R@bZnTFWM1L2PHx-9JPJX06)ZSx~|4XON+G})yeUmdHa{FB&UnK3PoF!cd zCbRxz9#_B3kHepYpHikY63vu7(Yz~?WlD%GkO<&JuZ$v-RMUg{>DGdmk?Cd@wMx=Sk0p zi|g6Zolt4fo*WD2XZpw2$JU=^W*&|%+#4U6YHx1AQ6-@d`lShgl%dZBHImxSPMYB$ z_i2iS#6b*!dLn(~^N`g;h@WjqF9-sLw7WqQAj)4wG#E$Q2k)}x6g2>(ONq|{dBj`t zXibzZby!J*p~}oIb164dDST?ZuAZNkw9*qM|lnSgw$X>F&b z31tVN599%|1b$VHA70?`6WB+|SNMbch*S?G_E~e^MK3&>T3?_uaJ^Bl^P%1=*bn)jeLx0sSG&6I+Eq4#z&_phMGt2sA}oIES&|{_ zb}OB*ob?PjA zv{Z2iu=J7m68K)SAxPBm$4w42e|m$&@%H2B1OA}CLuXU>n0t&g1Gj2=`LAsTt6xL` zoV5CkQu{;3FByQLWPc=62xW?7_&9^8{rOO$l*m=$`N2@S9L*2ggDFx6$r7Z;7oB^v zgVV){rT(#n><~`BrP%OvW@0fvy*#|Ixq0X5gBNd~y!w3O?n~16@4fui&4(Y}eDE>Y zhn~Fo>Fq~fEZ+JcJ$frz-WZyH5Fc4H$A-M|N;X?64^9C8iS<{N$|QbZMo9zz!*U<- zB^5lRTSM)2HUoAgLMKi;sdxu1tdd(*7mM5$d+>#=pIQK&b~ulcGJCb3Cxmx*Tl%brbeZm=Kd&c zL_fk^YWN&60U(vCv8joY9FesP@DZ}KE5gMmflnVlknbNDu!3_BE9BqWsI3U2PiF3;q)+?gbtci7!=sj@89s1wgV03=l5n0)YS3y9)M^ z{<6Ued=CalUoZSBF~1+|6F$U3=+S7r4jUoS1DK1VCnR{-Eku0WFPa`m7Yaurdhi6I zFok%`zWTQ1J3nmy>Awsvf6|{#C&PZphY8BSE(j%9mGUCW4G{f2?hYCsDNa8a8zk7$ zz5;+TaGuPW-qsFOePE7TPkp`yRbPSpfmiN8i@X7>>RMT>7nDwapzIn+WZ7^|truG}!xEI`*3h@U{1v z+u#PhW`K{LUO@5Kj9-DUr47A(Nj3{y2zMC~kH|sP1cUe6YOp^!eNG zKY8<=z2{$SKl*g<*|#5l@PjR|zx94;`fjYaHaxw(_26ZBaxDz23B}?L+xX1V^20C7 zV=L)!_}WEUp@>3GNbX`@vKeLcU)1=YJBR;A-Q0w2NE#1m{38S9L?q1MT{$qC{n6Ia zTFT{eF_Fp(uJQ1r`Af+zr9Ur$Kb%1-24YQGX&9HDQ^-z{9ZIYGeV@E6v&A)sgAOB| zKzysZ4WuLM5$NDrrsd=9=OmEl0{8>zJ1oLj~1OY?E&)OUGRr?%|0Jcs(Miv)U+sPexGv7 zfOC}tNTDyJxxx^n^3NJZRJ}|}d|pbD3<~`K?DKczM{shGw;c3(99E0K{^^?5#wM55 zOwKdfKK6bLZ-CMU0SS(E56k5xtUw5KiYD+1S~Yw7Uliz_IGngkDF1IB`_lGD^q-`$H{hE1(-F+&pQPrhyQ1@JK?zmj1 zI(MbvdQ*F2TerHWudC14-DmA4g->szov)5YzGj!s>_Xz>t|w`S{5`PG5Kk}u`B2;! zN(Lht{3Z@!b0Ud1PBOo6*M~EKSie7-3sANb%LJlXe}wUW8GtWq`5i%;c%)2TTK&iH z=GpvFPbB4wX1$Rd?mnkK=?bNV(9aDf3Ztp=1gKAxM!3x8HvJ>eJcvdw1UZ~B5%?AD{t z$L8*qCwJ0=YvsY&+dFq2K6pA-s>I0da8O<4SlM|nv+*<*2%`G(SmpEOK_5^x^G9rI zh`F$od^XqBTMWinFoaL-lA@QR?=$z>Xfl;^$#PCKlSj~ldWa2%JOb>})el49BrjQT zr1yT*?-FQ}XeM=7V&JeQ+&xevYg$-T^*_JV%I6 z!7ez@9|8FA71n$ZhQQx!YH6kn1pgjbM%9;!Jx72qkHB9s?K>6hBkP5)PsIA$@Ci`W zD++tXm96c#3(!)ew$D9cA^*E7q9ueS6X0Ebmt1p{Lce+?*oV=HZomqEfO|;I0XCp+ zX(sfB&X3%OojJ^u@K^yXN`8_UD>}eIYa_5MUvkEWH&5vTgcG2UPUlAEx?lXm_Pc-Q zk|~Qtl#WKECI;t^)AriswDk2+DN}ppGTT5Xfxe&BUXz#G4FV|Am3rqjN@E6z@ zboAV*lPIf=s&;_i($&|j(J_hd!u2MAkKAu=*LF}FA!>OXU0~nn0QI7MpUrEqdkNJU z?LLG)9sh(7_eVu|E*STPQ^80&6wLx#7aV{@ouCNSk*ybs=Oc-HJX45Krk5xNV|jn9 z-y7@4ga`OE^S3GR_r?o8^7#|_Ks@h{ax6r%1ECZOKpH21qF;3Kq?||e@ff=iE)J&$ zCrc9x(ekvvf83KB9h%!r57W?ZcH!pU?xRg~&qzV+UlFW2{8%xyjd{u}o{ z*narQ&g0MLHlMBE`CxhL`NaI~^3+DTI5L{dEDTR!O~@a_ zic}!N_7ohT8qiUkJnYK-^B8@SE}y^H+|WeOjG=$x98UKj>BkF0w1zJB)0Q8ZE+sxk zP|t=$B(WjcoKn?O@_DXaxx$_T``Gr(;R3K=1i3HM@IW!}m+j!;G!Pd^{=j9@ zZzgdqvwUS;p2R+XMCkk%&eObtYhWGd3~g<#ojCSeg{oim>VxGf$*?|xl(em7ZUY9a!}1@`j+)XgeVhWRFZG5#+W;$|s=8(agPDsfVt=ZwU)JR{i!N)M9` z=p4e_arr^P7L6|rEDi;_eY4zc|viwKjQL*y(G3E#AuC{=nuqm4kb<37e#ibsnCg}i*ai6sl-W@ zLh%CF_X(sIeX)W+T0q1*;SwR-`zcpA6G|3?iGn*!R)3NBel$}`)5tedW@dM3e6~1t zV{m#gJvc!h@9fx2bYRk)9yT)CuQ*YhST0X5-@Nzq;foI+zxw#u2cJFr@LPADe0cZ0 zw=1`vJbLxz;;rXPJFj*geY&*$a$)nu^wPbR^}Wg2af z$lNo%vKvhgSbI7VwjxvOp#PGxPxlQyurJ{+7GenFdF)&Yfti6P>RPVU*9PY-PkwW7 zaCE#>sN~W#h-MSAA*Id__GM^~BPU@C5ZH&v3GCDLveD#>7w}1A8Ql1n8I;Xn0XTpX zjz?Z&u%mYZE|4N+Jsd!=0)}xi3F3iuWxW*l9Hp7Bz+4D-#C?_IyU2P_5BB$y0f;Izx8h(BC9;ZrA%V{k@JML0kU|{D zvzDxYGm>lZbNCf}7QKgWNK7#0k$ebhh_zkUWW24@c!bOCHNj zX3ZUDgc)u9m{b_>aE%D(A=!z;!jQXT!)LG7;!Nb`;+(OXOvG>-u3aaQ19e2YcO>(# zGJ)!%dY*eo)`zyff%2S|np2eIi_ASGh<~8!EA2Q=9Hs5vB|((v{>QN|MgM;m_K%2& zKXmE{%D~Z+$Bz;Br-dMQQ;cS6YPwS2bfux?T2pInb6Zn;7ixcx(TrbD&`sC>cUjY1j&px5A*WS~Q*Z1C^ zSiHNu^=$Rl%h{DDBhxphCue5IM@Q1}a=@Q;SQB1%d1N|XSxn|e?YeGKa|gy|G`PpE zU4zwMtPvhvNLc!chlg#b>_h>1xkk*9{f=%b9Mkpfjs163kN-(<_q)Tz+(@4C-W2-0 zB)ohGcd;Rroyifr1AKS_7bxa{zoDTXwL*C9uUxs%)Lq+Yxx%cEt3(ZeI&X-C99)0= z-|zvh0vCXVg@S!yBZ$fxMDFE_U?0d!yFf^Mz^KqA(V|jdzp=3q>qbQJl!QI0x^q?}ab6_;waO$wQQ0R9ftI$878L^puzMu%!Kqw;Yu1OcFJE$mB=)(I zg}^>-?XQx%Kto-TiU47!@GjVwi~9<4ccOu%2N z0mpCxyvw9x_Q$2hrt2-OwJORE+i4`;Ox|ygi3vXF{K$R$_H^Sk*o6WB{OvgU!ih*C z6OL!XiBvR|i_(ZAnhhcPMH{aEa3&wk(v+hZryEbM5=oc*z&}~^MGLNAzr~$0I}>I{ z!sbaj0%=z`hu_bJ1JD;o6Y$0IXaYK`ud!WQ*V@^xx7j0^^zdY1Vm3KA5vfds2PZv+ z;ZSMZlo~c>hRpeq*zjz0czX5rLxlc&AAI)mqc872`{==o&+a_=Xl?hsksCK}J^XNd zZg+5UV`k~@>eh>i*)2w5P8M>L`CKUwEO;HLc|+41k;-apaMk6FxVqXagC(7+rHM2( zo~_ciS-SYDU_T6hq%1`CP#3M&tW4$4l&*+aN~j zpzTY?zD(e$9HzRc3&K8sTV!BYu6w2xTax{&{zJ&H3Vjf?t=m_S`p0vf2)>*02 z#qFQ5c++N2Qg06%9R&SJ_f0!O876@e)+e;*MJh7Ha+3BP3(3J5B6^R&-zj^o7(&p3YrMsh3 zo68&T%`Wdw&_*+vs4%EK;x9#`mC;#x`R1m!lM~y?!3ATdCXRPg+unHL+!dI=h%@i= ze+hZTq!ywdyP337L@RzceDd{7M(*g=Z2hw8m;PVd$A5pMQbF#Qb7@+1;zW|b2jfme|aSFkZ;HrL$T*q$m8KNZ~zE>KsZN*BSQEFAr^AY zXT@((zBW%nnhBi54N4D!n)8<$qVtV+e!cF_Z#qM{M8HEI9-z%Fg|BkAS)gcf&w;;G z1Kdk^4ff}HIyIQ}I9712U?(*;G*PHUWaSDb#ete9^_MFAg_)vYp98s&>e||_ z>(?~bE-TqP(yK3Xcocj81ae<=;QZ@h|8R(3>i^&L^6mTj6j?7Mel^B-fPI1YLj)h- z<-5m@o;Y!g6sz+nia1rUtr{AyHmR;Owq9#dH>z;}TD2A*-S!mC9HAd29!Dl#IGK&4 zav|ZP%VF0`;EPUNsX{DQOfed`Sjv?Ka;3pE(>Jno!zxV!UKec)9{ng!PvnvmmZa$ry-=$b@ zb>q%7O*J#Au~cd}m6;fuDU2>=hF6L+JC)@}gG=`VftazQ*{W@8Ja?vnw336SOj76r z{3`a@q}ORSUt5dxMcnPIObuq!tuKFH{kQ&~+^vtt21*Qo1M+M$HX9;_ttejtdCtg9 zD0S3)B0>{y^41VRE%mkE|LFDa{L0VjWiyH7}eiV0ho9E-jp685TJRep26GpNV6&%3ZJA$zaT(;5^C zW1p`LD?rI-jkxDK3%+;YAK0&$6{7iEi^kA4_M-mfKhsS-iv@h#!iIn??u@CU8*vH#QkhhWk+vm^L zo)Xb~%>09ho>28c{nZns?jOSbL1`X>zAE(p3Z4J2hkdGh$nxEf@=IBN2>Yj$HTwU_ z6UPw*2f%N*Qr~c`u^HR{YExT%Yd1HxU1w(|FW5JUK|GlJ$bC>x2tG$zj%d3_vz-i* zU65$JnJ=Xa9{!Q(NNLoO8E|9=L#46w$TTB+LIdN$p~=C;wXFwFw;w&*y8oP{zWdKV ze)JM;|KkTQzxC?VpZw%Izp#4yIoRL4{d#8k-saBZt=*mV#ksk1VZ1-LIJ+=Cw>GtK zD?hSQn7CP)y*;({a&+U_z~s82v$I!qwe>=c3hbXdTipQ2dVzgqlqY2?#P{p3T|Y}E zohh(9xA-T2^xw`dZMXj5Py4?A$0Jj-0AGmmB(1u{JZQQ1cnR^6O^7gpWaddeDdxAj!>Vk5HoWrRt>M=%7%S9Ta#>{;*vfql6E zN5Y)NIdJWYv>KSmg<~H%E|Dvxb``?Nblmu2_>Kbh>Seu}eLufY2{b)?mJdQzwO^i* zrM|)mfKs9EY-wm#8sGj%U5^ys@@va42}?z|~j(rC7?9)NQzs&QOOhDrO@KM43QEu<$rsj)HjprNduhchQ zZ2UfTD$t%sl%m2m)X1CSf4Y@kS}LA{V_9ex@s?}DablkbA_Bz#bw2@iv25?_7~0(&A-;H zX|d3Jl+OrR(Bm(jh{!;oQ zetb?B(|esp!$1A~zxQwd$A6yQc+>Fg51TiBp;>E`=-?<&piq??@a2S9RPi4BaiG%`J?zC z#4nYuPG(wzeLSA^oyNOs3xDSqf6#m5gQidZSL?#t{v?f4d{Wj+DKGCi_n((M2nl}{ zoTtlLQoSET6tcu$)yo)aay z@9Y=pDCzoNsroqg05yH~S}tF#r8x1_$(~k~y7rpVg70rw?*E5u0nS$y0IBUu4j|UN zb?Ti<*RR#Lw_k7XsBQ0rE;YAas%K+&bWp-$@%GV|gMuAp(w5mzzfFLjbB41D6UZqs zdK3WBZj1JM@&18yX&@_rKU^9ZDJs}6Rz|a==;sDwxshOUFqj$&rH37%f;CXE1&h{T z!4$~qeMw^=Z4PBUWV{Yeqz6V*rNQFx=)lBGacVJ88g~>%J*6@Kz*uy2rn10nzMBJc zDJ8_DuqYGAH3wH2={je`B%S2MR?R+b+YhZY#7tG-avcHunUYH8;0 z17EzvyuYMwYSG$5&5_aG{MyYs_x_I`{`v3y#;l5yArnJ48yh{iF!^-l|bkK znEc8ZJ_Y-_zAoB-i!;*Rmq)NKuSeF?){h(^LF!IEuA}o(M?2qK{)Fle;fw^n@K4e} z4B#v5RRHY3sF4e<R&A2PUp^CYihpU-d(RXH0X?%+q*9|w4bkStylN9X)PL~ zvztksOx_8ktfb{IQ#+J*hVw2lS-YR3Csu$1_>-jo3P67a%x01J10$u%Xt^?08WfZ& z3oWiOOJZjG;U; zk@9$acq%!5BQ`pdo1B~9zO%abXkvM5b?4E>ou^M;fBy7?&)@&#+wXt+-4~yH_rc3g zmv){^uiv||a%Xe*(Vd-JYtyv%tt^cUu1t?Du5b5`uFNdoFHLM#rgsPD_QqBptvr0Y zaQB1a!u`tJ9jDFNdGVb3{5iTIp@&0^+7vsE8E%ZBzgnwtr@G7AoyGg6@~!WE`0{`F zXMgbC{k_gR|AqPC-yW+Bja5*6l5&G_gxGi-K{{KJ4arN0y~no9#KP8Cr7b?0j>W$F zeD^yaJxk9&ZW#OIT4=I0P`+H>%(jz|0RoW30>D8}MoAV)J4&K{73N%nB<08%`4RCc zkahMffx~V*b&A<85UpTeNAuQht;9Y62e4H=AL);XN6<5LTc-5z5wZAD>{r!(7E%X5 z0iXm}`H#A_on5VTGr*IlIO^rk0oQyYB9zmL9an;|;OZjuCK)iDG zQhjqm;Xp7IPf%&tdi`n-qs9#e6gunm{rLJsl8@5D>#vaV-+b#UbKsC&ux}C2^AIdURt1=4S z%bxp&IW~4)-@9HshYxUif z7IgNh)ef62TqxzIW-H|Gj?G3ZlZmmp;><$n#=_Xj#`5mNxm)+{z4zw+dmp~{`mz;^7r z0u;?3Z=Oiml~zDi6Od;R_kdkO{s8s+K7TZSIEEl9{yI8wiAxCz>Lu{y*XCF7(>Q|( zApC(O@GD6Or~&+5NOAxPKYqKSB=F-eskAQF+_wZ_&&B=c|n#XtUcfAT*KKK`ZVFaDzS z=^yBFGgoRG;7VV^K5`##KqJ{lCytAn+$#1-;K8NOFr8yZYMHs&-JQu~A(zeGbm?LT z{fN4|ub(^1V4bhU`>OW-c^dB@RqFVviyy7&6CL}%QN;I6y#FVjk!<12J0~y}&tGXi zU#Gd;X1LU%J73#*wNcyN>tc+iJ(%+*3jS=_M^roul|8AFBVM+I`fZ_pK&bv89Ib6lrVQ7j)!=gKn+!_&*zk=f|rjK4gY9-l4Gtqv}3GNWf=b!U0| z!M*oBeDM6^S8u=b-bdeF+IhTk`{}~=ld;vk<*mne?mphxT3(wUUY#D?T3)-+!Uw}0oq{=a_sXK(KBwD&r6Cf5Knx%zXZbOOjrm0n^W zaf8AyqYuh^y{{5f*$*=t4&zcfbO~Db3JAJ*SljxHO zH7O>r#6FN`{{wSKVn>AU4_fb{{7c|h9sAOI2EM>PzIR2q?}Ht@E07l)KrY||QlA%s zUMbN9@1R>ko1?@#1_CO*L^?;g;4Q9HbcTJW0eFXvaB+`X{I$+@4d0pH0>Jq#m?sE1_{k`S3l(zH1^O2vkc2%YERg=V9tEw`c$&zK7<-}&sLU;F;I1}Y<+ z3tw91UZ+!ud^Ez)bC3@9fjs3r@Bt*gd@ei%o&{7+CkXTpmy6oiu&sFWC*ObYcYpO~ zZtmRexc^(Vl@Gd-^Bs;PcHY;pj|RnDpZc0J*A?LZSnM~Rt+8meiDZ(&EH1m9P@uY@ zj+P^A?CWP3srln|eu;fy6aab21b!6zQVsa3gU5kq@5i4}xWw1wzF-RnpF;ZclH)nD z4bReKm<;N+3w6DxuXUZe+I6wPsOqL;R0iwb5zTlq{n0`>QmT0JC0lC15YFoZSxY4E zOcwp3i~d-qJeIGFRYs?V#-@kIXQ1NHWSoSY{D?1AW@wkzY}23*wzSb0hIyggbmJr` zw56q^wyvwLuCuPbtD&L0xtST+eXVUwyz*u9aTv(dVtQ!Smml+&rUL_0(UIBs*g|S- zVSe-e(Cp^o)}z}`K79QC7n}E9jV)H6~-sat>Po6y3S({#;9bI3Ty?yt| z+{XR!`R&1}_59#MZg^#U<>Ac6v+<=TBa075Y3sfB{+;Jv%xpdxT)OLY1XSnF^)@$= zrXqnaj~Ir%>DjjD|6S9kf4=zo2S5DtKmDy=`$Z;T)i)P|J?Yk|PYtOrPd!c;ie_TG7tNgt0iw2t`X>ZRiH{bgB;lqay7~p$;zvq{STE-s6 zM%VA3S$ukJFVHLcxA*O$*GH0x~#{En@6bGoR^w zPDObupN}5P9=F?$0U2@#K(X0i)XC+wMFqbT`!uV+svtXm=Qo)@!#?BxfA^LE{0RFj z(+^w7fh_iOGd?iDmn_Jl6O{!8)uk0R&5A0arJSRy5?Sh%UQ0)pC)Q;TCEEN^wJ&Cf zq#TK~HP&kgc58eItuLYXC#{jbKpOS+{)_oR0gS( zYEE4(x2l3yS-}NutE~~$)rv7BrJ+vRP%mq20Mo|sak$!A`CN@iKsqud(#~|BuY1s& z9<@aKv0Uj9PkJ;oFdfWHOswwrPOp*yoh$oW2e;9%3vys?^TPPb`H7V?`xmaCIla5P zJhi?sv$c1AdFSH5Pj?#%p{`S zky^ekK8M2SNfcy&oaNxxBqej31no-urXGg`Z!&b@yNV`Jc|uEpgP= z8moTfWMNdY#Iolkv@qW#(S>3S3|@IogFL|n8H@e(W)(c#O%u{yql z7aXl$|3Y^5%Lnf~`%nMwUp;?xSFUrQ7%@qkqvFfQHxFG8qyzg*>0=)r06b*20DcrW z{6~j}tarYWzV;^%-gx>S|Kq>?_WSSa`c}o^d8Iul77HPAvWsPRS5%-?Xg$Va=D$({ zyi>@NXg&+&Q_kO1S}drm)ym~s%o|mp^R*PqtkDX8m!F?H0dm$o+X433VtjyXe;W<> ztp~rx{!w2qX5%yLlc`?pQUE?g4O)WJ(VrOqhb-y_p}tb$s+4*fl$|PP+!F70MAD{? zgxntFTLNNBKxPT({7Fl=*Bt8C`FpS&i9uJgKad`Z_M+})INUde2p#fsJjr2$KPf^p zklF@VNl2PRBTMvn;#XG+YpccewNlJwZmgFzHOQJ96`bZ)9!D+U0sH1QwI>{Pbfw(s z9!EOk?H%gq8xHl4hB71R@tNf4RPW5f)Ye|t*i2?_Wp4Mv+W8xq>Ght;)sdzBtqXT{ z4{mMlp55En*x5ny&-t;1z4XXZq?5+@J-OFAdv<*D%E;>B#`!xJ zZoN6UbS~OGE^9%+qtc*K)gbGuvbq+by~;|CR4bi(+I;3`)`8{cw=VX_!zcqm#SOam zAg%`$!xq>k?lfj#Vjn^P%a6`h%?@1!(aX2+p|+s9I@h5O#@Ur z>Gek{c!{aY?tHez{m+2^66$lnM^$gGZWpDy4E{*k7xG0)xZGtb;2o$3_6=%1Q~iwX ze~o=PAqK!HgMZcmNTmU?d|5P~sd!)?i-q`eTSgVY7Vs3y4q{hezC;)ltuh4$`_j$` zBpeCIW8t3zX2Q^&13vrbQ|U+SZ9K$2!#=9{C>=stV?}*4U(~hBJNHMnv)}o}&wuu> z{_-#SyQ0VwGHEqQS}1{^17A4UKE8bX5NsbN0O{{RnE;N!KQq#sOfH?5fBaY0!|(p# zFMs(j{>OjbH99Z0Bpdk(2vPb|R1kkJG7wP#P+7@CB{fE2(ky-E-y@EFmb6c?UxV0R zc}dgBQfN9Ew;3$}xHVM>_Ei^>DY_WA^RlmxVjp|}D~Dyq{2BR&A&@;zT>-gse%DTp z*kdQh5V8V+xebART$3pMEcV&_ud1SgmS#bN)LG5Z)zqTvD<&V96>?>JyV2?Mx4Ak* z?fzzkm8*6rtszsm-y9t@hBBV?usb#Ahz+`81EyfQy`u}h{f_jAJvnHMWc0R9L1QyQ zqB-CTYO2uRnJ9e*{H6wZOOp~~+K#Yql*`cU+8=>`wkyz`P7e-d2H*_K^kp)sRH|=i zXn1mRd2@4eWi>H6(YLtPzqI4;9ryQ*_e?F%?;I}fUmKj=ym0Z>^zwe!$Vzf}35I+} zYEtKlb&V{KES?{jJJUTu=Juj{|M2SJ(8|U6-RqZbKVI0s)-}4)rqFRK${@5!FYJnH zqD&hpj1baV?pML&N|H(i6!6#2eO-*o$U|M228cueSh6%X^q(kYW=#S}r z{8-EhkO&7xd;49NzaxD2Uw5B>`lrAC!^>y)_-)pc)fh;M>TD7xfdrtLU6tsA&8w|p zX}USbKHJIW?&h-hAq}VyfQ?T|cNxIZd;go*|Lt-hr}QCA@=w$h6<|w{{`bQ^&dfnR zCm+ZW`1T3vY4d-~{9@S8dzBm~ThIVZ`9!C5d2?;!DOdp|#hmJ@=87^&b3OVanJpHP zNX`?qawY983YXX%R0dM&&R%1<&l>4926{B!w9FoEb%qs|4!tj7jrHrpeMWyugp{Gm zau9xg)hR)BCBr_``%wDwoYLoW+k||bSZI_1*u3maw;ma!9=k@fYe=@o|g?%=Hy#W(KFNo z=DC#(1klW093*GL_QwJxsI6(TCz}_ZB{%Q<;wRr5iTk0OB_a_SnZ&B4!kzdPm;_+_ zAK1q)g(KKv;inl$B-2CFn#ccK@Y-LbX10gpAqW+aeLe?Dz7D`A*r!R|q*F2q4Jg2W zJNA!38+x2V8>E`R?KsT;KKqOYARDl;ycFBxOdw|wytD&`z`TXuIL^R+e47xE0B+&PxpE6fX4ZuQb$tejH$heHdjRBj<$EE4V~R>CZ7P~BTkj`F@&9BpAr6?-dD6VwNj|V|+pB-4(&&=+OuUzOtD1UnQ@bz_7v&Yf)FAz=XDF>>{bP})XI zS)t;A1;89|X3CwR9!vm>5q^jTA{l6|KBzuVz$4fP-VsZq?RvlmFe3m^CEj}sVWLW( zaeJViVV?n$Av7E5qxL?uJn&9Lfn1H_Y=^l5@DzC#0H;Yo4ExyPA%lO;#fQ9K_P&f- zkdGmw=|eCmWO5#tkAEa;=Yd=jj5x~PfyO6rrVF~B0UxMmz$ZaKx{oc+LBlNc^e31^ z{tcr4a+7HAa-eUazr)F?Z$N#HNw34NfL{kjeU6pk=g4*R!Vi=c)E)k0XD6(-;M!f` zhyPJ_;b#JC6h%a+=iuO*t*>XENCtm2Y^*IQf=>^69E$#>^;_b1#}N1T!Ta%ZfPL8J zn7~Ui1K}h*()-`Q^$|m02gwJ(J03rH>;o_nM79k3bhI~P`$TA*c(tyukk^C`{oZE2 zp}LA&RaV_tQOm1oY$&giG&ZAEf?g(TE-P)WIN5OWBt!&HV&F?n0tuF$r?BzbJR+?} zs`Dt@y>0eRjXSJ!MMZo$AC~?R_JuW7FB$s``)VkCihaFA)Gn8rm2#W9)v0ZB>$P5^ zK45MSIczbgey=+f@b!i|`=gQJL~=5np6~6R>+hQ!7#N?MOD`;@7dL_Z^z3f;%Im-0^>RCWB>SKdY|xoPBY+7I&{(g zUD&}9ve2@R(}HnwBncmbSE*(P+s9(spRs*B#UKwM!1f7ar#?F5} z%miTAXTU%1;im$D#RrKrz+zXx#jy`*z=#B_{#LmX{S1*z3%@4(8~FD!)C2E1>=SfT z_~(GnN&pD_!3PjCz#>LCcoS)skFHu3TzS1EftU-*tZ>=7+FFOzk_ZJ7oxT*Ehy^Gi7iyrw!pK15Q#`AgsX?X6;b`o@Jiw_b_Ww%n1I5+(`L#uGEt1$Dx!Q4Dvwenlit#M=kDvC<_hqQ3Tfz{q zkb>AyezLy2s<9kV>?X0&#xuluwv52qEohI5O=zrH9q09sd4EXgD4l z>*|{7PA~LzuMK84Mu&D6X4keirna|-HqNEy_XZZu4lZ5D%%7cDyS#et!Gq_Y-F^DW z?B2D^+-cx_`P}{Gv-if<5Z609vVL)V=gR2%g_YA+7xpf9O)Mdp)l^<4<#6%S<37dw zR*bwu#n~w&m((?(`&k6lpb;{I8w)x#q&}q(Y!)}M2n>PlCeKK5 ztq3eutUQ@Xqu;+VP-QFlOrz z9W7M=_y8{;&#=#Eza9a;BWU9Y>~R2-=I@c%9H0%5;w9{b*k+d!mjV+YseL)%1N&Gx z&JT<;p8z8RN2n*` z&;8_-K8lWDWwgMM&*vjE3bq;J{0;Cu9?i=;uA~2d*(b0i@Fc+hCWP^Jv>C{WtSF$s z|IKrL$EPAxpDc>~hVn8#U)e0s)-}oMIV!%|+t?Zq+Ph`JQ9)=_5gm1QPokQ}))5QE zy1Ki%f=)Y3bwoPq3iE4Gse}&Xh57YGg|JHMOF;=vwVbMKM3?D?W+m=Hp_E%w+fq@+ zKUI#93B`UTuup(b9eu}@K7bFUPmO(RM_wK4oe(6zfh$h|)*K*%i+J>RMJHHn$+*~d%!D<-u7rx|aNGT8 zE^jh7E82@I5xd85=kg*5wYa=dv*a~8fYQ_k8V8ZVS=h9Y7A6hyccGrVNxoLdSIU!Vhs4O1(0ijeexU83XAS|w6{0q zoH$plw7~p;KM@U&xLj@{%6w@80)c;7Nlumj4rKjT+x36@o8lV#kB zau(f#hhN&%0LW*(eAL*_DSd{0vqEN5DP0=1N3Zn&`xaBkZjE_dNw2#*;Oh%^48-zQw(sneE8r8m2J=`$~ZTGk1`U z({8p^=>kptS6a{hespv-6$uBuPQMEyo-Jqu$3{Y6pqp8%u^F^hy#}^F3<2C!Say?< z!-4PcR7+ibt3raVDMiJ_xTSHQv*O%beJ<{KX4-@ElaT#U+Fp+K=VSt&%Gw2R_tB2r zu=mlIUyFwwz)%plCPJRlF? zGtLjrKo@oLlPUld2v7r`l7u6-ArS=>{v@UV@}QI|Fen%n3z!Nzi^Aa#p$7@Tz80qO z$@g zUqGICUn9YsD*$>Qp@G39S}Vt-QYA<}ysk(yMDPTu_(JqeMT#`i4bX2Sr(}V+qy2B_ zTzdR#y8u?eOGlWapaA+eD=2c;gj=5Nzw}h@3e3Y$35Nqh0K`ZqTLcskSR}>5l#}3O zq#t1AE`oj9Gmvc2IFu^ug2tjkWG2^_o|K4nYDY|A4hq{`%_4m>udTX8S>B|o5?X6y z{yJ4y=^1sU=AwO*eVKlYkmz(_cDd7{R)YXEmK2e)rlP`zqJqZaqQ>IlhN9x8lA@Nf zBIG%6%1ihsOZWgjocy&_V(5BKvy{t~bGg#S21RpYE2pK6$I}RSS|MLA7J~3oV_#-h z$z58FSFiQ88-wVs>2@X2-!kAycla{l&f$1uB9)x$>t5*ZTNxhQ92?o4nmj!}dv0;z z;_Bk$+(V6Y|)K+MG4TFc8D=Jic0gU>tXjD6KL3Hnb5Ip#o-qm|OiFk+C z;k2UrtC0+Svs$70t$M9Rr@>4&EHsYBaX+TTS_9IA8~ z>M3RkSI2R(NF*-niYM5orFl^OSlPrLTm-L0xSxjSh^8iqzNDBJ$D#B=?16eQSA;Fn zewYssTWEPm1{M@xT%rvYgp4d|ED$=8;}S%kC{&0-HURtZsL>$;i~umDpHuhLG9q#k zqzGRQ+=GPkA2|m|bq@-HP_jf92ml%QS6+&*6cxl!-$(^A8QXzzU>}KmOwlt(A9MA? z4*)p;TL7Yftyo6@KNec}qQ@Dhr3FLPgB~viEViI48vN}?cxRmX>xVh4QD`Fykd69L z>|_6UTmVTybtMH*#5s7f9ftjv4iYRMy__W!k&D9>K)3Zppxgzd`9B;H^fS=BBRUR% z*b7t+&@)-uA(Vb&X)#HwtFPytI?1VnyI9?%un1Icr8(Sc>=4Mzb(~g?)GRlKCGAOx zdqCvq7aF4)TR0p`0}fDp{ps#xI2^E9;Jd3U%p*!4@d(Twr3o)sEuXIy@b$3tsh7{BkipUC0ProC>uJF1w3&&A z-|GbSyMz9LNN^+(ok%B9mb)~VSsNYNo*3PqnLNKZcet{6d3||sV#+tN(1nIwYlz(+ zjIAGTU3~4~v(N6n{prEo=MSHM|K=MX-+uD#E3bX9dibz+<1hx`FYWs$*Md`Pp_xtp z*qSYvY&un?;Iu>ozF1ek`tZx9$A4>BdzOetJ2A(NW{P4kgvVkA_8l1ePIQT9#c8!-YA1$y!MBaBHCRQ(g-il4Wry1vJ5YI0{W*Zs9nw}8 z)-nEo4(LZF0U#VOVUvsL8gvj3nsQKg905M?4kSVaXH_<>(q#ZAASE?Bl#L^ncNBeN z$5|0Bga97mYHZ8I0uG`Sh!K9$q3cNR6Q?3e;mf)BXp#V4u0fF?!h%$2;FCb_=R6NE zK!|{nD=-m^1S{dOq@fM`+gZdP-vho7_MNcr2YMfzpTzY@#&b3zKwSN_0tj1Ze=sOw z`$L?Tmnmf^C6?j&7KO=;x=BP^FfH9jsx27mvBf^b0)USsKo$`o`;id(b)&8yq67XF ztI-(op$yY&H|VN?eI(n{_5TI?fbB~FuP!`MTbvJR@RHuo!ubgMS^OS7#B~5PK#zqU za55wI5Cvq>4nox^FDlA!C@ug+k;H>TBP+`_^Z#J^SL(v(GQyd%kh`QD*y6WbJHV zawRx0k(ihb&aC@JR-NGohm=d0cd!LBT9QN|k=LOpnlSlBl0`(c@W$WfH_Egq(!%j;HkC zSpb~DANvgQIpCAN9H`Hw?Ruc}fqm>l(St=0!a$?{R1RdrdDQ2iQX}4%P4yv;M_?bS zAO1!|t4<4Ep-?F){7G5@CJo_VO^cEk{8{Lq@qZ>28TPZrKJl3n+E3&F>K)Ly7)?%4 zF+^hlWKOr)glR`Qm8we6rbsNh(aF!{j3Bf}@-Fv7|$2-AQs?un+%A!<*atXh66t4gVtid8MmqUuUsO}$8JZ)i^G;owAopGqQdtzvK2%YHrdY~DdIt~2x1o9<>?KfjG(WwfQZof4?3*d z>^_STh0t-2E$Mf6clt7s&Y@&%G!>uhO)U)et&9w9Pmb-)PMxJn|LWSxjm`DRsX0r0 zMD6eC7+M)vxj4Cf^YpFf_nv-!`^l&GpMLN0yTA9wi|>O2Y+rqC?!w*V=7r$=R%d1+ zn&?iZ($Se^@6d`THo&W^N6`T?nyit2O=jPZ(E~0L$9Gu?>fy=9{64@Oz$eq&jRu=h z?=Tt7I@tRNqif-4!VE`Un;Ns8z0~LLb68zAi&QKqp?V(@fOW28q5%7h--7hAd>&G$ z3)eKK#;R9J34~v^}1DuHy z$Uz0Nc?o8*sD3AUpY2d-fZ|+q=)xEdQnH7|I6uKYN#r9{dpQ62VwYkc1|o2RP9qQ@_w(X-*p|@%(0%})Ev-t4@&T|a zzk_sJvg`FTx6g>bU*F#5B&fE}zp@hVofVIT#5Q*nW)wo=J$wrJJpYh;v&G{BlvjHPUD z5LTXSE-B_Ui5hw87Eyb%P+!m2@RU}e(yr8b{NcVt@3=3V(U>|_=AfX}+Q@BHD|MOf z^k{$oU~li}z(6LQN{54Pqh5_UQ01j96#Hmo*?6i33EaTGpaQ_J5S==SPUZloj6C2%m2h3d|C*MJlt%mBebc1)ZKQw>J@R`|x{(UACmxp2j%KPymVa zLut};X{m z4WimAm)&7P$DvRPQ(O_M1L{Hev4xfg_M!B%df#ZU8VzQ>4qJ?O!6Zi^p9huUFk!rV zJI==fE3mcer6OJ#9XZXg&uD*+{6oQEF^4}2T44wS5yuzkF?q;>eGT=PgGp60Nvs8v z2bvhWCXAZK?5LTa6$tEE21dpTa0Gb7mY$YX*?1uq6g@Zq-G{wT+V$iT`IxT{TVmr= z?6a2u{y;tUsT?4Z2Uo^9?1M4vZVFm zJ61q09ml?+u3BDKC2Odaw{Y|-gWDZ3BhQ@AZx;!HeVa^bQ>p+Lt0U(2b=h1PXc;Hg zmdlp%I(j<1J(#SKjz{B>a5CkIriVtSSEffdXD4?S=gzOMT-n~Zv9%8DZ*A^R9z2-6 z{QBhidlS32=1$)`d*}JJ#~uoZ33LGHhlG1`&38Fx&wZVWbKS8XpXw`SUG0E%ZL@dY~T5V$cKo zhBmcSC@{k34?h4VM4N~=01Gd%7-0d(F}@uto+_s8oVM=yYu>N!@2pF)#vgSU3VaLJMO{Re#p!&)^K; zvmKP>LeVoDA9#nlXU0Bu$eS`_|5&7erU@a+z!HOrhyS>o0qWZ|##~;pibw`h1%ND? z77nr#so#$>f6yKX0cQI!X~2j*NIuj4MDL?42w6gy-$$JQ)IcD;b>Keq*sO5HbT~+i zwZrT7`J<}t6;*iDW^!N}f*(14c5A>zep5gmMjS)|lLL4N@=uoE>vub)@INED6jivb z@`}7{jV*D?9j%wp%^qTlI$x}po@Gj(F?NQ1SjzCylhXisx^SE=YOs^8zqxjxfH|85 z0Dw2awT{?2nf><)?Gt{YA@4+E-ihXd{Fb78j06StaWd~@sZ}X6$s~Lry&#W&vdknA znHAD@sYp;!j=AB8!Esu|wT&{405^h5V-Bf}L5bSU6}218oq+%FOF0WqM-MqO2_-`z4>?~hu~=jTtN_5LdcPdOeN63im@ui+2v9>@dmZ+$)8+B_ zbiv*>Z_;J8!+VdEZ1mK!x9f2l)H=+6z#n=a5&(K1J080kGku(vc9T}^G8qkW88mXP zI_5a`k7#_>_s;MRoeoPJ76G1Rb-o0}Oh?nwKjO4MI#Pqv$eEN3NMBT;gl4%iv@12 zx?LjVz%8GDqPe_+&ueXHQIu8kY8%9@DvaI^sLc_H(j}JK9loB?@r9+8^`+H~`Nh?V ziTVDaQ8b~Cdp(H4t6Q3UCeui7@6zlH*3|Hj$7qx_H%potTU(kW)s<*}Cq7jntvV&E zsZ=!7wM%6ty<1~W>m7Z-kXb5gmq>`xS4f;DyUm`kx>D_ql*88-4@SZk6UOIZc3#r& z>hB81Q!$<2BeB}m-Y}{N9f=WlC=u!%_x8;9PHrEbK67ht^V-h#(8}h_;X`15;rg4i z2d}SRdF%S)Pwqec{Jqb=dj8!nH?BOG*t^+-&RuKghG$mhGyVP1aHhMr)8lvRbS}5c ztkof^%c-go)YhSd0rwA!)ME>!fcu0w`f$%6lyBCm5#2NE3H-5is#d@s1b{IC>|^0L z#yUed;8~ahNa%&R3ckuXD&zUsGOLzJ06fKV`Rg9*IE#fNin z0y`Pl&%r;tpRMF2fd(u{8+LK*KpFt@OzCH_Ps9}Yw4fOXRUoEDauX@HA-RjJc!5d) z`VY@e8-yr;NF?z-S_J?TiA4tS1tL`vLpP)%q1~v5d+S*8ox4rYaI96&2F1hr&NRU|NXIFR{-yNA>;tTem+5c zZZ#AlBQ9J15BM|5kOe-F3md$=j9XgLP(axGOY0@k_e%Nxn;$eEh24hQMFk$ra+^>P*KaTZRAK>Eo6>{(I=J~%#O(T z#NztK*80Zg+WPw1`o_}Q+QjHsDiDD8Kvq|4RwxFN$uYS8y1VDc#~}wiX0t&iMOK~_ zuAHh$EESipQEK&VX0+ln*wPlypw~a(cSh|18hnp3iO+2J_|i5{x7C>ngob?nu87?c zG8?0IbHZ!yiTHYY!r|_iKHyb(J=R2zJvnTO45~a`rr20$a&P{^>o@K{zIuLte}AuU z<;=ymK0E#T`+E=GTfF#q_0r?hH{N;t{tsSz_lxE8_d18x+{0U@uIb?9I!66wVllMP z3-^q5B!*z*!DWY-qa4-m6&1poT8wf7{ev#ZF++A^;FbXPvt-^#*gi0R9PbS5;Qv^F zJQnx>7O)TH1?;m})p7V^pFPAqj2-AQX7J_|A9f%R(1wePrW||`o2W(gZx(<#BoaIx zrPa~uJR%T|LmT_-1O`mDWxzi|H#B~B#xAhW_R)SH=YVWvS2nSSkapw%U<--CW^+^F zKvD&W=YfX)84bWT=j(&y!Lx*%5SvATID$kV0{ls$GFeC1rxE~!C--eI)erb1)d#?5 zN}mFscuT6Pt5kBCy*(FrHF#_$uhR-Ye*k*S<#0LCQ^x~`fD7|^v(Xs@XbcSO_a)3AFSi$0_ic(r0lu2!ZVRMDL%# znYoAc1$nUI1t-g;wKc#dxILrpP|(L$P9Wf5OK|~a0pqkLR1LH9^IpYda2~R2it_p8 zB_@%e4W%AM1x@*{@Jox-P4$Y}YGqxGNh(UBlCd)wiuH6x`xF{`9amn*lZw6x%E7Ik_$Y?hGIl=Rz&y2Hu72)eCVqG4B0Dn2z6omlkrO_`G8cC^|)edp1q zKYIPc@6K$jkFTxvE}gyq!4IGQ;AiiA?(ZX!VB@% zBAy88zQoIMME;5MpLQX`_mC5V4F1?cAmCfVf)HTeA*=wsMh$C=;bpJfiv) zXlqkfjrcgx>BM;d2KE`oi8n9rReotPucU}ml$8MNOm0b`1buQa9K58^z-!UAG>PDc z&p#nPRp!%aq7KJUJeI*k@^El+XmDg=x-;GcnM7i^x7V%LOY3S>O%3ig^n8yXZA+VJvj5`2a~6-2L`7D zL&M$6XJ#)w-g)re-4B0n{@~&G&h_(mpI?9c;rQ0&(AZvT`C4H9pmS=cXXVg4y6x&+ z=*TQ4M%SW4%fa4pi^*!#Y4|AP#8+BZ4erO%KbY!gw*;5~xshCUzun?=SS${&%kCuD zZ`U&dz;r#dJR|w|2Cxs{v&H9fPEvax+8<77rkIkklw7%Qjx;md+{gFCCb4ixt! z>eNI$6h{dt9!UqS9M%ZpXeVBv2?wzS<)+w2Mjy5PDYs`Y!L#fHhSMDC*$z2}a()JU z^iyT;h%H`)m%to=G$0+la)$qr6~L}cP9s5o*8iUb8n77N0e||zNVX3SGWo-!2ChNi0}eX| z@?s&^0ug*T@bN4pIi3RQv7qspkjL-Ky#0Vb7JhLVpIe@f$+oZ5m6dYQwXgaV2i2Fv zT~Am&G(MPoJ^Xvv$2s&NBl$U*0Ox#tegVqEjS2-R{xX{*eqX$!1D$H%xC828vvZ0I zImJat2y1O_)Cjm50Y`*6Ab5%r_}|^i8_IbsH{Nh6Je6pbT^H1=K3b=*&oPs=6 zeT|;mtU{-(@=`sQGttwtwy?0hy1G0yory#M|9B{3ad*fy7U(^j%iA|JFf}u^w79Ue zFgrCefL!`iXJBh(`2hI8K-TGVXU^=O-rLz-U0I!+oweF+sLcko@aeQ;y*+bNQw!sh znRr(y7!D`9BE1>4)uFQlG;))&v7xoTTHREqX=yNUn+f*CVvD9-rSA~6I`!s=-jXyq z`eNyYU~C8lvSEzM3;1n8zbot?n;V{88t$F!>zW$vTiuvFyghgM;quKVlNatSUVE#1 z?V>X?XYUh%hfc%Hp#};8<+yl@8EENRvojcqf zi!a*l>F_ulE~^DJ0KR=Ne}?^>fzPx*13vV=L@0o}kEEW`fHmMx5>()|K1ya|z{e42 zc~U}#rgpT{9n)ilTprjygCre=L)0`;vPXV?zzX{e`#4T4ZmRbg=f@F-eQdFxTLg5p zCnvVpC-{{TfFH*`lK>#+IkA8_TnGWMm#|B*_hlaj*vFe9fcMCLGXwyCvgn|n#>RTBs`5I|uw9qJW}a zSW?6*%;)Bx;1=YG%8IqkjqQ9cR+rnoHaoYowS9VT@9fU*%GC5wGM;q%0yeisBsFk2 zQICCjaba(3b8T?}!Mm8#F0ZN-ovP>#ht8irI0N_}9-O~?cyM?K1~9R(&^IyZ3Wr2u zA=33dTFt`P=+64ux$WKkt^L)Ft@*X}zVSJY-KS|YDH@s-b*EbEYBbIDdTygZz_TdZ zOlm``)+5t-L~@<9&91S8ZN5H#bj;c@VDcvIfvCyX+2-@BoUTY;YGz?{a&fe8Zgl40 z?7@@g>$l$6y7zSQ@b2*GdjtD-ePbK$;rZ11#gTKb&0KkF_tCeOuf4r|`SIH2Hx|!7 zn%KYBzk55ncp)}>K00}>b9g5@x;3|TdHdkbEK2!i_Z;y-x!Et&JKzgxEGy;IR(HCc z=%o*^W9k!b4CHP+st2(M8f%kjD;UTT{bt;Xw1kiu_8draq5#)nD8?2Ck8^%V0LJ;T1E(Bh0y75) z8Q=<8$d7%7++~?{Pb3d`J~9cZ{Gs6n!T|t$mgC2;&ptDgKe?>{ZF5kShf?2$dP;i; z`C9U=oCo&t6i|;dAq1GH$N3fLxmTEv8NN=Z({8oOI4wxLfm$b89%Fh4 zX=igeF`DyeWhZqKZ#+zI$OpveQRJc#i%|s@??fI^2<@HyX0ur-^O}tvE~gLSW{51c z(%Py(LOa^mAXxUB*yq5R5B-j-734v`1M-B@mzBs)om5nvf|e)n2Ood~A^!vpT0{bG zn`$5$S)Q8OT3$K5xplCA`t0uR?)uu=?CiqmsIyfrtthuhMKdGAd+Tc})6<<+i>$Vq zTLkiu?=~8@clTDewkGFiH_q&A?C&hCuLXNj!JZy(JSI2kMXd^*Trt+ueXzfO?c(K& zm#>_;aIk&$T;J%t&ghi2aNz2f*PT)}H|gYBt=g*5`E-`J)*2RQEkdn9rnAUYR+qmw z5FK@N_L_WYy*t%v>1@SJz(B}=23h^cy6s!pnwnT28CaW~I)8fk`t7Bwk4DelnY;Gp z%;h&GFFj0cTpHNDGjZ_d(7_wiSKnT_^>qKQC;=z8Mi1MOET)aZUFlfu={s6O-RLkQ#+ z0XxlFXIU#JX}LOT(xD0gJ)(%YfPc1EHbX1J9k$;9K26I9>a&Zu0=_oPnXJH;VIQx} z2?3ywENbrq<5&#rjP?_mgMpjW6#x+=;t=fPe1>}V8Gv_ynxPv)fVKX>$H2$KB1fnR zfE^|xuw|cweGGOrg#=Y!&Bw9NR6n!*3F@i#&uV-calV}2ey`SON(WW?!3Ld4+I+!l|?fNc3p zS2E)Db|62&Xh5@?PODjoq*>Ve$MilR51kHBV+T}Uj*eI*MUVi1d^@+r$Z6I#){DwY zgvEsf{`n__g$3%m8mCMWF&T#{l>|+O%S!xu z?PO1Sv^y2G+0>ZbTtLW!MkE><9h+QS^v1&BzEo_mcV=xdl0hTiPJ_eNs?&+p8kN>C z(m!b}*))@og*m$gKCX^U5x`KLVP^fb#oql5^79Z+cnC)MkPj0RcY;Fz=&Ubf?b;swY zC(m7+y?A%w%ERUBZ>-#Uvh(nRk^Q@w?YrZb-=4Vq=IY(&2XB77|KPptyHDpYJjkqG z>R-CNaqz~ChwtCK|MdRj_pjW0zIkw`cVZ*bKW7Q0Oo14NkqPAL#>z^BiMykb{&ai* zqs8N4+!g4w54|7kh_}r>Y}@=G(ly)_@+CStSk@m@1FCzF%#UP0Xn)*$(EiND2f`2M zGn#Fu%n=v68jkBmuhpq?S3BW1y zC-A3TzH@}1nFDgM2$B|UmW^#{=zWr51&3gI3>H+HCg?)CMQ1>GU zHxPzE#0Bzpnh=zSf3K)OURUEY=m%0=gPFd*WHRWmql69Swp}6P!b{JvPeXXkCtl$| zbtrp~mc-SzbT9a{b!P+c$3BzIx@x^6b2Z*P>~u)iu{C z(V0`NDDlMK?m(P)i z4-cfL(~+5h`0{w_^zy*g_C$O-b?@D!EARBI-k7@f zbm7j6wFfVD9==$={r2ctX#CTuxiiD-*D_0&)AN@G)^3h&-Rzk^mzdb>=$&Z~bg3Oa zmDMe1HE^Y^Esc$Gu^47frYnZkn~bJnA+&Yv=!|N|4+XQ2eLa(1u}C`D847qp=q5^; zq>Fg`koafN1NF$O6$zk*xJ`{5xUG6-_XvB zX0Adep#B%}Z2+IDT7pC}S)^Z;)HHOpU zS7S$r78aAN9xSMP#`AM}A2fiy5xW}O$Ch0OSHM1=B85fS{+}em$V?CF0l@LB#;4gw za-ml0f*IGTUtaDa>xkIzeDK*_{<@n-BJkWq{MMD50{;wywqdRO^uJjV>Xyd zCQ!T<3kPi`5O~0xk$gh$De!@PZr%xk>wy~*%IwI}WVk=kH{6d(7`wx((wq86CN5mQe(Uao zyANKwedpo&(wbANLHR9GXgb_}M<}j!cS50Ks;?v1W%S0-{NEgj2D&nV@Tfa9Z1SY+ zojuM_UobW4i}hRlF;gfRh(?B@on!IfbUMC}NpDQ`Upzg#zBi3leUtkeZ+-UZ=8Ze) z^^Lxr{l1-R6XzZeZ$F&4{Pyzw_ZDxxy?o=1=?f3~w=Sob&PM0X51xLIT)pleJL?_T zbEH=+i79XItS2?D^G2oZ4rRN8uQYIFn#LABZe8RpV6YA}KGZ=X)X@>`R))sv4PmR( z*PRGMDd5Jy{R1n&Wi`RM1Xj<$PFl&8m*wT><>j5oFUUjoK41>8Ly0rDUKaH1Kp-Blql|h65NvZt*t0+!10BOJlqnP|gD19ZhiO>q z#3R(5Q~*)wfi1gi?jr064q}!cWB5W24-2n?y2lIIyI{*c2F}b~0SEDRkTy{FIb)w; zpK5W8cbrH66{rKdcC#(##aFoVUs6^gE-7lO zsWLV-m^jU5Zi}I@USC(+T5(c&vRqVH06{@zMIOWj&If1ktCW5<6kdx>BI?j-P&ktb z1#C(gu_ItI6y{4#mPgvM6!RfQtuif4`eXw!n!raF8`SeV^z1K=TsyaV zaCP_a{^j?+|Lq_C(XZ~l`)u*z#mp9(@Lca+x!%2fFSU7l=Xr^H~F8*BnqJEELTT&cO;)R&Co9-udX z-{W#}rP|6`9@HQhUW0NIQlnFhw!1~eMTG!+VNt<}yjOFppfDc? z!SG3N|0tl(I6uKU#eQL7A(H@*0)<7zs3JwWOAb>EUu?^i<4d^1Qw)sQ$>E(Lkv*gm zKn`tAT?a_iA$B@DnV}wATr&qi@-6}r<;Q~FKMMANdt__+aB;jG!iT*zw(Kc_WtwV8 zy?l^71mG-<2QmRx0GK}n5y=67D-xr?8S^JWgsc>j6M934heRkwhacdabmAbjx|kq` z>V8lr@-?z=5`vU1JVfihQ^=B{dD9gnZ=wVRK&TN!6_i>bHMAk|vJIUU!0^!lK2E&2 z!Jc?DlZ>KAlaMb!n_r_|lOobS-V=}YLcB*qkpGYp-1?d_xa^vnVC=)CP*p_Z`y_3T zVE-F<2d;CAA^m7ekWR~OzUBjz31rQADEAYuNGi(H;m*~?xy6~O@u7jS!M>=&DlIJ` z7-uU#53m)N7E9sWLoQH$o~Wn*kSFv${{(qhSbzmw6A94L)K*ogsjg~otk>04$%>1F zczHoSPQw;9zFQ`7%cY;%#&M>G}co=u^D{`~UH_U^{+)!wo7K=){S zN7@`8N9E91q+>b}Ug%CNXHx6Ky*pFG2kWypF6}o4Zc-tAjE?Ag8+Svu(1x;lM$fBNA5@W$o2GdDMGzLQzMomsmbnmO=~?|KGS+GCR% ze?NL@s-3}Bt4D6Mi`p!Fg_$GNSj<*Pe!v`)o?tn`yM$IwNH$kN#qqep79MnXNihHo zm=^)I1qB6wJ&JVDmjlgtu+7iUJCSz+3)-Kpg2DnIok1S2#L2i_OX(kUJ}yGQPyULb z>cm;qOjIge#1+p#N>F(;2^afV40kxl9Pi|y5T$ls2mMhnOZC1SKxQb0ZpGD@wq+NA zK_NxCF1H1zAtq`HD$FS;dwJQ$4Tu0%W; zjie*7OcF+49JfK6R7~Rg<)s=__%zg`!8JoECF)6XXMyzP{EW;Fy}~l_yJ?6~N%HI9aARS&kE=CB?7{ zAX1oBrGlW8Nm!r+^6I)GcKYVePRy$_)h6C}C$LH!w zL}Sr#XGh1}%D0jYQwq2O<@lqhCF-6bn%$o`cmmKT z!2iY{qy)KhkD7K8x_j-z)rP1{E7tiSU4FAJ2);ugptM;*_$$W zG8uw>CXuj1JAE-epUpP_$BXb0QrFWXM%lf*(WF07s}xG1IH(L zKl;Nrmiq#8z{d`gyh0D03VhY7LH@#8^-^OAgc=sAiB{x8J!R`N~I2;ODsdz zj?#LrF1-@ZOb?~wQ2LnMhpkbi1oq3}qeHP(aS_S!%ckr9o!I{-0bt}mtL16N4%-Lt zS)2~Uo@C~f7TXl^$==?z`Puog@r8-;q}wSjDQ5aQi`nurGpE@O=lRKUF*JB#L2G%L zgU5A?MGgVqDd78MQY<%$WSg7X8yjrg7MF)?dRJc#HIvXn4dxwmXu+(7_+e~toRpGYDT{c~ZPwB9F)T*`1tba)0bbn_UOayOOF@#Kmg`2k!v#CIU5fzr4#F!?w#TOGZP~R(_;s- z+Lvo4@|+zx{WA^U)8#HFxF0*o8YA_ugB+@nq@R8@HZ) z`smrmZ@&NiJeznm=cbmVMR2{kj@v{G-Uc|EF)p{R0jcDrROvJ9<5nbhC51E5K4nCkLjnNi&_21p>+z3FZz0V#3``H`-0G0*> zah%Bz24QTW#2NPSLiTc8lf4Q6$G?R`BepmzwGzg;7EGrhsH6<|o1w1~CAdo6zRM9_R zA73@2{Y0VD#t4N)_zv>{_G8la1j9Z9KDPM7uY#Wj-#F|u{B%gEz;raMCbF$Vc14vY zAm93KY~|AE;P6noYpAEoqHP2A8T?s2?{U4KrTxsaN4CeaaH+cw2dS5yfgM{^)*vI7 zIq(Vg=>qVvL(TboheAF(JUBBnv^+DjI6h{VN}%qck4Wf|2!MUJSYSu`P*tU(yi5jNUsNb6DwMz_ z2&G?KBq%K47v`%Q>Wqj}wOeHE7L~Tio&XuDAv_oZS;T zUs7fF^V^JalS|g_6X|?Xy~kj(@;S{yBv$c6by6L_)qv(Q&GmHvKBh@yC>(AY(s+;F zhXnV~Ik}o#fFHH^Wo6`61oh9$%fX*LBzGoNM&yR1@`2onU=bxHXw@l!m7Q&XpT$0) zceG##Q$Tfpp2B$G3(qKK%N~~!d1X{7!Mer(|&oG_? zJ_GylZB_!{%HRViDH4dmCGhS!L4*&$Bml0Aj|4a~M<25LfPJWbIQnR0KO5b{3v*6C z#{BW|h>wv<04RMFFGwig5OMf5r!X`UzL2je7SZ(}PE>v#`)-dxKmSPG7ZG6#`C3?z zk6#7apV2muHH=KCuOYu|Lv<}iuwWcRB|0BZz25rf{~WmRt^VQB!QNEF>#i&+An<>c zbjBl{wT^LqhJ9=qhtC3>NdVCMBcq-XemslQkc1C^8q@d)t|4B}J3%6MWQVx&UPa5` z!B}{zzi)kZdV6_sFcwu;S4#@>Mab5JQ4iOBLB71WNKsNO19;(wEHANhn#0Je5BNqg z0^Vtf>D#bEZK{A==9h@QQX!U4CU&Dg9-8HrmT0OfVGHOR>*4J;^SLHu8o(z|UZxfD zjAo0*f+QS+!DuxGx((qjh1abLc(g$u&uqlLCfH$$hgv-@ExIIhMGc*uwqPvWJJ>xj zHZ(UgHaEApwz{>qd+zY!l^eIO-nn-V1mOMmzx~wMwipcEnt7J@671n z+414?6C>xR#xBl`U!0k^y}A0+FTVRve)Wrg^sBG__HX~@|M<`U=l}8_{=;*O(ncA$(g0b-A5^KFR#AB*6c0-xzn24|ecG(IlEc36Tvp%7Vp zP0Rs?dYpqryYCWikM$Dv8QyUmJGqq;2!JX+0J|D4$gYWp>{GEo9uz$wPa^&l_>BDH z1LHIzALw8p>H(njyd-ysNI&|)Br;O8NEW^XME$_3V7ar!ga8aCNKSl@)WV|QEBlJ^ z<>J5mJe-U(*&_R3T$Jz=>=zb7fR`1Q)K=9HsSed&*MK&tb#*OGf>v$ccI&-g>)!YW znYr~$GExn8@#-t(uacH}=(G0{_6hK*a?fdY#_*AH518ZNal(&>u3$Lnl2?buc)UT9x zsFWS8N~Z)S1JA9Ld$p=|sX$d*NfL~0uJ+ED*5}s*yoP9}+~;gYdzz5nlnS~tT|85p z#Ac8?Yzl{4?+9*zW3tU{deE{&X0fkd%ySxpZw^nfBa{E@khV@(-$v3 zI5RV~9FMJZB{zFhTf>7Zv$L4Tae8#{{P^(U)Y#$7#FN8wfAaI6e);*QKl$;L?J|6l+8|N2)Oj~?Cp@VjRozPEbq*~t3+$;`+^C^*>Z>-YLm5^M_exqD~a-J`~i zL@P4+?HxLQOl}LQEFqP(^X>LPgL6Nf(* z5_^ckrPenCHG@3c0&Z*pH8>f_!eY>7dLQ`0advU`6ms)`dU9o03WO?ry@#4?u#fj;?|6I%`*2XOx6bK(27GK==}T}9I6ux|r)6c`k=`f11p<7roJa!^ zsq<}afoLUo2W6t`xcp?_DrHgx^;pcdBEY9kS@Mu!pX?Cq6ZjVvA&`*6J~D|>^4`+W zgtonJK7FtI&7Wys{8R77)oP?(5R?C?k^Yh1hgTm3cY>3pvg%6Y!+^}^;Ex?(UtV2_ zgP`|pv1eJl9$H?A!ZDQIkWk$T@NId|>olJ9YU`+2FAyB1CLWLr_00E*)fDoeh-n;3&8ZZVMcN_O^k3GF4Gs(;( znaNHz*(AHkZnD{Iwmjco-+S%>Ws>DN&*AE--FtN2UoRnW;km+$zkeTO0Jk^a0N?jV zDDQ!{e2*OTKYkQ&j`a00B&#ZOvnzA5^ht?X5sFk%NVGp&;pIlK@8u@~mKLxLLIyysq6jY!xu<(1+ehT#5`ZuOdj~(HMYGw$fB=px%wMVSj}CVX z;R_O!e$nzceLhPrap19?5ZN0H3xF~*Ej^>CI8|R9otYJ$qK;IjCS>JkE3321%f<0= z0SZNWeqr0d>6x{)TaO+*d->wwi|1!AUm97No!nTP+B|pt$&+_}_@h@JfByLw-+cbX zFQ2`9Z)0k_PLo=m7+;o}q}6Ft^u;B4*--r3vNPIpGuw1I1C?cuZr`|m9?y!YPE zKL5jC{{7YmFVou^8mAUYyQfQf&*YYm6{wQ(BIFuHSW1{w9-a^osSzjUi4)WL5wU(E zB_}L~7o}oJA^%4!le5p>ekNc4vG3@oP64rqD@WxFTSSzTIy$EicuNe;RL9@HAIzO_ zdgc!YN06So=f^*CG!I?j+_enJO!J@^1SFi3T3sOP(c1)t{V>TE}H1n_kf`#`se zq0<9=7w}GKg5xe(KudapbPTzFySFM_5Ij*n*6VEU+z zi(DSW^P?UPfbVwVC~9Kxo$R>|wlo1}SN?zFHBa;7C|w0zo*MHpgWV%M&;GUV!_V$x zegXpyJE-2y``+?Db&`#;maxUa4fpzMKF5z{DI=@(1=R+9V_8XYs#@&o!ajb?@7PiA z1N%JQe9h;;TS5yn_R&L-`B{e#^32VE>zr_Ti6*sDms_5x(W?^hFcIbMyUbMnd$UWiV;=t!hlhsA2n%C3W(AQJl*=}sC&nPZd zq@-pTj5+1ynlfWlYI>j|CMq*Oqr7rvb>;S>2M?Y-fB5|6?dQ){u3lNVa{0#7XCHt0 zlOKNh(;t2H^^bq@kDq-0&7)hl1}aL7$*O!!W^7K8Jg+1xC$B0qy)G-GF)z0Hdf3~b=+u7G8d3?0z@NeXF7SzM{|#V%Pe!A*87K@yWgz+jEPZ=o zKYmaoOj{yNkolpJD~u8XHZCl7jIOkE>eEj?+PQfrBslok>#xJqPq9xEcoEX?aA?2F zvBMZh*g?_?u7YlNo<2CyGwW63*t-0#?7?}Ph*eau|5 z6C$_O=`uCtIhj?(<*iKv#ih;RngV{JTA)$O^!ZVx<=~b@l4{?&hh@ z`mWKY;v!w7G*c$iNW+v#8H&7mS*npOjr9l;dj?Bc5{0Ko#1Btkiy{9)M2O%Gf{y6^7JdSB4LxWD?c-mz&jMj;b zM+Qtz%pY6^&J4HxF7PStu-oZ|_hQWZCg7VGH^Dxs#l44mnlZR|DOQ5K%vKQF1+u$4e zK2`aFcYxV*Dgw9{5|cn;l3@uX$wy=`!G~_h8Dt!n9*FtD8D|eWYkRCeg+D<( zV=QH`Pk1^d0yrS9K<1B;)F1@xD*hDv5c`SbkKb|segy533SkVLw>oKMd&Dk7FKL$E7ZuKrjSh}a4v3AGr>bD> z7bhpl)M)~xGE5blqSF;r)%1*wG<0{i^z`%$kG7ADmA1B)wl=35OB3_;B@K<$ue1*T|&Q}bl{qTuX2QD%``U$5;RFCShs_RLkb zj?=5eRBW8I<;3qLBOu!x@_3=lbj{r2Lo(xC@1O+@K`sSW>nqnXO z7H^0Dkg$8=G>3_Aw^R;6(_any#KI2)9hL9_WD0!7G)E16jCYJ-{V=LCSR?8))NoJ? z@clhV5@7xv_9aHV&t&=`sQS;m8owmb3m~%r_=uyXGbxd7Y3Ypc z<0!3rW*=wj$2p7l(B~Z3Km6MF;NM5(E)v@Vqx~D-cRq0xRj}O6kGr7qEtGe-%#I%s z`}!a-p6~2HXo97M5A0@^=3HAVp_3io-kOgCB-gOKlEOrp$_NFA9e}KM2K%4@z&`uf zQNZ8-_%XpLi_FMy@cjyHUON)x%Z)}&idqyD>(7evaSz02o;VU}XASJfvRRp7lHwHf zr%ox;q&4dEQNMbiu?}FuRJ_^!Kaw2xPWhij0P1^387uYIq30Q(2t z#O!xyf2gBfj9ikIou01E)s|JHX!T*S$#$;xwv}o6x zC`pcs)TE0NRQ__YA~{2?*VpxSSGKq4DoW})J4O~4YrFb#E33mb8L1_e#^%=i%G!#K z{=T`{sk0kntE(f+%M&Z>XD{6Z{6GHe$6x;R>#x54_Gka_t1o``i>$shZW1pn;H3vXf5 z!g}CA%=kns#{e)*JQ}+l&V6F%XOv=-I*f-D!t^aT#+hCINYeL+!3_gvn3s{Lc#1?i zo0vW&FPKfw0$JaMyrYdB;E$pKr(oFJw~vhd`|$$6A8+~Y8^8My=XZ(2p3M+vK!q4W zv@6@g=m!l2=q5q?jIwV^>Y>UDeHJeuM1#}-BvuP71^psI37Osjq^*F5j~!~!5i$z& z5>bj$R1j4>#7X2(U@&&q-56l}3K+YuXG{SE`3&~)F4O~{d>;ZH?S*1zX=w)&EIZI8 zt=Xmh<|$v%k-x(}%=?IaJ#vs`eG1j{V8=tW9%fQ6%gH{J`1%G#HvYs(S}2y1V-q=H|C9 zY~HwY?cuW*AAIuTpMLZ8FMjolU;pz@zy0OKyDwI^?~l%0$T7CY=(-cq3k#D~6*(E1 zhPt4%CcdgJL{lcsFOIFO)-?7O^(|#Ij_Fzksw&FM)XBv$(FKv=x(G#KVnSw7RZK~H zUft<%O}Q{KlOs=I%i`HmrFW>@U###ENIm>TsD900-`*$0f|ug z)?18ze+Y@UNVg{ib`TGWX3b{1)uh4kX;WRa&V(4Sx9fJ*PXM3rFE;5AhVG02B*2X?hEeF-+$vL7*pr6gntc0Ze*;nI4$14^RW_bO7VPJ{AE@Y3cpq zmMQk}33v;gP4A_jMgYE-`Acz1zk*)Co+J;|o}~Bz;4nYZO9l36w2uk<^nmy!8EFAG zjsa;OA4nq#Kp6;I1`?Z>tz1$BLVWH3JOyb2^4@~{kt(3rr!du$L|O- zPVoQMTPTSL>@$2HOy3FvQQWZipqPV`r45Pnuy#50#_QnwsN0DaT@2s<9^Cgx#YJI! zlOG@0CuV%8^Jd2cP7W%0Sgu-?r%q0fjm}afAY!LHU#m?{g61yva04@iPb~socyCX! zn@gCddyu1p(8?01=fd`GaguLt#ye>)adQUDlY;}wGBu|=+UCYa`kNY>4G37t$&FA% zd3!{9!ieXdA{3+u_-R3bMR73|X=-C?a${kBcUegZvho$OIJR#B+c%ldN%Z#x_G8&T zk$k@pMTl4-P7DoA3=C8U3Nu8ZxrqsR27OAQE+I1`AtyUomoH67M%rDtR2uALD{{4u zi;qai%StcF&oXM&1v!yfDKWr(TVq*AN1Cxvo~((Nu;iem)0Jb(IV1T$PL9Y<(2B%o{Xl^ zoVt;w#;(c?byUk0mm{At+YsuSoWn$NEX5IO1@> z0J%Fm(9Yh8;vKVtwJjiEA_eA0j~@hfu}j`~18jm=_E2maX}M;{Q8)s&ddTol5CC~n zW!mZ#>drbk+QT$vV{1jCPzci@%BPu`IqcP7YRFWDGz**p1r!K?unFckW5P8|VHgzt zI7z7l#k=Wc^m8apz*Bfgz(Q(k@1hcz1wwI#;+M+sren(M3HhIH z#J)!i{?3?T>W9_c1pn`X9p8e1Zz0%s*tG=GOT}f=8=?0??+&Vp$z(xM56y)H{Fnj& zm>mDVyE{S_7^30r?g<`Ecs=+zlO=(5#%fcGtI6(afBRmRAD4g6oG7hwt@rSNM9#q=AvC@i~T)LPL3e zx>~D{6s08P73kDO`5DCp$%dkog5tz1Z6s=qh0Egw{5Y<^R-LFd7G+nI6;_v|>GP5b zb7J!|a%-vzYZ}svN^>h3^6R^Et6JMnk1d_sTG~24eP*k7Vy$=f%)+^gQ)_3njWxxc zE&0t2#hvX5hN3uwA-|)wVQg%8_3ZrirPkruGgoeY`SWl8<=_78U;p^WPd@)*bYZ2g ze^^)9sjVK>7+W+&?PBnrLUxSPS-VKsOqfA&MQfbD~^pU zijUQ$s56UdOG>Nj(66WNbXrMgWL}*-yEZ(lDnOaxFINd8Re~_3uTbLd?Ta7tQdVR*oa|xFPI6Zlj9?pU81nFaRS_*aPA%SrmR$8DSM<&h?2d4)K^CRW?%7_MCZhvhRqT*8o z+<0GaU_a51mBe8u__1PGUNR^9Xr6C^DqLUFWM~@F8Vu<&QE_rYewI2jKQl?66PKSG znU$|8s?1MK%~VLUk>ndJ)F@cXS6Y!{Ebknf>6u!r z?&u$1TwdSaUcY!{W$Wsh?VaU|*Ox9_oY`1!n;5Ph>MiT*Y@Hr!ot~;39I6=Vs~H|@ z92*~BKi51oSKT|jcJ0O&|M2a<{NWG(^t*rFdHSTJtyNQ8ouY58>7QvIzmRPh3`;Ew zN~x6V>XfB5Y0aH&^IMHm=LSM@EH^(<;iI@3x! z6N+1-@|&Y|4Phw-Y(5xkJKs_>MT-{Kk$KJ)s&ehr8%^i%^1xX^jmGv_h1HVoQYB z-Bi2X7&+{7if%xF@=6>~6p(j)Cv=vg12ce6?EsVl&`;Q%87_=30tEm(abI)*wzxGq zK;Ll7&}*n=k6@p%XM&~DzrzO(!4yW}55AB0;-jb(K|cWxF(^GDSwD;m;QwAKy)Wio zsE|V<`2l<^0u}x#_8EW^n3GpB)POmID4<;miPz(Hh^Y_~5vjO>8pv?C61NNNtHesn z)bt~jKP=4g17QXcprqjFF=7Y6k4PjNVjCoX(EAas?O37POyv5FE~f?-Ls%(^WJSw~UsS*JaD4+9-KONMODyQKK(XX6A>f z^D;8?vfv*N3(1y-rUvk$z1@?8g41LXIqBJHxw*2GC{zMc7sDP=UR+hzJ~%e9u)KJ7 z{ruG{7%LkWmd{;;FYoN-Yx7&@=C{sHtt~Z;57dnh^e)W|pIvR4oB$DMoE|S3=&v20 zsvTL(Y3|CdYdnAZ-WOm0{FlG|m!}_m+&jCVtLhLZ7@CKd&s}@bGrnEkKB>s44%d~a zDr<}b6Rk6whQ8&zhLQ5j9AkWBWsat(szp^hlGQm^+Iv>tx?t>>Pd9d`^v!8iy$QxH zU|*hI?2i%w>O6@&j>QddLc%yP?(On@Y-4O-IuPuA2xwq{Po#ZDog{nA+R_mnTfyiZ zpeKVbA^_bLj2_)4++7{rTwut93Csy+&`!(U&5Z?@9C2n5AOi)av=>OQI7tVdqC1}f z2(ggU1Dqsp*~31?H9Z5=(;z*@Y{(?}GR8E>6*vR`I6)#44-9w$s(%N6BFWo1SR6My zj6g*O^_1@e7<+&Fp#lI}aCrd!B)kVOXM%jl{V_CzQUeD2E=(m)5;bJHNP6*@>D@t{ zCyX9)J82n4y#F|0s(4`$h^vCcBkh&-AP0NKe}+yHT0sY_2PW5{y{#RzFqA~Iv9Uyh zUt7}P_oS`WNwoTeu!2U$qym^ZyfDX4TELh=#D2h^F%%$15b;-UK{PYbe83;5$7Wy# zG4_Hmnt{H&@fyKCJbioNJ$t2lP_&a|@lc<>2drn-mSHT9_|V`Kg`y}^gAkpD(&G9u zV@Y;qR#aqaxLlu_QmxCY%*#@Tp&bq@RU&RG(s$KXw^fuk7U^?ja+v1g{e4wD|9G}f z6hiUsZRAc4acovmV*Ge}%f|f7`pop==*ZI8NMBWD5en}I2#v8Z&G~tTgqTc8NO6p^ zJUO|yqO7N)tX7knD&(ece3N->z(1Mir{Z$re7s}0eu;_bR$(k|9xbnGG{nc|A@(&W zFfTEwu(T?xtUg6so}G{o@9zUqAr6iSo*(p!FgvRdOAA#1Ulpkcj8PS{p|Xs8&hkWBMUI;FE^d;>tC7eU!ET#bYQ4q zWU#m!@j0z|Ej`A8DMRmIdS%_@#`e2E{OXf0fByLWA1q$nsp=k2)}!Or)(4;d^!-o2 z-n{i(Rotj5u1G4cE;~I_Hhea@VKSw(tvDmSC`M7C&8+GeEF0OZIK9;{vfVauv2Ef) zvY|y)T%Xf0q^TN=%B>Qm8A6kE38}geQG}N_$I;%|2{s+ZZ4Hb-LMEFKwdtJPz!4ai z(3?cghZuSM@X3?MQBTg+<`g=fIXc)fac_#0SXK4om}=hXGJCa3%@1Gzk|kpk+63o_Y3f z6mtffyHYwO0`S@+frVBi01`~1{vPV_p9I1%OcE5}05V^Q;*ZGxJqmy?q940^U{6AY z=`h(J=uLrA0sEk4l-Dzip5*mVjfUz12vcN`POFVE(hgH|7%!&h>59=~82o9>08<_i zWiQbU(#+Ap#>vqZv!jERlcO~%tT?)$*|*IpSn#ba5U*utZw-MBG5~%}D*Qw7hvpBf z0it=aEc8Y|4?zl`>{AK=`fr9B>u>o{d9;sbjE^U97w+mLgGt}bDbCLav2Cj}ljqk~*XL)qmKN5hCc4YZV5!q7sxmWh&hzDZm<*eVDBeyjv8TP+o7z$ok6s+yXOS?L9F zvDvZl>Dk$an!1weMs0FRhD4MqV2+t~QVwVj&}U#wr*ncuiDclO-y(o)~T{OJ0* ziH(b=mp2C&S1bF53tD@#>N{2CO{rD28P#>un-|~x{Hu3=@RP^yeKvY#JH=3$R9v=j z;o^_K`N!Y>>z_aS=GRRl3t5#7G5QMaz*Ji6Y;@`A7=1gshw39`=pHmSzcjgZzj^9v z>*Uq(vk&Tr*5mY*nf1-urjg8=(~>lOs9G19rdOrr@&iQf9xN#901IFUZbI?MuoV_I z@S*(&p&3F@gxrXjetW2e))p`T0sGiv&gi7z?hK>>nI4|5-aa1w9A7TaPlPgX0sKIr zz@LSvHa3UpNx|?qCWoB~z@uM;FWuePOhhh43SeRq1{mR)Ae|1%#OX0`N4Kl#S!`XL zp@VK?9AhWbSK@#^!~q5*d88l^g#YcI3H!Kl98>Hc01?=C0I`S;hyg%=ALH}~^&p>2 zXIM9F5qVE$TE`n#j@zN^o?;(2MFSCW*Ob=bSvnXN|5xl&vf#_~7}~W4Frq$%KYkrj z8<4aBAnAb|Z-3MTL$x0#M?2K}gXl#Xjd;Pn=|uDsP;kcZfyHuw|I!{dSx7PFlmeh? zBDu>q_rah?X+C}q98gM&RRRS#1+oHd0R@_1>Sy>qO#Oew{u_UTebmnNI&l>GeoUZH z9j?%4q*oy(KPL-JzqY8bszBFZ)HjtD*XZ->^@XivC9UP9oi$bMI(+u<}yk%SsCBD@qOM ze3h1-r!Q9J=j-ZfYwN0W<)JA&c4kOm0^3L7;wW=*5Ia~4%#OzRxFrbuRWj1=C^kGi zJvKHiJR(CT9jL26e`aN4Zf)SKuuM91(8$0^-?fvMEt*&lMuc^yxLQvlMcR&B~LxlCd`#J3V%DjSDec|w# zrB8nLlRy6FUw-q4Kb^gHU*FIfQ&^{}IUQR$E6nK^>rZPmg*g#n`AO09(<@6CpLZ_Y z>73sgTHeWT?2pnK;|l6BD~45uPEo2>4zp{PJ~2hZ4G3~@bV2_&R|iM@=-68L(HV7; zD53UNB#0CSFjU$Ezi@E0LsmH;kKN_v3EAD1!|~+@@B~48u?%HDf<@9$X?R$eJWL`B z3kXK7XfB84=kK-KjE3+yCi0f@da|Xz>*r!j+InZ(5Rzsq+2 zFW8~WoA6F;n0Sbj*w1iYfUJ=cfqT$5K^_M{Jro?0mxK~Sh-G5*W3m+c_d`yAox{n& zfpC8s@5hi7hN?Is<#soZn6U{^?BhnLEdmc2_!Eu~u;U@48lYSWaY=l~@(+g~l7D@9 zz8tPEhjMm?Tu^xl{k}=%Pd_GF7zh5S|4&)~uvsXI?&m8AL=7IUKcCI!ut+yVbSdVr z&>|6)1Q8wt%4q9oV`FcPwny;PfTiO$al=Os9s)VShv4f#b4YPfs3tD%NFzeT7O@u| z+=n(ji0p;fPeS@>m40IJ`z!WIHZG~7OG;;lvAmPQ#D(dpRYe8*G<9xlOlgLuC{+!1 zU#r)3)Kv5})(^F{O!Rh-cXvVNFNlsx7mM_<(J6uaNDt=-SH~!K=U6ZIXb)F~lbzhj z4)V6x=2WDcQ=VM1I5===WBu~E^-Jg0FRZOhv^M2O$PMu^T?TzI%8>;K^bvBMT#~|J z=S#&UQRsWYNru?(&q~7Z^GWpeigI&Ox;tlzgYu*0#fdTcn23y&WVOCPRhXAkqOUI0 zLRyGpdujp&YB&e|d?H-!gHN51*jPeqC_}P9dPa&mBS9IH5}%e9l@RO4HpImZHq;Ha zG@Wi}YOk!G92>uR_4>8#t7}`=ws#&~y7Tzz{inn8YxRS3jiU=gt84R@E{?38?V4V! z8W}6>=*w;HOs%d>udJyX8ovALy-$Ap^OqlgF}Zm;3Dm5yX8FqYSKt2PPyhYDzx($; zU%LN1uclj(Q!mwZM^w!DX@>lDBPscH8f8>=T-3nWneO@9Et6L}=5O{)oGq$qj@MO2 zXlk;`hN5$8qVr3XnFSdr9+;dW5=#^k5h}S!P_B#1^>RVl2oz*U(T+B!&>qGdj$-)m zZLCkZdm!4(orB(@0lZLAP?%hdX6^C_Sy;F%JT^+MjF2dRe2G{VCRWJMu#4}{0r0`` zu`PjmY*dCxkz$o(YcYd4-iyv<$O5(>lmbA5Vjt|10tDD$;6sYpV!(_KYCwTcI4{HN zabV*6rjzukT`QKYodb#6IeD04pq!Cn-!y|*AZQMu z?`R(Y?oVcho|xzi{YHl0LRKLW{(BfF_X0Wq_)`+MduvXl9RLU~!Pd#v4k2FF78Y(!j_%Hm?vC~+ zVN^i34rKH&=014(7$4sr_L=(mw4x@G@=!BBipSBXB-a@9mD*gG@yjwZ@UR%U`P!WJ zsxqK{dZ2Hz4>fE0nu-bw;-dBOu_cKKDFIvnUutiS5$Wm}=j{%h${cLMo$V#ImU2g% zB#v))S?Tur>XogH>)Tt`FKnKhnQF+*GA71VrK%gVG7BTa^A%F$yyuF7(*>NIkN}O4 ztA?`A0AI!S2KMpc!4@YXTpVBr$QB0~;-m6%vQ-5zI2udy(#ld)8KR&Fcc)YVCnHFh zD&Q(TT_QbQ3Y8JLGO-~uO{3Em>a-a#(T0qIELC=DLT+BFHbW}uEXp6KD$SIN8+Car zr%$hpk9YSD_0O-)Z(Y9o{ME|#mAZkcqLzMrOIz>a%It+J(_2^D7gx&qMv6N7vKyM! z6_tke_A}S7z4O^;pMLe#jTg@gn_I@uo_p}_2fzI1-~H$R{=eV<`+wef^~1dS5k+nX zH>uVsy22^C)<3sD!Pu@&&(6rmuOD8onY`RMbFF!FMPH;(%hDwkw&c`|Yiq}%bIN0K z3S-l?8M%dVNl9{qqQ72`qll*O4cJM9a^6y6Ye3$Q=O6j8h z)thgiGB)aFr-e!CiVE8*%j*jBn~jFr0$p8!uE|)`-&8+8JOu26^-p$n_SRH2=yk1y z`Ne466)aTq{S#Q8(H<^f&G9}S;M5=j7!Uwt_Es^TuFVDc7uQy=UDyQnZ)~5xwzWCe z-`iP`4~t(vHYD z>5S^C>fzCj$%XEjwYrJfmWidV`L){NiIV>Q`tgyTh1vV>zWU;uZ=QYh;qc1Z?U(P} zeg4ijzx?%||L6bx>wo;^#V233jIPJ#cd=v2oTH4+$`WB_eQaq%I+}%d4)m>EZJN7W zKXxHAyC5noR9oDTS2vT}IG0&D6qQ*li9?Iu^t24EGB#EwA&uD3t|dYost5@Q3l2hq zT>u{~HeBpcrPUlRB5Ny46x`NY)*=OUm%^4g{0MY#YVlwFz* z0DlaEtUUuh<@l5cQ20~Xp4zU#$Y8jJG5-jHUy$R%U>UeKB^Dtj4&h1gW?S0Y9zS`K zvzvf8QO>?BJvXR7GfS`B&gk5K!lJod>>?%&*ov}xoi$d zFBc7?g9I#|9~z|#CBbaIzrTRPM)wYX9}h2F8Yx@ghUjWEQ~-@+qI!Hiy@Cbkuq;HY z?g+6cRv}M{QKD=wMtWkLN*O625Qy|0tD+Kjo6G4jLK2fmM598B#y6wpSk zEkCp4VmBvka(rh^Wlw!AEcs1kCB1bu0RCuK`_kmt!r1V5PuEm$&*|pI?&`A2w3IAK zs3s&JBP2jA@K5yf22R1O!L=cEL)urmIfXmff?1cS5-zVU-?@4T*uQ#y6Y#%yW_hBm zrA3$5T~tu7QA63UN{-J67GwnpfO>omJ|sUZG*1)+0RYqh9~)+6;kW+{{;`8eT%Xbu zRi2?JFko!c>Fl9i( zKTafk;te+^A#B7IjsfhWKOh`upb6#{7LW?y2uCCwiH2e50Hq<20pJRO*$@=KWKzI2 zKp4PGWqzRE#QNz0NP)@Vk9)_!jS!W8&xHrS2bwgjF?l_rpV=9)rh% zpv49JG5lF5cH_?t6mkOiypRBKl!6x=;3wd4P<)xocK2{WezLc(w~w#4r-!S(HT?L* zmj?;o-O-5_@(l|NME72$Gz_i!P^&)??ct;nQD}%AB0kVH=-A<7Z@h;7+OYW1@E*kW zn9A-UOVhE2e8ef~6ULGG^92uTy zZ>`VG(u4%61zZH`0Q;#z4uo?k>}h;Xx`3+*B%HcZzhjPZjd#db=N7y8`%M zy|8)f(uJEBHqRqGzPl5meqneRP@f|XN(*249!$mdxl6_Ufn)4xB29~AAIzqpFaQK zhY#NWVDZwmfyL$C`KkHs^P?MUU5j%g8>`dXTR5J*aK3+ev3_J|hwp#! z&DWp*?CW=b_@iI`_IH2y^M66+|Itssy>|blx}=w#RK-rJV5xE=4TU)^Wi_We7A{^u z*Pf=SjiRdda7APigqWW7g0_X+n!%*P8vn4UAgNNN&Phy84U$K3V=_1~xxr$&Bsfrt zc8da@yR(y%4HM$N7YRe5geV@ggz@(AlAu*^gj}kS!JtP*xLl!BN)!sdfEbv-?y&=b zdcYqu_92Q^VPLOPAs^tSK&G-Qqw?gp7kA)c=FF&lOFWXNL6evWq^e_o0 zKtQk`$b}gY9vN3RXP5@u+}%(q5riEA5+Pg|)^<<>SZ?k?98QE-4D3TJL_0reNMMMN zCljORjIV{o2^%yEJ$&FKynD3W1|s>H0=&rCM=vdy`dtp}^F+n$6DQ&V0?V}dEfv-M z%}r47N083m+C0(IwLCGlzA(E!KeIA1Haj@bX)M;tr6lyu-&f7^PhlkUL`eH=Z(v^| z_L%fdi(9C=kMJ7@i$+8{KXehV9NK6HL}j{U57`JJ9U-8)yW7~!$+Z& zGuR|>Mcoi!ACf+90aeA|;D+-8yq-qEnTP;T4>_L>+6^2b?HG2bnd{+mX^GsM1gYn2ZBbSri!uq?-T)0E+QFA%9m4 z_*?kzdGo#4SVi8@P+A*>kE@2 zXe++b)!CU5?W`R^>K*Le935Qk9S|smU?4;Uqg0o`mj!C!hgt#-wvM*8LOv&4O8hy9 z*@oNC1~DEdj@TYKXn*(sdT+tG4;R1vfj8|B?L*GK6Uu5GIVAP+&}%X)N@`nc+lD*Z zse(V*-!n7NhvT)`=`+(43&TSLbu|T15o!SkYX08vOBMKQz_lU&2MIER1=*rNJQeTl zp5W~s@9Bn79IHG(H~Z}Ntvi=4KE8G1?FV-sUB5ch(N+Li-NOZ7Z^}$7ijo&c%8R2T z$`ayAVxk}kr1JeB@B{k=GBG~0G%l(nHc~4U<%ohn4YK4?1RJ16Zi7bMkdC~V*!&1- zg0ELaa^gg5b7M_=MQvYYRozH?>y_1&E1Mh3lT(+@UwQWG{bz5#H#|9~GnA!B!=s_> z2l7xJx2aH@qg3UU_trNLZ*N^Wf9=89s}HU}ef99&5AQsEzIO4#{P~U3XBInVC+a6) z>uYPC8lT+U7+hX#pPHCFx4wD%=7sxrKK}BHOLy-rZeRM@FMj#w|N7tG{ozkv|LQlN z{^Z-C#JTgWlMD633k8kC=@os3u0>tbSZZl&N@10{ zpn@fkhA83{Dz#J*E)NM&O2omksFU^%Nb^84o|B`CPl(bYG~LoK+z|owh?qtBdWHhP znhm~YcI+57{7DN7QCO%%CJqq?2f~rh<#GHte4^m8Sys8o@SjZ6nqW z^|hR#d0Sf}&4|RUQbnCHj~z8LJC2fsl+i=M2N3{S z0QPYPl);iDv?Ddq(llrov$YMf6wN>ZpgW+F1``>(TMq{mz|#CAH8WDWL}!QrgorW4 zh#=lj^&htnzb8;nX&vSM^q~p+I0-i$){_pbq-jZU3|PuCn#@LYH2)7v}uu3dR@`{t`h_n+O_K`dW!gaUfH zPAYE5NCoN0mx>Cb6b)G!6^Zd#p@At}Ul{rTf1J-_zM?cH5+=cXnFJr05ddQ#CnHc$ z935VyRAA`f>GSbGg1~4;kG7~GQCrr2`tx-8zoZY$p^rMf~uHP6wv-ZueVC(zefBn;c z{OWgq_{q1wnLB?^*ED>3{c`ifc<1~?-FScX$Z+4%T2=p8$IRK%?zyVLwYt$weal!* zWqWRUb3#_J2S*~1M9bomM4_Uvpy06JV7Wvr2~ z?t@-D;CKXC$kjo(C=y-{9YSqbUxKUp7?#;9q{YAdCD~Q(WQRMtiIlkI~NwB3+f!JF0sm&;}X-=(MqLE5-AA{ zmxP7}@?9Kltt`#q3q?BX2c{MFg1_1S48+7x8=H?+2Csdd%4g_lk(lKzLm4*WN`EoH1 zz@2dtW;(S$OU3d8{IddhLsexDZd^f=niu!)0Q+y>zcbn1+LND`CkZKuk2c0eWeRyk z;nIqv_*QMMF*aHgBv9?y4`By@Qh?7dKn9;O+!(7g#zYm!Bt*Xm66S~kvO5cK72@F% z;p&8B;SzOHVM1I>zJ77_!s6!5TTkD4{?UgB^4Y$7@9fTv?fW}(+Z#O#lP!}YrQL1i zy&cs9z515MhLNGutIK^0vy%S4+^ZP&i_s_ol?YW(2 zeG6Nc9=*Kw_}R6`kLE6Jcg(FcPb@SH&-O1}>X_MXpWbes*sAQFsqP#%)^tq0pvl-Fevuvi!lTKID_aHq$;C~fd!oUBxBJA!LcZ5gxF>bGk6V&Z4gN^uqN&! z<#bRg6H&5n?mPI_{$q!Zpkx8G0LUctE?~f9;uBDRXV>e;(1G1Bepo*kDTu^8K*#s< zCcV7rF%}IJ5;Im6%ZSCqz=DI!;v}UB06Se|`di>-WDruo@niXUdwN2B!NroiEh}s4 zlE9KW*xR|<*xA`)vsSW;W>>CW{pqIoG*z})~L~^R-6vI zAChr=y%N3M(|LZKg?Ts6pL^%o69E7D-P_M@-I(p^m}sgUsjX};$jcK4=Y|MN<+2#FBLz1`v8PZRiqr$e;R%0!i_c#N79l`%3LfqaM^$`u8r5$(V)mFuSs z59_V2o|;?BGS*3w;`18IEgg`J=otoIo2`;li%XvrR}P|;r6NdZ-4XKKmFw||NduS|G$3n?QieB`oZMJ z&i1_*Z-4UnllMNp_T=p|*B+c++&sO!HN1MYclm0^^mhI5TEp;4+t6G|b!U(y&L=2B z5~B`+<%Yuv;_*TQgwl}Uu#jL;pnx43>7eOo9A4O%9|{Z-n4LTY%z%@ki8j<&$g!kg zFH@`=y!ACSef!?`5EH`oga%4bZ`yc3H{Fmpz}^G)gLr}1lMEXote;8+06s-J#0Eca zz@OOqm@~wg#aQ!5gdy~L6ZVO;PsM&xwcZ&yE9S?{P#$3aZY@BL4dLLQ_4W(f2&g{NP?mS z*63IOTQ~9=ERf|w-UYe=9RaVzOtcNS1DKLpD60c<1W5@s@Zki6_Jr(4dPgJ8iwxjXnt+q^JeD0#(U~6b*gS6!Zvfu{oj8~- zOQb0edUL?Hfo8uX-`B#?H^4&>R%^^QYSYn>jw6namPf(PkMIu!fncDGFPiy=1q(xi zg%HK8>`q!cnAT+?xeku(V@J>yGg%beURmB#Thm%vd~SYb zd;JWId&t27(+Bn^JKE}VGHWzxdS$py8mg6sKnKqY3(>B3-c?i#IKz-VbG=%GD z1iUol;(_mbxuy7dx8-Ht+S+*k?dLBa-G6lR+TDv6rn)=MPL5qYvp7^$o+0EFDkRmZ zs==!AiRQ-2q(ovZ^mK*HkN_(pB>w;|qzmx<^7xqA)a1^Byo&f3eYmVFA*LM7b|AE~ zJdM#2&DyNfE$yc#S5oqfG3b#oH+b#U)5jmb*EuzkNvd$yH200nuAe`1{(4*gd~H={ zQ9@!`umE)f)G#N?!m9O^`kMag-kH9!h5o_e@s)+1`T3;_=P&Kt+PZn0B>P;ux_;y8 z%(=4|b6cCU+vnS-Cwk^)`)6lb#z%W*=Ziag+NY+!{mrlc?N5LH=imSPKmO*AAAk9a zn@>M#89O_?a{j@~_n*A?>4SGaz4hXw&6^LWHZG5zz0$Y1)jp17frZL}rMA=ag{6)B z(C7d~VyG+%QJ?5DAmnm_h4AMJf(87b06}O#kY`$rYr(u@K+FlWem`MG@;so-zVQYu zX23htWbjW~6^4QS_weUv<3KqPb{%E$bVFh{Vwd76x*_SF#8Vh__kxRIt0RjIe2wgL z#=!>&V1O0UC_ZY?+mrK&-OLrSw6+dF9%1w-Hfb{8L*{j1q#4WtI7TWQq5dR=7fLJ=LA>fY~!b1lSBTXK4P;on;X5jn4K5moB|5Swm z^#e(ykk_6PI15elhNCWfdF$Yaoh2$=mbB%uKUj-88*mE%bp zCkrP}8+VR_v$qohnw`DuoP0gmEMLBzv(VNyKOqLL{Jw^|=Ay#6)5DjyF0M^YE({GU z4iBx2k1d}b1V}se1;#j~o|xrD03)!kl>{RW2LobxMN%A`YrQ3gx^#7RXb@z603Q%e z^mcR@qF0w_JJ=5MYQeRi0N`T7% zd>_I5a328sDSSUD6eTec#;EWvy>6OXr7A&s63bbP*95qTqsr_`g5M_5y88D^~%h~+4h;Kx{+aBYyIe%Gtl>c{rf-s!>|73N8kMR>cjU(&s;UMj1MlJ zyZ_?7N3TA8@Xn_XKKQ|nr?2KNTpL)~>X|!J+dq@lG@aMERM|D5(Hesl$)c!av>Ook z`*GQ@`@;O=i{k(;N67IH=5d5UA-0PA!`4m^Wj)>9y*)f2+aiA-YV7OZ|Na}Vy@mtO z0Wemm`~>;m`=0qxGhi8eESMh@A_xZjclR43`D1fpPh!RZhoS=;l`8mjpF_L{_F?d+ z>uGiyECWD2MKlrbEl_%d=mnqv2y;GRM)Da!K=5h6 z{>hVg3fMnt4o8S3M1kW7l_$Nk%}?MO;8uWjlvXK5M!?|%2N4_><^G*})IBo7Hz zgoZ?hi3#>4p%DI+a)~z^d7Jh&_GS(aW*%Nv+z=mjfR7u?$=TnPY z{p{Ax-AfnG4E9$gB{pWIcb6DeM~7y*I(m!-eS|ET?WOYdA|XQ@mKxZXir@%n)TE8o z*T8bvS6qbnz03fwG8%8)#T=ts1o&25jipO`jWa-Lv4nxrYJ*SnVMRksczThjCA#GUEX=} z?guYE`t-)bXVYsNOBc4UJbbY8;`zfDudd&Hc=_J#doQ2gdir?h$>Xh^o4pIO<^8RN z?R87r7r*-Xx8MBoH(!49PcJ|D+0uo(Ref{7{@l4sk6(Rs_t_^mpMP}sy^psaJ{exy z>YHC}8=uQ*8;h!%&T5`DwDf1{N@NMSK@wC^^+U850=@lN&?sU0_3-oYW=H`bj9PG> z;F~A_A>eQWc?i_;M+`XBRY?0Mjvj?h3oZuD8cddAAMq~U?q1k$qJU6fAL9LYSv)o# z<^Gtl1F3Wh;8UiL%}mVuOvXB?O@XSL%syv!I{`il06-^^ol{`d;(!$A2I-tehhpWjfLQwL$?v81Qmz?fPV;L02z$YkI(@yddB#V z8U;`a%+a*w1cZ48=_iQpPf5Y?6Yu~Y!oY_B{$Tx}1{jbBkm+s#g&L(UPC~Fa%1B5@ zDLKOBVciJXWa5EA5g@mmGPgwN5wdHT;zfx4BgPFddQd`ofVd_q2m$zXfLdZ;B`Fo9 zoEScSA(_I*$zR@j$W>AzFuC~0}%f}`2+cUA(!tFmh7O^ z35Al--~dE;!WJYA4weK2g!1_!fj|}Vj2M7iO|GjCk^@oQ>$sDE32lovbMIqrn+isa_-vY>syOUoAa|U^eqnd&vmv9 zSC>^JCzQrT7e^{!a@R*HjB!yYqGgBY>Z4DtKX|sVc^NhRHm+W~`Q*{v$7s@dfA!Mk&70Sr zfAqoA_ujq#^2MbGcZXIM3RdL3jo&D&u&p-M6%lAJ0^5Hw5Ub*w4cXp$7baC_g z&h4k~UAX=3*}JbcA3UEvf2C(`scU*(-!T?!=nK(yq*rv7G<0U`%EDqbLIme=u~%UE z^#$a8y*zz9+`Zl1yxd$_o*v|(rw9C6b~ZM?KHhi%PU2a_c)OzxhXYbq?C~t5V9*D^ zACf<2$g$XLK>gow9Lnpl{pbz^@Ua8G%k=3^hM&DEumK+#+|`67(LbHK>=`l>^Pi&hz2~o3;&a-WP;Mf=v-)H zWovD23H{j|K|xTEU`Mb;`9f=J5GyJ&(Yt^Gh3H~9B2g6tLO6&U7S`R_g<3c%{25h< z7(lQ%gh(KF+9A6x~>7pc{)!u}^^yB0%{*eF~0yvm?hXj-P;k0&v9->h121 zlnwmEgbVnv{Cs?USUg{Ee3sBVIK)rH_Y7uxDnx-AwJIw!Jxi;}&}H`b58t@9 zb7kY)=HlGB*_pMmk>$RwUZWn^uSkq5QAQMo%Sz&++w=1(Qs`|JA)aH_mN9@t8I;l|y2FJ3%;_tm49 zFK+Ja?A*JzdimnYr3)`V{rJ_#AK!ZR1fBK<7Uxt(ojf&eokCxa`uPH)~F+PvPoynz%UZTm=IZo5xI zLI6CGW=U}Do8XPuX4rTpgC5;=n8BVfV_>gR?3<*0Y-1__V8-@_@PO!YWX3_TH-jRM z*!#Vjncu}bAWsLNL_~eY;{ag<*8>?Ftd>C>Vbes^-@`tf@=nf<#5Io+77Y9$t^@u6 zFR=f2!v9J0;}8#keM?5Kha(T%AG7&M==BJELN)T;nZO^C|EUwGaEIt8XE$dX5(;f@ z2BqH|xjsh_IAmjs!nZhOhLs}RpCMwD`-1{N0DvWs&M-;9nrd%j>*nMP5r~n9i0K0Z zen32=$&Xk!3VaG`tm=1x4+@Nd9|AuIv+`UX8-Z2+2zCN7gF^(#_?E{{Se-nHPU~zR z9}spp_ug9r#PLV|D?&;v}Vduy96o}Y9{w{88#~?8~H9aCSIyf{uG$Kivl9ilWoUJP@sHv&j zT3x@sxwW-0i{!hr6JyH*z3utA6{@(Z#JIAUNU&PS>SJ{^-6h36CB=x(uSrW8s3^a( zx_b92u(3GSPy<_BQG~oMMK#^raCv3v?S~IOdi6H6eu(`~Z{2u)XXp9tog3%RZ7s}9 zG}c{STe)}X!hA>j|EKFc*s8kL_3vx%Bzvc6>;)0&y;r3w2vVgeRf4Kst_TFpM7*kAQO|sAbYrMZPhjKjUT<_Xr&rFsJ)|zvT`+4rCljL8YoRG8Ae}}12 zkf~whM(aJ!_N4p)f6mDWD&7@#h??T!{F;=+ypW*8t%Uzv@^%I#ZrhUT<53tMN{LWq zdS>yys)oky-jSK6{{D`!f&8jcdf=z#HP%)&)#qo`<)-ZojgCvMij2>VOWsTKfU@G^ z>T0_6^voVVdw%}f*{SKNq2ZIK=I=g!^8DqmPk;XD+P%9Y)5nI69h*FLa(r&~=J$_p zJbE~L>HNgG`Ks=Y(ClRQnDBy@rm<5eCr{4xj7?m!S1Dqe^0e>8B z6!?vQ(3!)fHs{2AuIrK9fhmQ@YD(OA8s%0 zebl`W8;g+!i&n2%&ZLT!E0@u?S}AK-iHi@a4+p@-htLp~K2|@R0U@wr$!e8V>(tQr zt8w8`LV(bK{|ggf)iRN6L>in5e{O|jN__H9nZHCm@Ry4pS(pQ~*HU3R)_M&Q3KZFZ zA9@^dyAYKU?@|KD`^=q}>RuiJzT^PbW|mf_=7b3lKwLku8w`@Kh+v2Zm!b0Q3X!gf zU{}ib=&7iMM!*bYnHd@&0a*Bj*fxZOgcabhiF!>#?}&A~+x3I8_hqDS^KsMAqdlXl zj?Ow$D{T`q4Ly_fn;eZ@oJ|9~tX$l!w(fMJKNl}8t1YW;tUWS3baMLmR8RLx^^T$f*4TiRAzxxXeUzbK`!wYajoJSB%{LVi+i zUg@FC!s@(z#SOJp&9!y=oA*zTO`JS^@#4jsu=_I?&R@EDxWZn$I{wo^Sh=h`X*y5+uh?Ux5U+3?kaQLQ;<4K zsAuLt@&&pbMK5%{Xu3l-tWfJLPBhi%;iImmZMw$JZ?%z)j+Pd$hK@o?i3_7R7~G7a z2m26yfKMcy2Ue}6iQt@U#Rb%J!v18hO6L`3tY}h4QwUL!YM_S?Z=EWRb4V}krG*Kv z)W@M2`LjF#w}Nl13gOBVieCAdbFq{O)I#`t`K55ZVcIM1HMF?G%tgFj`rc8#_~^t; zL7G@DUq-5qumaG;NCt9yK9uPT8((sGNR8-hvl1OCECr6rm?7+a0eOjiK^PR^gK_8q zl!yk!bh?z~LoyH`>SYXp6c+Y6<`=+BxKUh!#QuWeFYzvqk_q71kVe6xufJWs_&Z#4 zWCs!QP;3Em{6v&rk*I?CBVf_*Kpuk&2_Wx3M?nTm@YqmgVTI>JP@WB%tIXV1n7O-p zd9_q#`vwH9^h{UvNH(*ywZgKglE@Oql}wiM5;Dc&A=!9nG_W12G4`y1=WJ36{5_6;{x z9X;H2dUE{C(aE`qF-|TXpE)+rH`&v5a`ec}^Jf*{Uwd-z&f_~bAKbcr=gP%%Gn1!| z3?3isyLoQ@7PG34ADtWOp6F;EXsjfMpGw|cHkRmp7P!X2xYyG?H)Kb9Y4Oqio{65$ z-s;Ml#DvzItn}^P?kUe_di>QBLW>jD7vZ znYEQEX{}|I2M-Q)9v(eBIMi|AKzn1;X#eop(`U|Jym9{O?dx~$-2ML1vnNk~`swFi ze|z=p#V?m{-7Mp4o)j0hhS4Epc`B5tWX+DDz+3?byn^P*&1!e1UW$nw%qr|ITW4a=_<}w`VpbxSwkgp`tkeACQl)#r8k|UcFj*9OZ)q??xEsy(7buD@E7!Pal#fh<3 z!fOwA5G8U8g>w9H`2hH6_J1mC;pCq}--`#L=c{jso_$N$io84EkA)AmC31m3+#B#p zAwYC~mC)e=^$X^}f)G&j{?`}+j35=7T7f*j!pRyzb`T~X;lKPloCYNJ;1Hm7A^IJj zz*12IVWOKE2>>j8105qADEi!2HHA5dd;_EwqK&&I$v+ZLcr%nGq`|_%A%I07*Oy<> zJRCOz!8j5Hv5yd^0{$N(``mM+F7W3lzXJZ1Cj|{Nw}$g$N32F@uh&)C9J6&#_WbPO zBZq1?g(YeErWiSFvoFC93#^BS;T-jE;$(zLCD3xsf5y8|v%f z8+6%dx5?6yJI zcCh|%d*?Bd+xrK`4s`(DqrF{i`8hd3+f%l=CAd0cdZ%yqDT|71&dL~UX&UY5IMUMa z;2cbT_RRG7#S_QL;kkX`+`WqzE}xj4KRR{m{JBRrZalej=fTaJPwwA)eDBV~JGXHC zothXO={Ugg-j&NYuU$SrfAY++kst4!{ps%9Oh-*-kYB9hMyh)NHHrO58|)4~#z0g3 zWY3|oLk9<&8d@?kT61%V3wo;`W;{K*eL z{`AWaFMfIa!;e>P-Mw(**70-aPG7lv@%GKx3uospUAS=j*1c!X&Rn_NGd0#aIMh2n zF?M|N(8N&7K+n|qbN$oDYP$yM#TQpLx-+jorG6}_>8MjulW$hb*0`EYVMWo!?aBM< z!8HV5%RkD)O2w6BJFub8@7*`FB-aleZF)ZV!kB7v%SIW zN`1S<8%*(HFX(-YeYP*@tbmujOW2KOy*6U|;bIcnpM5CO$@H=1b;up8+^0moA0PfraW0k6g>$ifQ2z-l0wT%k;|QD z0etCt#gRhL=Hxz#?QaU1QNapaolQ-G_lqCzR|5)(rMLjzP2 zUVS}MJxR^g*uX3u+VNA#YFAjYgE%LteW_=}BB zMjU=UWvHA|aH}96q$>}8fC&H@`K)}BX9%0|7gnNn2@I?yy#C#CDoklXC`19Cdco;A z;;RsPUwH;Hu#7Z3h3%6EgquMS130|0WYP{tY#Y1?{4TGTe}`A1{ZaJ7|G$ijTFRn@ zDQcY&rp)Y7Q5CUJ6-DVw?38gIJpcIh@MlOq^MA3=#mj{dz%GyrG9Z_r0ip;0 zSK?1FW|RMYgQIc@3Z2NE@|Ec?5CBlmf&(B8wA8hTZZb-N$_G4{-z*kFjXGy~F#i$!Du59sZ0>48)Uqrj{EV15_M$nBcP&4ZsXZ3qb-n*w{iC5U$)# zCNh~A=^HT&X~POlLlq|vo4AC~)TCX0TdiZ_17j0|;u3eJX6&u1svhncI5~al#)V6B zlanKdI*#^qjUH$%Vq}l!R>jRo;-MP zOGNa5|NU!Mq5Ai)Uw?4@I@o{m;Qq76k2pSk_~7Zo`;TtlxCK2xkzPD^YJ9Y-v3{Vl zb82Lyt)il>qBt)yG{(^mG7tQDyEI?V{IHO={Cu(qhMO7&o5dPvZs@J9ASjO?0IE+2 zQWW<^gyiiC+n1D8T-w%n@L+LELsDUWX)QH0<5Sb8PaL109UDJB)IZ+WN1y&feWM+{ z1ARkdCr+Qga^v>H$KOAC`1t0{J6CVryLjW?jeAe-J$lBv`|!!br$3y#ac$t}#Q2G0 zr!Jkp@$mlb$B*W(UMADmU`twQ-rkZz@2IHAEKX`t%bQb5x?&3Ycjb=wWb|3Z z?B5vQ?vvAPA6*ew%oL9y29EedXFIw3n~KIbdZxf#lv&`kTBmJlZ)}XWUl`rE^Z`CO zT@w3R>SUf{?rCG`FV!~l3<|k2c_1Vtc*#1Q<;&MBSB!mR0*YRuUM9nEPCQ*xcn`Gb z?kF7Mq|h!VDGrmFzSXqeFoYDCn46GTK>fJj$@KdrId;uT5m#d={%*zQ|DU;f3idxp z0YzPc9SMAnhyyqSDpl0lP*tkB1_vD#2rv|mD0&z>u*C`x#=er5D}Th~m!UnfZ$LEi z0b5=u@nwtA=J0kvP4^D4FUbSPk0&DNPj1|@W#siOK@^hw!@CICLZRb2-ew7wzNnp1 zls-pOF4;hrf*>vv;DUM~vXnX_G42G$uh;+*`TPixVJUoy%tggIps6TAKmeaVARdYU z5cx}pfW>4pe!EDd46RsAX|SQLfwifnodw9W#@C0PrH|q^)YCUGP%+uO#>_+45ceNF zIpOK#;PcT$GzY>D%vq3sQv(cq3^r36{0dq+%T}m-y^>)|nzr-Lt_(yva{nWN~#CD298b6 z-ne+>`i0B0qhsTp?c*H>N@8}UdAY;GiFr?^Hs=ECLccOE`|_~8EO>7!%)1Lx-H-Sz11{b$#%-93BZ`uWSZ z&Rn@RFgXeE=dN5FKRJ8$##L08|ha{v#q-E_)%Gi@v7F$>vS6CTe z&_WkJw}@sv-wLxG`*xKKcqBE2X16wXk5{%G-X53l;u}U{w#bgdRj;Z_WM}EJm8S1%*nwstM3S%%t>*yk^x zi0TLOlHr4KN%*DGl+R=JOYEakMbo)e035!}U^!~;h_^pA|<9&rhLek1^x2gQJ0vK_9Mh4h2}$R%&GI3fy^ zhl2b6-vi=PDNkO6XM&&}795IOwBXayW&>!^ozK8}oq?6Ej)tBNE?g1hT`=$^)raRp z5iMx4YGPzT*~|M&0Ty!!p+Pd_|=eE;sX zOE)f_yLbJ{!&^6=-n;k1g9of1A3b{U`0?|H4>^B!|Ni}J*XZdne(2zVylisz=;B4? zKF&SXE^>yI+@6H3o74Tbm&8Q3=4R8Ct1%;eq^)JLyK}s|1ABxhWcJRWtUy0_NJ&&g z$?nL?#5i1$DKQz1?Zc-LEtk&E%+F7rAZ_9N>9benPM)2aJ~29Vd~p2ep~FL6eZ!|t zpSyDT>cdBmF+h6jD#m+zuAIAg=jPozx9(lOaP8#Wh0)2A`wtIw9~qfGb@IsMcw^^5 zT>7m8eW@h{eo-OLz8)_A+x>ROhbCl3rRT-u=IzSPOem~K$Zc>7$=7jA)pt!yDn5`` z+rKBjwYhhyy>EO^PL)eQEYACNt4L-8)Eidfu2$WUmzhvr+qfY(Pup@67C%F@(EC7M z^lw>9V=k2s5@7v$rtGNNZeL>Q`|Z*d?5Gdq6V)TOtKj)Sp8OfGz>&o>>|uVykVmt_ z7ikbJHisoKU(q7c6t9z&$p(#eD$My_L-c&<+O?DpFI%&E6*5Ig4Jh%VZ_wj%eerMM z`oqj$SOPH0L#X~QzW4{&Ct2>>uQA;fy)QiYXklqhfJ7xIFEl>SkdUJ+LGPvIE+Nma zAP_iydh$_XtOD0O&+8@RxU@vU%7Aupx>fMPp)Z)eUPG>shV0 zbuvy!jLOVNNJ-lho3JY_Go_@os;=hXfw}`zw4j`sJv)2i)a2yM;NVDmTX$tyTR~Q; zpJ$Sr3#vcC#Uawxa<`M+p@QURH_yI(`x@;3_19m2{>6Xay?gz~Pfx$UedQtx>_54C z7x)8q7AMdEp5b`s%9RVpX4(q#YmyW9=Vhk{_=v)Mr7oB{UnBr&!@MI(zd9)q?4$SF z@^dlvC%ZZ(x;syd4uSp59f9e7UU(o2!h+!^m5K4iQDJEj(QVB=XD(d2@%Zk=yEn&= z9~+sRnE>@;(;Ynn`wugHySJvft^H8%?6KKfSFb&~ckgI-S9@_@cSGH=k&(-1&fmRt z|Hid@u!W(q<0I3vBgc>Rj18Aom&awL#ujAlNQ!q1^0xAJatQF;77^yXYj+6g0>wpv ziBaL{Npbn5K9M5^`ezOunK{ruk-o1nFgjgd-)aqp@7lHM>cndd z$kSG}^DC<dSI?5W(gLP5cP<*{Fa*-#`~57jADxsQ{$Y&XNCJNYfj>hbLH)>6fTL4c1(PGS@M z@{Gj3#5>?cSaAfl{41bfae|cKDB;YJzs8Y2%PZyL*M*26j-dRLILEIDHz1TC+ydY6 zzQpy+E$Em*+B8iwO)M--tr=lpXQQpBgNp$Ga~<+hq^}R`OTGUo@IQq=|0*mjfqi9( zbY+2US)wS625uGINVp~v6qoWbQ2UEq-#rdiF4j!wmy;Te>p*d?HYp@~QKDZ8- zEalEBQ5#iEC~`AI1oPXk$l}scU8bwI!rH|+Dk?ZPDYh^pF)=Q7Z%RgS2_35sb+q<$ zH?&L-ja)l>{??^S=VoV*4G+x>^iB63YR=9mAeYa>O~OCM&axyTbmBn$^M`l-`s?l6 z_wU}kd-qD1rfSG~A#k+Pj zWTx*g$R$0mAbckcxBxQ2!~}PjBu`hcpA{5P5)&nydztCP_4XI#W9*N09X!tLkB;{I z@DN^%T|m6AC@KsmWNvh5Wp-wN=iu4%m##m!dGYS`Bhyp;!z10jgDnRR71uQs);CnO zv{%%%bar-6kB!~BeC6ToJA2KLA{c;fFzrcLAOdl*>_7tS)Hh?gM=eY7JrL< z3-&>`)czb<68^Zzu?67#yoP{2*vG&JeL$5$dr*%zUT9$yx>%r9l-qo_R8SN?Gk^ur zq9@PV)hft4DsypCtY_jb?YZC$0`dp}K+OX0QesFsfY}bs5EJf{%03}6l%5{o8ZeOE zN1JTq9;jCYAn>Pksilpb4TcZDOPCMl0`?*Nz*!(yMEEfNg_Bc49>Om*zC14Qe{Qn0 zu;ktT)Y|&^Aw|{!5el^bx>YMe1AGme4 z9!bho(^gl5_LIy>gO(-Bk;`f;SFIH{0*~y6j;%B>5*8H;p9)@F;i#nsf}SXgXkzx& zKk4c4**{3NT(x$knI$PnD>k{9CZJr#q1aQNq77fbt)D>>n&C80~C7+Sg00ft*8XilgZ8 zX={-l=v$K#Uz?oRT2wr6sCRM--`xE9>zBu-CVLMLHZh8~et&6AeQ8bIzViCw!pfGq z#zP0%ub#hf@7lGwv5}6llA67-UF8K$#bq_6b(z^!safUuW%b#mg&Czesm1wWsY!uR zq23WYoP&2b?+kMA_cw9#*h@p;lXJ(;oul$Mr?SE|IMB|=&%({$#x*#zsJ^tJtF*Bv zzj0`1YQ1k*nu( zL(pd=0Ol!CIkwSvD)2?267gpXMF(Yu39$VD5eSlmpS>q{CO&+p&5jJ{v3J_Y2oFcc zjdb;=%bSIj*%~ELR=}Si__|eM$m>c%ek;g$MUk*$YQYzxmQ`;GRtmX;R6yNdwUl}TJeU+%V=8g@;g2I5-zA7G?hEdc3E&QYc(d;4iU{mk;LzUj>O?VE;1`F}@(_;)_)( zE6mKa%`DWDQX*O!YCD>n(!)ddhJ+VqH4x(ys`J940{zqUHo#c{j74u!*1{qNr5R1zE2QCdh0qPwc{NcZrO zvFVxP$H#|8TN~S|Yg+0mYidd=tIHcRQ}R;wB*le*bwQR&ngy`Kx#D zoV|XneP|H=?-bwSB(bQp{rsAv;(3VR|0FEMe7p#lt{~P#9uABfrdy=gP zF62Ix+mfvd_G#q@_MI8i;pXhL*}=_g>n7(-B)mZrNem(cj~9*~SK)bF#UO7{TZd~M zC)>idm%E*)A9!a;`2Sz*gCOWHDp=wX0w4^3A*Vn)IZ48^2U?;=_NIoMe zglF>u@TCv{{yal;0H?iJ>lw45CNEIZ8}0{x5DNv4@!>Bc4S5T6;PRzRy1`LSkt9`8 zCYbqzJS`#rX2S5dvM{%@K%TA`*!B4NCDJ9j<-O&`%7tZ5b+XcA zfT#ps3&FUQqmaOyE8YgOj*)CjRoAUD#?44L-qPCMicbBOR*Ih$KOR0hs&|RtBLqZ_ z2iZILe6)1d;|A0=)X)_9+d7&WIREhk;`kNPb;%OKm?D0nERuQH8?ffkQo1j1Y-spUeIrdR68(MhVs^C_mvl9@w>KQ@s6SAj zUs98keWbmWyq%lp&XRk7tiR_-b0e|!lIV!6?R1g02{t#1-QoftI5pb;+s{8h`QJnS z-@bW^5%B)K0)Jr!AP7GG@c!-VKmL68_HRssx3Ay)_Wb+X7cU;09B*&!Z))h^1BHj> z1@A2067(=WXRn^Wc;)nk3n$K8=pUbG?(8UR ztjn*dD{E>UJUVmfCTYI+uim~jJTsk9UbeTaq@c0ZJ1WA~+s`#5);l_X((f4?A=}09^^t$6w6#=M)1cKF9h}H*7mjLKo};kW4=VnH1W;MO ze7z>j7_uiM0~(YxaMAn@$DD}q$z943v;tL*RyLNK-JD%Lwrp`1o%}a@xO#5)CaZ5H z(fm~e!Aa?3;w!;-G2@vh6?eKYmx-6L+Z81*0{sdKgo9hcUmS&D3@sK9I5RtqBNQ53 z18U3!0@q8F+IbRemWi41i`lP2BZF2Ym4_4J`d}8K&SwDoKwILSa|wU2PlH0ymsGI` zxI>Cc71o5{zoZQ-`9gvKDJjIfFCUwQoWY_uFg7$M2xer!qLHDQF#HAfX_;qfV})wv zx4<$O`TTR?IpHA*0JK@S=oD6uwBZj*oz4mGNCh)l#N-?jgHR|9QA^=oU~wXSn>>&E zR7Hhvc{$T~t*F*ynVFdzF_({t95n5sWSe-s$l)QHZ*FR8VP;Cao`Ov^ZNn8hHY?Rd z>K9Hway;M$gnG&H;`;bdTsHZfXcPP>_)u4{3%>al3HHwqxX1kB>ky7g1^z;;VX&;< zu-3uRI5=e6p@aJ;hmW*WRPPP)PxklD2n(&wEo`o6YOdN}Th>&OQ&5&1Px<`(#Ms5z z>64>F6NlOl*H*RVW|zlArh2&tTNs6!n{v`uQ+V_A_>%`WVfw&dJ|z41J4d?M7sW&jHdgo6RStD@&XC+V zF*kGU?78#TZr!?f<@%iyXD-g1JUw%2e(d<{)QP#%7l{Mhyml_$w;hACLp0?3Daf^2ZO6ZG!{u4($2xX%qtHC~VW7+0#c+7Tm z8cU68yyYu#+Th=p#=a!^yhhRh=rM?!Zd8`|=^-5B7Qd_4l8D|3^pw9&g{gdj0an0*%@^iY4w@uwVBzO5s~qpTlado;T#CJHH~tx3g2iIWM=GVpciOnR1^_B zTwB3K9UmE+nL05!d3ti{)cK1yZr;2#J)!I@%8R^^J{B&d&9Yji(h?`$y;7dBr<hphNe0Nu=vgZI80g>A%MY5Y6A%tbg(irCa;MXU_S8&6=M9kz=&Al!DVux@=o$z zoP5B(!t-&4kS>T5OiLkxlPP7nY%obV13>w9e!Wh+!_Li=04o=RtsCdEc zwITM}1mj5NF*X4HqGgAjt+kPRnDLgtoV55u2ODg+?^1qGys?b?@?UtLgMo>x(jus7Dnt06b*_;CL$)%P78lYPA>MxYB##o?h5 zw&pAb=k0N{%?sPn*Vgpp0m*)EAo)tv556y0Kj+f^m)QU7KUe{Oz9k~~+s{g#|MlDF zj*skbs!mQUj*1X5yXakH@)t&gk)==9zU-httbmrR%%-f2f#&+sm`ECTr1)+lF`zIa zv}8|AV^#(c!=kXz{W)1Q6Bs*q;9%)KYK?QlLec{LnFSndV;*d65=e^xQzQES>~gT# zVQWT@pde!depuTn=zT&k(o3^qm2^#s+&9H_D?EPMR+{=|^oo%&KB@5q|3^x!U8Am~`jD9CXl`g@ zXSYn-_Fvzvf@s6Gk?wrUe7{mFu~C)p8gcNAhPEcw{pM|39b8=;U0p0~tyI{L8fw4+ zTSAa+viU(D4Qo*Pq6CJjnboSuBaw)QOCDg+MH`**A-P53LOBv%0Q>(U=&z`LK7tN!#yavTdz*(f~FeYE(Q4LdcZC@ z0PU(JAK-`_0N{ik04yI)TGpJ46PDue2mVME2tSw4U*KZ3bVOPtON)Yd1Iljck8MbT zAN7!6pTD!<(&rN7#X@(a3q?p#MVu;fK(q!DHG@kQ;c4Y}$zA~Ug5cp=wQ5yI6g-r_D1zt)nMB0E-WXU5p3Tp*- zA@v5y>Ct(G$@%#y8Sznj1N?x0s{i(QPfsSpWky65XYMP{ugHpsOy2I-R#r6LclgXS zX+QnPh6hfKjSM$86hs8aZgzlvhng8j*;&!Sqp!Y#ejTsL`T6_3L^_I|6Bg=To=DaI z=I!g3FModi?BUZpSIKbns*ScIBEI*sfwHwiK~VJTSE-2JnTIqQj4oA+740#)~wdVyk0sqhVuZ=IZ7ZwL2s^!#y z!e#QCf_)rG6#n2J!{Go7S1tkdOIIx6Y6Sy_m9AVuPdZUmr^pltKhI+tsFHPxCvU?B z2J8al1zu99$XFl5iae2E$*bm3pZ2VPa$1J_b|~}6sj*8juB*2 zu#ecJ4F~dyHU)B#gxHxX!y2aJQjSvIO4wpWGJU$oj7wP)OW~P=7nr05m zHt0dTw6rvru2A`I>FO`PS;YN?QlZFs2#x;PXYehB^(+#^OL-Q^^Dl}D5M)r&75TBH zB9PaDi2Ja(XG<1uvNqoCzd0bxEjB(RDLORX+dJ9UH!Z+F+0S>6hg)`JRCPf`QG5~t zAvY@Ya9zdGfu8vpQeH+Vz`u5C_V{2=UtL9IY&2Q8Ar>Y?+{&UthU&{6T%P~)?H~UO z`x5x_C}{tmWCgr_{pRJ*Pww42fBNd&?C62!#7A9*S&ab1Rj;JxE{)K12zF_?*m<84U zNw6FJBKD@TKKs|_U|*ne^)iqyY@T4)0NaoLIm!|CE^w_*b_;;iQqw{a zGIxlkBRU@;57>neK<^9TzydHD8+Z%;h(!HI)xVf(K@18Q1r7S>2p}y8`Ff#(l{{%~ zE!c+-zy!p^Tf*nsjJ#RH<#W@3Hb*W~3IX{eN4XSN=LhVINa)&CIBb+!BsiSlON#1W zr7#)1rfW2{H8pjxr?A;pky_xi#bt}fR+p`=oNVCZi0gpksMD)QpVl1udLnh-#Kg$J z5LQp{Zv8rSZ9S9a>(+nu&0Ly5$T-+{dCb8*g{6aY)#YHYork-9_d*TCPg8d>~H^+In1OJqOK=N%$;^T|sczSejcAyu=UXvV8N1Y@Ohu*?H=ZBl_T|M;% zuRd&FD1C+Ue>fM<0Q`6F2?xA;`}W-*uU`G~^4BNde}DVx`J!+o6XDK3TvWbfRL)lUl`2KG_NkJEts-c%oN zVg!gI+J-YQ$XiFlOIzJXPm38M^aWKWiW(<*xYCO_-oq_KnE!@5EsUa^>|=a`BZAW% zoWhM6G-~dlsqd^|v{~1Tk=Wb4f?`|(BdoW3n|W>3b=;`2$zH|WX1(P`Ogke}jkH!pqzsddGP$PDgn z0RisbUbwp%{X*~#D;2Lh-ERPbf_;$$iyRP|YsC_gA|_}*;$|g607HP{IQmC_AQDIb zncq3BVt}L+6n+ge_tBnvqERaOR<)a@#JusIdP9ptN;LB$Sr(grYF9@_CAyMm~ z9t&m-wm(rE#R?GdN|n`A55b)fp}dA*K0^}SJhyG}^mK6DLi!dLgfvoUh=wkWm~@EW zk&v%T5g&c|Ea~pAr=1cV*4%N>F)(t4&c^SStr9Uvyede1%=wS&Bjpo$2oOJ%KIIT# zpPPe7S2}_TQ;vK>Xdcht$YJOYrjC))fD}(7{o*#+7;F!)O-$Sw;qMV(V;<$`kVMv; zpI4%nd!m;|tjE?QKcCFt9rfw4hpNij>KX?+yG~9`J$ZQh!Ig6-2K%P_d+`TSXS}H6Wo+SMVCi7A(aqR(o1T-ShMk?d?Z$QHwg6wxezWaX zfFJK05Nc&=W29%SwvJ(F|E>3>5a4@3?-Q57$?geXpk5xa_bEriX8u|fudhHaVc-e8|DT8CegKrOS_{3Qsq5hCx@||Gm94d4_#c(~ z%9Tsd=@Q^<611LzeNkQ~cL+an!jXUCNi>L5ivsx62(Zn*ChJ9ze9`mb1Mw80WWXYP z2vqT`T_s!_YgYjI#iErK9eIR<4&VcS;&j5+WwsPbzx~ZGtxffhYQ}(u19G zW4g2&pqiQLLF27%cCovCv$OZ?3UK!|GuUZsxo7LkZ!0WfKU;p;}=R0%L?fdrUXOOu^v0hbtY-?V2Uu{iKO-13Zkm^J+GJx?w z(F_B%v!V)s%w9T(=7a`Q%17=W1HTeQ0iZ|x)-8LG4~`ol`Skup%QN(cEFj>&O;e4D zz*G$+d$jkn@t+l8+GlSEZw)+dv5m%+~w&NM9H-=jvGBAZZFUQpAz-|jeQ(7?823+ zrGEz0AemJ+Xe>3@v_{7gMSzbD$n(3Ht*}~-oFf7FCdn2@9!SXmKSkj40PLSW5C-fE zFsH*rYz+oH>Th&*vbNtSyapJi_}TEhVXIQ`t|Zw4f6>HJNyQ=5CDkZM2lf04J4XVa zXXMrr)qIE^1^x@z7a@Lpj?mFBXwxTp^HP9B3i48sA|PUXBFu;HT}i|R`ydzS=@}U? z0u*-y*k>u&$KIz4o}q@WE*eo=M{s6dDHyv{kjMuG`+Q*f?JMm!rFIjIdWcGj)aUg~ zrV(R0$$P_}AmS)wOel%L027ujf~!irfWHa$`4JFubGSyZFX8`xQ4g%7^i%BpMZ7;> zj|}SZ)(8QP`1aSW7u^$4`cML?_qoHAP=S5N&CZTnT-<$q-MqbM)xcPFShphE)Uo}6 zzZT~I22rgE{87iIriRuQ76vZf&Ix7P!y{1M!g-Ij5NaRdFCLt5=Lz2N;aS{aqA;S+ z;2VmdMW!@gC=X(`7#WNoqRqG{9~NDg88-~}$*AK(=}_Se@$9ie_Q9#@uL?`&t03RQf_i?Y?yMJ z;e-1J+nPH{iaSekkN33Pzk2S?>)-zR>)odkK+1tn;r|Bf|JBcLo?X6syrsJ&qbWa| zl$-wQ%C3s?zJ~h4_0{{L!mASFs34AJIH0}FZfAR1cb3J)mM0`sBqo-{?#>AgWOyG{ z#tClD|fWVL&X zbCUn|q=2AEpCI?mJGR*Sdf0h7+j-j9`&c?|v)tlg?n;*}XCQCn=xVlktL-)~7oQz2 z+x#~=ZZS19r^O{5@W4Kn6dD|(_8-F57yoBri{XSOz(+bP_8TsA;?;PCRo7`O*EC#7 zV_914kyxj={RQ?#u8Ok9IV0D7#R@?YgfA%JD`NX*Kug&qX> zED3x;0H`id(t5>?{Ngiy1=$7$apq%mW8f<`z7mU4_9vGF6&Kz%dQCCr3#HFsVI3_N z^=vGBeHi|j!Pw(;*@d4Y0l+Ej9gq?W!1iB3zOiV|Ba&)lJY9y@0iZCt30SNV-iD>5 zP~(VGyo+2807v=Zghv|i88{)YNhA|0RRh=&qTMhq3&qd>e>J}3|Hwdc`Q%;VZz3M) zK1!)9H(5COHSkU_7*tg{OaMNd-`d8)$=Si#bu%s(J4c62&Q1mf2B>=$*q88^CrAL| z_@ws}0hC#M#tZ?l+PuufdC}L4rG)(edjT>7CWpeQj+c{R0c=||P9N?$ zIXo~wG4k~8wRdlRf2S~f$@V!)=Km@DG5z1X{`rrmXYb9m_0?qVFU+BD% z-)4J1+f81ZTznk80_}YR?L2*LJ-r;Z``P>Z*?M|!@ekhWy~EbdS%_1Hw|?y0F4{O# zj;X3a*c;S?Oz??Nme7WsL8e8YTJrXVG8USODjKklmX}KaoCoc!R;*eo`dEv6TQo5) zX^|p_MW+n9!ruer5icBZN}=0vh%-z|bl3)3+Bzhch@?Ly*BAVYErJq-5wQR;g3 zFv5YOe1;RTtCfTnUMZK*eIL^W7YKkjQ631Q57-wvmo`114_iVs=NC|<{50eDB2pgn6=LHory_Bdi!;paRl&XrU{dXIvARKL{wz@NocFUSGTxfSxr zjpB|WB*h(Dyoi9Q7)?e;2K99SU)lmPy64D<9S1vGdm44y(0#+q%F@W#_*3jl;M0|n zX$vy3Uuu6*$WJMsmeyJ|HSQnW4bHWIeR0E;2iX@mYSERoIOIi!RHW^#Wzt7(dP`<(#qQ8#udO8fFf9~~5By>EIP(eQLHK@yR@5YFP9)VR2?-pP!dRj4q5atY94T89 zKp@RurVuDJ2Ar~lY{euO1#pb-6y-mhgMEY*QId~(U*tlwJ|+JP_e7q^`zfgr#Z-tj zu1<{5BM8Y+aqshyh$Eo)t)TC;)3vrVG&3=R%^MmSiuR1U)Nyb@@+eMN3j7uFFZy$G zCx}d{Y4CAZ;-5hH@x=>AAD>ita6&gBaYKjztFDosN1-As@o#Ej`iaS6Ld(k|^F$=} z*_;M!+K*F*AOMsY*jX?+6!<$OB!wpDM&}gm&d%DEmK>Iu5|y8^r#vUAHb1>}UqNee zA@x4(`-*z%YKA*Ij*T9qw#y7S$;cJaq2_usvH zLw3Ia{$Ik|FW28h0mL~`KPmzKc>VVGx4*ym{mI<*vAi=K@nenoBL|v~?5CG+9XULy z{ywp8PU!u;-tOtaf%&03i^6y2glwnmmzm&92WHMMmOh-H!~+R`Vegw7pzhOrHa8@P z?$3xWkJ&-xZ<3b>ZcP4QZd7zxdRAFhUQt?JK}un4RN6N8&`tK+o!!DB_Te>j=A;zv3y)9r3*PPN6}ZWAqlK|4mXxNLNiFWP6tlExQdpKQfilU+as7cm zx;YV=VOt6At-4{EuC0om1$7%>Ur7lO?tKvwUy{v(>s;jQqjE$}Ej~lO zb|LabWanQziG6??1N%y)fEMi{`HE@0FQ#hJl9x7p8X7o!;QV-jz%sj9qL?FlSZ-#H zEGYy)w^VlWpQHqk&q(ZJV^XUtvCrob@K?%S*n1*spcK|&+RIoT0EF2vSV*|v72mrs z;o_$(Z8VkUtW`Rign0od2z83p^`0h>okNd>%0aspyfR0<2~f+&zW zoezV9kt0va55@~vlK%sC>>WfCRWSqykqKDJ+)plttCJEB?6Veb*QfVH3P$b^H&Fh4 zPPoIoHJ=UUPa+dlgv>Rhai`j*qcby9pMpIg&nyN? z_~iooRP^&C|3qdNcSU$WHPzvV-1{$OlYxJIfdiI1EKM82jrfT-sH(9K*@0M7oB(sF zm6Tl}hN?@&B#+E!pxHQeT+Cf!Y6BCTX)8*tXGo}jVrpo5URZWvbauh++@gfCs_e#= zimr~9vHtFfzTUC!?unk>nc?B%IL|9pq%e_@f~`=7u6;zvpT-@SkH?(J{yetGor!OU;hdmo=}xH?fk-P6|J zSi_jG^0=72KJH0gTaw&enEXX?Um6*|+dWhKwy~0ZJQB8TC0*GD8j>FsnHL$A6BV5ozc)Q0D<+cZpz+(bMFoeZ z<`gixV1Hgobzx0iMRV)Ex`yYza6`r95C?BG}ddZqBxBPDNC0j>}l7`P+Bs{ z#G)I6h8AjSop(^OIwJqW02mk*=?@0Z5X`cGyrvv(uc7taXr%71g_cH=z6K{jCoC{1(an)_K8$2CoXdp-0G|9SLPHe&$r1p53$;pv;zi%$CkOlZ%A|x`u>H9w zQV2*fC?&(f9h2|Ek$2=RuqyGlt=f<4j47@~rm#fJg=*fOMOux~__D9#ennHAtm zbq~xrK>|p?AXGr>9OIJ34=S`rNUpbCXAo4Rjwp+?*IM${=fbb>+iqbAr}68_xk;-XRjX4 zJw4HIXSC|pOxN6K-_b+ueYItWO7lu%BXUCg^27YH0=A|4xo7S0%?|RYA~Sh!cui77 zMObm3f!UEU#VNUYDTR9?QzOFnW@c41G&t*0h8W7C+Bfc(^WO&jM$Dmse)7IpL$cqc=Ecfp~|lECN4 zxu%)`K9HA<_oW+0_#QtloDzo2l+@8fBJrstofp-=3KL)_OVk7ZPtWC#a-XyRB^>xD zAAqm4@=$VmMP3}Vf&DKbPb`nlKMQQ1&545@;1g9B!8Ih2GKNHKXa(a^^>YsPMO+Ud zfQ?Vqj}i*xM3@OmXddK(bYvw9;0x?4N}qS6rTo=m(L>vIAMK?eK8$m z<#Gh9aFUZ1$Nv4TLKqS6<5n!(4NiE5+ro(y1)MCvpSpVN4*0(wU6UaphGRHO(b$-7N!0dZ(tDQ!#yVdg8?7=!vOuuz!AT?(+Qn zrMX%BekaF|oSGcFICuQc<@1kkU-{|D-QRwE^!v}x{`lp`SHFH-FJHX*`{ofuFQ;hhz@EzQ@9aXi^O0vYN@7odV&fpsan%l1ST zM1>W{MpvgMGheJ|cT_=iWN}hjRY6rnNkdUVb$)JnZB6r`9!74Abq|ho4-Fse?W=8R z&nzg7iBEQNa8_NdvK$909htuU7R7?UPD@A3OE5GxAPpWK4qpa|NC3VjBmf#&Xli0H zUw!r6%5}@vZNSGzRwoFc11I@&;2puM1fz&h(U((ywUzrO_wCzUooLbhHI8h-#FsK; z0)IDO8DB0og~obyS^<1e`m0rVBSz&iI1=ES8qoclKEVIek&E^R`(g-}m>~rvVB$P+ zX~4?Xm3vzPTyAf9WHWOj&)Mp7xAS6Q?<+U~DxAoCex4_zUSc2MFJK?_iW$RxmD-#{ zb}iCb6h9sXEx=jP?84oL>gQZo1Q=1=AH^TIAQRC0k`lmdWfcJINR&D#?mO58?!*P8 zOR$4|v3MT5D}+S03gLz>aDL&0RO|vSA1H|o8Ks;D0>DFI0v{|Id4D)cuyewlk$sKC zZs3x!pOhiOL`V=wWGzAOY1Dw(#lrp)v(G+(zlEV0OYncEeyI289slFA~PQ;a^L!3x$PmL_|QYNL(T)_jPV>eHX zT$t*+K0El}%#rIy4?LJZ`1IEB^E=}|+&J>+eD}lC?GNWW?w#&h{Klj)Mme4-AbCk5Ba-8SCgDYU${%tZ&LLEZwm)42~pprI^$P zXZ{+G3ZTXLj4snP&@(e*PJ#g{6vsYy6)!n8=RgwqDFnr$MIv^uWxPtqbgl4NlejBF z`7AaG$8~F%`M7q)nsrN6G{0S~K@QxKMc)#OBj+BNvvcV4V_|GzLbpv~vjQ+o(@_&6 zr`D?RE%T*Iv4F9^pz-C2qydr=u>ByM9K}A=GlBuqEtZN=9K^KK5tZzs!zTnlw4qz5 zVq=Gc`h}w+C{TpT0vphN<$-Su>`T;hMEj%nF{lAPw@B!ELU?RGW!#|RzGJE=z3PP> zt7l@UXG~;YoBW*hG%Uoytw(mBsKgNjKymSLT(<#Vn5M95Md`6vvH(ypUJMO|bxZhv zAo)shAD={05rlwb0=x!AFZ~ko^}wHBhq^$dv0%7(fG^w_3jDbhAe{@B@aL5*DFnpx z2mzv~qSF~c&F=*2)it>J^xWodE2h1Sq>GWq@)=GL0(9?ZaUv~oT@AFq(D?c~T6h8t zFc+BHVQ!+SvEIx;lLY90eMPr4(Ku9Tmi9Sc3#^AqMZS9W07q23u=EA$h3iQy(Fss2 zkr26l1NVy!jOY2EN;)VJB1}=D62M^ayv+|zM%r2oY9`26h5xf91P&dpvvee&9w)3-04XB_9VhxdMX^x)SY zfBfUumoI<$`RC_Pe|q}h#lsuV?p}O+?eyLAGq+Ap+&y#j{`tv==g04#9lk$5aQj5} z?UOwZE{wdmJNxQ~^S^yR_xt17SI;l~@#D2OKVSdz<@G;bTzL2V+`AW-{(O1s{jWEE zdU*2wrQx#^t;Y{nPwp>1Qk&abn%-HscYn^FhP0^Kz0nmSLJ?b+y0<1diNvH@ssotZ zm7U*MRN7MA)Yj0++>hQ~suQQi#%GR(l`@85jqxo6{(BBAzOg0A{MYEAqHNr=m<3&`|H+&dQ|xWuj9l>_XGSFv_B`( z4}kZNzaz2FQ5hu7JXt*m1O8-42blzH1|izS=4b1F2zbdoFC~E3?U)}__469VK$b9J zD+&ZDMRrmGfH(*Zp?QE0)d%KcS^xx>rqZO-DYKEk%t=oskY!~^C`IsETAef-D`=jMZb zx#V}sYxoxMzag}FhLjs_4@A-scMX&MoP3-D!;ACMdOG%-`Xzt0V%3#3giXw@jk&NSZE6DdM=%$GB&4t5Eq7y5=e#!5St(w2^$ro0!s=G zfGF6YK}Dj{E=lMuQEbi^xYf-mJtMBXx}dtTw63Xqe@kU&Yfb;b=Fy(c+0o$(Cy!s5 zpSyN;{`!UU_ikK&_UPfupI*HF?e{-k{{HjxAL-%w==QY-*DpN2dEv(gSDxQL|LF3u zTPKDtAML$5-FsuE`_}ZqTgN*eT^M?CXZDXDuDt)_(cf>MzWwFKn_us||NY^gzd!i< z57zy^Up@N!^^^C%Kl=6Qh39vVKDab^<#^}W(U!Ts+L_LhvBu29Wl4vM5<2qY59G!- zWyOR2lDIvk331iwX-!4No%OA~?Y#rNM@DGNH9S2zF*|kaykKR z2*Fd7zA6R3>>LHc2J)+C8vx`FT3%21`cObf1D+wEBPMSO!Y}o{*qrir2?{_h*l!?` z?;B4U@CVik2bS;`WI)mQg89Swsc)CqU(ox20IWeC(Dk4UL;x;;54wRlN1^@o$StF3 zg)sT$GthgeK6*}*iQl}8LK{7fx>^SMup60!w;%x&4}hkK$|*b_#m#MnHt|NhBUBtE zF7!AJ14&a<3=nw~09R^=NO)sW#ENSMsv!9Z@=E;c65&1Ll9lOWq$^_0aFq(-;nI~2 zAU45AamRnTOBl#xO{uD`CmL@cZ=*I|R)yPt1iK<{MFOpv!UUvDkl5#y#M)SVN5}`B zq?aS+j;e;%8Uq^@Ge@T_ZeH8mwT;Y|su_K?Xqi&JBa-_F?_A?Xg zTx8Ecm{2;3b3hao5M#pQ^bz#pzx)Wv!R1SV6hj$tB;r&+OsHwDU(epci>;%M4T-gY z-;yu`f0QJsZ?NRPTZ|WW%Vy`ajJ>su<&7esFpA;l=5jb0g;`4$Tkm zzkIao!TIr@?w$VqhwJZudjP=ye)Ig#S5N+W_4L2qy!g-S7k|Hg&NKgc^X%_8kKeq! z{l|;TuYNrL>W6bb-JOLqT$(&^;&9Ex{=&h!jK1o$p7Qk0eQC`(d#h5DYUuY{ys!KJ ztL{DEw5qQ4{|gMgNN0Ly3cb!S^xliT*C;3f6f4+`Es71rioGK$YE0B<)L4@kdqWhZ zCb_xyCb#$U`+uLc-uKMw3^Q}~0nO)s+#b%H_mqA1Ue9WK?X}lj`@s5pHaz&)Ll11c z>+VNxyy?!h>+ZPq&UZ;j{)`(R$v+l>zx z8q!z*DNGrTpyc*-TqU z(2C^!kYJLF1k}SZG2}t6EN=6*N!(lNPS^1Dr{)u-OKMR7{9{@s7>?m}c9j6&GsN$6 z;!NS!$GV>5UmF1*wn$qbNeK+Zj;=;b<7P4K-8&`H?KyCO!-P@5hz1eB&FXiry8&NI zqdNAv9_f#Tj(W&=4s#?mQ(*Vn?#8Qbzv-I$Z@cN?J8ymT?mHg6dp$G#AHVnRC+@$0(?btFO*)K6 zAA0h^dmp~-ru%QW`u^*#yzkn}o_+XE;Q!W(o8EqL)9XLm@W#&``OPn$`snSSZ~f%$ zU0Xl+{WdTT(m$d6Zri6S-?0DeciTSM_4WI^zkUZbVCR;%cYg5(iovJ9`RUuwJ@E45 zw>m?p?F$t{<|G`HedhDJ%A6Rqs zy4B}iw)*_bFS-2cHP_#`_U2ozy7sywjyaZiXZ-5s(4v1Kc5UdvG$Zl_S%U$KID>#O z^M{NWHoC61rml9>m{Cjy$5X<@E+R}?G0V<2G%K9yFkzg2#PiT!Dl%5htST&HJ{vQ1 zQ3hcph$(l#@GC?LZp0zTnI_9wQ*bczuW$XefgK1!2CRy`5C8A z7d;Mtr`zZuTn`6ew2CiAphEd_dudfF`SYdonakhYs-d8emWUr}RbfqiX)dOe-iUfbwZ2r~vjumv?U8v2(i)OweR0;Ny-vX6+4Y z?pc5Hee2gfboZ?rAH3^{2k+i^?;Vfaelt&jqKNwX!ns@);@as+9&T`_uM1v z-~8D|+*w!p`|6XO+dlu@_RoR&?rop#+Wz_O9h-l*W3!3- zkAAo9qupPBxa;e8cWwRkjxXQ(X7ew<{`94wV_dT-qhC430Xw8Keuesu?wQH_l zx9Z#r=Fgwszr37|rDec-C&j60mNdjoT5QtFv1AlB9pVORgkk|cNuEi8RXbs9?f9{^ z<7&$*N|?`OQ@MN7L{WpcY260hp6+1bNg$6ipWGNs040NM53PX5XdSLTR;x1XW29nz zqB}F!){-$21V|w~j{+;;Ik0yS9U<4jix1!N?)dd%E`t?NpfQde`&=It9^%x9IpAEv zu9VMR1|!C!dE7u78YZzxQh04hU;c2^fwbJ$F@6v;ei8Z+hoqrgvb&OR9zV%_n5Q!Y z4$$pU_{*_cP(5*u78e8j+#+T6^QZui2GsR7(ueA03{(L*4&fB8nPP`Zih>K|3EHt? zeF=RNaO@RbY?E9(p4rdHOfVlHCPPZ@gD`XP8H+QWPm}v#1Y}$3#2_NG_xON_&7y$O z_-;={?MVxB3^%ZYK>$1*L?_kDf1<&;z=r@Vu4cF|);WS{7z4=QRKd6bBS$Jy^3zMA zOY(!hj-|wJqY6q}7v$M<28Ep=0eZ=Uy(1#@1Awo4J9i}d5gQ)(6ZtWO^*l!m9Zc|H z%}DZ^4klYDOE^)>VFTa^F8Z+_P_gJ=+5tZh5AkW-`~e?;agv4L2;%%Nz2x4z*FSjA z`p55Izv=!vpS)-NW9x6*aNErrZe8~%<(73DZ@=xC2kv_LsfT|3izhyO>-o(ey!rL! z4|aV0#jb5zc5nM~*LKGHw(Qu>pP%pg<})ulw{6~qlK7KhuJ$T=`b$4IAX6+@HTsn8oJS<2X(M?HtdAa1{ZBJio zMA)=S_Iy6F40=W>s~T86bmZ`nb+ycutD7{TW^B!XK~+T0fqlS?=I&!COy{96t6s96 zBsJyLBWr4pI%whSxeJS`N0WOMmI}m*^H2=53I!1=c>QobY3PZBH@p+Swy_@1eT9pU zd03h^WI`NqLl#>07M{&$zJmGyJ9~PMYlLXCDE&$<*33=Q@J&u}JJhH#!2#xdW6z^l zz7@683tnP~syM%Z>8en^LVm>p)IR(hsloCGBKNy>p_RZaP<3jLP$$rR7h@5rUd%3WIRo`&{T!_C>4l)93{LIO~VU~ z}pGd^sGhA)nv#=Zvo>G_BT{290d8bCe`d}e#eiNVJ*4}u6lCN>iJJrLZF9W#2u*fEpFqr?uuYdr+iqk2?9 zXDIxHR-$pD^wQ3Z0PzHB1^y9{l9I6fRB2C|w^=i0tR_?LZR;Ms|DLBdJp9Zf4?pwJ zgHPRm@1}e2+H~)F%2W5<_1uR0-+u8a#`V7b{I@%{ezNnMExWdTxqJIpySH!qJ;6Ua zw(Z^_KCo*WOkgu=z|Jq<-u=ZZyEnf~*}3@_l-*li{{5Dhc7OKlu1}u)-6v1}{?niC z;`|qH@7nU4?Vr8%)w?fz{Q8r>e&ONQescfIn;v{&(_=3@{nYbM1Adm{c;tc$E}B1o zLI3{!31jHeu@fAh`tu?5yl-%Rw;!PmJ{ie^CW+XF0&(tn=I0SLqsC2{GFQu# zxu_VcCUYeejE33`m!WdKqX2l$6^IyV24{d-o5QTi2@jm69eO&zR>R&$B)LqHSONZ) zsKJ#u_7q$JTtG1|pKkPPa5DQtIAFn0=v>1?$dm#?&9%qz!Ev=uNB z_8I!@u^(o>q&b6k3eDAEy~1*E=v7rReuqMVMb8DPV#&BLQo%lv5^1GX8M@#AT#pWb zst?|IPNW9#3LLGxOM1Qq5h0RjD)b|`W@imfYnT*91f54nrz>g1Ow>E%)x|u=hBBq| z8}{{4#@x7uNsxRv6LT3Hr$J~OwU3!sv@p#`Ul=2ml=lOgox-t0TMV{pfZGL}@yo3J zonuJ*Le?YwIq(bELp)ej5F}tnl(0nyC^eA}-v{rAH6U-F%^9tSK8%9fQCY~ySdd#t zVia6|h%=N(NR%XIIH?8=6ZR+0oHk*`)Y0SXSk@czMh;L|4;Y66@XsyKlm`?fi-3{~ z2>%1{2QSV63_I;RVGg$Mh>=)WR5Z4(?#ROryYkXY@45XphW=lEdeiIAKlS#@Prv*6 za~j`&>%~vreQoRJ_ji7SO25_1?j2hxyLN2dx&12wes+BG<<4)u*#6Z=yEec2`!6W3 z{eH{qyT5#M*OzbZ{_5@BTVM!p?B4w9?#-|4`r@_ipZ#*{d(UltAr(^jlJ_<#fPZQuct>)xiIs`B;;e28Non$|d%sRGb5 zgoMy{;L{+e`bwleob~KRG9`1uw5c`Y$ANu9Hp%8>vr2n2feu^DyPkk9-Uzke-39~% z@8ct6oDSWA_(?1^#^}-f0Y19DOp7%3C7rsG^p2b`%=_ynlNV7)a^0drBzsEYloJrn z$_^T*qhMDuTT^bnu?LbNssavf+ih0A>!2i3?FFf}!e?5%Bh2l5(N0qMwnhq>Tf zB{d9Y%WBIEtYz$V9nWT}Ck#sjrNwdni%=402g6b_|2 z&^HA@2|A!?FNl6*x*XD)lgoi-<&%<&B3^-;!LVPz6VMP!ONe*O$!9@gCfjNy5h?tP z>Gx&@atuw}`vSGYoIO|u;K+9ewd0y&ibI}X{eX+b*lmD7$EM9fv}pP$2e>M6t)S|_ z==e;@eFj_#;MCu08DbQ?ox!y$0NCj?qqj2y{Q%h@FCDc5AX|0LuE?xC1ELnWjrI*}#z4_V4?{D4m z**9N(y8UaSd_Uj5^`o8Ne7t-6XN>OcM8tpf8*}mD<@<1_hWS3;_0@;lzj$Zsr*D1! z-Y?#IY2!~f-0{$@Yc5}P>Uk@d9J`<`;!vxNdxse0;T`Q`Yc*T59yr86mLS{fZaA%SOXs(F-dd2b&VX`75 zdi20}ud0NW5LRk4M@ks(Nym_otqNzqhVN}&TZ_y5jCw7x3z7@)tZUc zqo>w{A2f)mPKO+H;K~)t&R)6vrt8-{`_$tvz3|M-FFpJEFMs~Vt1rFw@=soU;prED z_S8?Ger(gmhc-TR?*r>^x&8VzH?O(u%8OPnIsTZ#4_ri+GbV<1*PuKz6qKNxMUl1S z6Gk{|paU3+Ho$HFSZ6y2V4s3N8~{$mpTw~uDJ<|CDdY5s0qc=GgNF_sJdDX%!@>Tr zk=nxn&<8Z4ou99c>JPV1W1p2bnYk^kFNJ+Xg9c@Y#Wg`pkE7DzIK|PQPnz7~Jk)fQ zUj+mZ3|N#WQbYT~I4H0%M)sXSDz(FoKZWF z9p(T$0={7!mX1jR4G89RX^dOs7);A9rv!0?tE55K=^F z5l4iG53C+oRn0^cSOy_S0)9Ro6@ZU-g*n5-RzQKpxU861PB;odD6G*4oe~t>*@eDq z_#A7a5_0@n0Zf_)%}O%3Tyb>aEms6J-Yhma6-Y$nO4H6l>z2yx>H%Yz7;wt_LY4(m zuMxRPlrfA4-x&jv z$6M=#@fxb?g$kS3+DsU}2M-2y@7xL6Kq&AsluQp(A@MQ`y9^pKh%#`<;K9R&vc%d@ z;6HjKMiWU7Nxlg1HG@N%UN!xQWM_Dn#y3xn=5Es|bg@Qfm}F5@ii%!QT87t|8sX3< zyCb>wDD*OXfPw$~{Oo*E&*o<{$seTxZI*9g1QM2SnO@|5k|Z+hLtXScR0m}`Jnu=g^PTbgQgD%Ab=CG zOu-MpngZCNM7v>BQb4pMI{{&(q_5|gI3L`AJda~@2BuwqTqgLNv0zzxG~@@1$Yb6b zywKL>!0F0(qQ?^SAPlM((-68T@J`8UQYHW$=c=j8eM~+5YAK9>zZDz>81yW((H%Dn zgDmhBN4oF%A}=oMsf6UMF3a^uV9n)1`&)NtM8-wGq0+|%#9+B4 ztscnP!ZofNFs^DzKl{0h1EYx-o5WX3PzcaROG6wWk;!0&0S--I!8!2#EzA!B_z<20 zAMEp|TOX*PP~-5yWCb47AAQVbQGxng8&!eSk@fT_g_shw3k6hweDX%J9Fa}*b0yi4 zBH#=93i{3Z|^9}p|p)8>kX?_n9> zojttWUAWHDIR^5)3mJi@2yqFwr{`f5+S`DyYkf`|j(zIDt5ev=Ofbt4Jx8O-7Da|{ znPCysEes{uN`nUuuNmc55(6dG0|yKu^ncJWd2-Cq^WIeGgnM!Ew-DvUBpRZqOu;sU zGK;lH&DNl?u9C&MjA=Lr zu0Z56vx8s~AVUHBuzd)F0#zUj;7Y~t&WaL@aWWKP)9%LzJJy{1x@ep7JfQqL)j9kD zJA_wl=kYr7(+JiChFS*<1oeGDC@o)BOrBLx%qwUqji9&%yyHJpc6tGWU ztGi9+GKnA6gRm3`5gQ*4Pv{=K%zz)ssc`+1`I7NJLi(sPwW7lEw07vgM<9bJ&?jMd zZbz_pCIAI;jI#{wS+YLEcgH*#&G@k5cloiyKYbU2bHX0Q4*)n=a3!C;1Dw4MY)+lP z?x@$IzAEAXmRa-B@6+Q zp~opsa)@|QorQCVCetoQ7yXc^Y<~!~azH_0AxuzNV2C~=)i}UpfI5fu6J0>lTBKrw z)@*ivXQJMKo`rDJ@?Bx3N?ne|MV$c)<3AW^^dN<<^qzGl<~^QPB=|~(WJzZV`cdtb zztZ%3!Zz>^XndAF1&J<>CEr0R%p27X+Z`ZV1Pnl?^TuRiW3!}M!(t$Lm-q%Y32}H@ zT~5Xc1_=h5lCVkrleOihAHHvXcVg`)l7 zi#@Tih)a+@!3{eQ4LS+Hs6P=vm1jZnG3Gz1Wp>h`C!kTOJQ=S~x zTE zM^J+t_}RD#(GM)K5735A4EX#+u(OIy(ziVvFee*TEfVfXa22>WGmISUwx@Aq08%uL z!{pcY4A{c_>2LH+!lm$Ck>=A&4jN(~h()X*^$Fq$eVupFJIcU^QA0~ApB{}P&!=la z)@ZhlU>;6c<=dplIgl=N4ZPfpCTUIl$-QzK*cggm&jo}|scV=Q%7i<_0ECTg+pmwX zU&4>zgT)7!w9NtPtKJ6~eWPN)~cMGcmScXK?UFzv_TR zibz9&Z40SUm{KD0W{;zs1JI)BCGxBym(|F&gaH^t??RJi(BEV(3lLK=WoU8kjjZRG zhEqSn)vY&Cp}CA_0wP9f4f~ob?6RBp!iD&13Rhyb^HLMcl8oE}7eaWuw=&C`_8g$FXO1~ZZSK+Zx* z5Ila$mM2ezBKhHI%=A?5Y{ubaW-4lw(RchrWK&=wyfkW+6z7*JF0L;Ak0hmEkP9|g zXA^T$-K0PfY6{I;sV=xbSs}%&`qg z<0F)-ijz7p>s6Y0dUPI2)AcY7Q0USevV=G}pNn=W)HAe$y@$vKcqsE=pN9+YfSS$` zWYT+qg<1nB6_cl_IApp|MOn4%!2V1reB$=@Y!0HSc?bd15>U4^r_p^q<|%^5!aVqP zx70W=?28l9Uj$}JN`Q8TmzCSB4H0gH!E+kkK=Y}SHCtEl(kT46uDsCxUTAv?41wS! zXA8M~eU%|irl8P10Gt-HrH4q#se%wr`#R8nZ0h(h5<&iDtJo- z4dB3)TnMKEvW!2Wuhj={Kv`%i2zV7mt>XHFi_jA$xIrwefNwK~pf?)I^j_+~D=Z^A zQd<+X|hzdM@a60CH@KP1DMO+;Oif}c4Kq)*F?q|pajGLLRriM)y z!Nw`DK(#!Qf=)rs8PII7tVmj05e6F~fCw4TOaf1uU;Cb{^T=X(vPFB)N@Ue@-HPUy zip2Kn$))f73rMZBu-J&|5isq-o+p!jQyo3FV+- zQJ@f~6YlYX0J8I%PrjgNmIAK!P+o zg%N9;>Zd)KN`A(Rv*SHj{1SmTpY05D$Qgr+2D;7hH6D2A;`@ysm#6KD8IYKHMf zPI5*!O8w9=?UTC88cilB|3TXVmlBOqXiAUoL}J=#ssv>!fh$4{X~pV1ojS{Yadm*6 zHC~M}UELv125u<5WAWQuqa&0QXb*e$5OJn3}yD z-t;<&V@x(5sWAafJ&}M8Tc%_Z0t@g}(Be@~s5LpV+2irWbbw$m21!Oi=}4eHcjNqF z?LtH<-b?WotD88CE0D5lL^o)dsxBKfp&ir|wnrO*mWVe2-BOHGDlDx{%SXK+)l3d0 zm?DXhQd+T2()b10iWxH_l;v`SeSoiCmOqvE8NX87tMaC9$(S`UhcwgZ2v)xvu;ip> z)g7(2A<${n8VbioSI}olB0Hf9-HVl7jAPO~#JvKl|$ik}ir$ns?+n9ie-)LTXpC&BxE7ypp`vwzEFI zn05E={`ugYf|RF#dJlTVJRDs!`n>bb+wkb4Z@u-_yYIgH>Z`Bbx&F=*jz0mt&{|H^ z?My&!14Z&$Q6H0=MdaouVG&w4SyL){(OlAYtblq{0&fSZtq<3?cN5Wx?!;SsualN6 zgbUOrR`daOi&#+l0^MSN@^(46h$U90AjK(}h_sayXdcNyR8f1V#VRO1#W$=X(0s<<3Ff)({IZH~U6(z^#8YF*Q_4^J0$LEB)>AWt1Q<2JVf_1b2ELC8^c zLqRq01AuCT$krI%QYr9elnq3rCLGiNMWviQ_fPd)nRqeqMw&i#C^ zI+RbxX(Q*GRJ(}7!zuKWH4DG_#nh7uAPO~pN4Nlout8>I?nk@9G&Bd(Am5(qjUGMf z*kg}bv10l0#~)W$S4*AsH2W5wN5$kZa&2GI!ki2?ZWxU_Pyt7r#EGF0Y6triu#7FL zaRo$_;f|)JBcxndaK4QyL%S(W4LjatJ6wWCLH(E^W_Iuu`W&weDLkf4n|jhoC$3nr z;^?D~8Z~Mp&2BU=dzOGIdW2eW2VbwEVM*8q2P$cza@LrVYbjhqAx4Mki@8fhei3f8 zdpY3ADS8&6@*EYN(es5GS=%<%l`avJR$e14^lDcTq)@Lh8Ap959v*V3gQ2 z0j6(=vKUR7H`lf~zMnI9&YXF3=5liW+<7C04F{e82PTOs6$y8>o1xX~jDa$E;GnuO zW2R1-GHd2c?zJ+HC(NIhDw8KoPPI&@&f~|8n?G-U{jYQ9MYqhEJ$n}0>{-;qpE6{~ zV98kn8vDAsG4t6L@PK*THfPRk%3Lm;>YdwyNGU?GK*3+L;=%c6ygC_Gij5-I>8l+|AKnZ;+#oH26-2Xp3FOU+ZIsl$A$ zhK}dW8!~i==_>TGoCfk9C=L)PLJR85>C>k@{q)oS@-P3A*+u{NfB(06^X8>XmqIl3 zrT9zRw!CmQZ83Wm|Fh?%`iG4jC;3Z-s?3=_V+I}R@CW-Y=%Ex)4_Hwu&p-dX9Xobp zzLDeK{qA>b)~qQhEzzif0Ug(n9!51uAuyFr{`;#ZfMIn_$7+_sF01Qr@CQq99*kh0V z(?9)FX0>UkufF6pLe1O;xnA)}fcbB|Y^MIZcke#__~ZFllt#*b{^x%-|E|nmk3Q-s zWSK-5c5U~b#~yu5=C3UtFFx@k6a$1Q3>Jj|7eZ&_SLFM>Jr15@k3ANNuzmY>y7Hg@`JeyeKmOyt|NFn2>NQurOg_kmzhv>^zyJHc&wO3; z#~*+EaZynbGzR=%eDTF*F23dFn=$h6AtAjepdQVNI>SG&yY|`!@~fHW@DmD&WSSW# zm?6zyocZewH(by6I@e${{P(~Aede!C9V6FZq%;n1eDu*ro4Ra|=keabW!!(#x=rh* zdZF>X*Is)KEF29E?7P_xUk8=d)m5K-@=4?C8=T=qFTeaUZ461%`wf0=>Q4wp?L#&p z*c|f1>;m>E`El9`o^#bzS8*FJ4Z{9WM;-|QyLrXIQAZxtR1Nod{=^^r04zXAuqp6> zETQrxCB^T(_ud|_Z*UTO7Vir4w)p%8KQ;9y*cW)2vw#ZLu@m-SQ&;Zs{EX={ASa%{ z{@QD=-QzWTJPGy@T!?sDNj9Ra5&@s?8a8y;uYdjP_yorDVbtEw7^W9oZ~@)7$CoyB zl6SrG$}5mJ%=2&l=5Lz1jPp0IyO~y^W6+T(^b~*6-H&Z}EU3af3hXlhPeQGA>mcl3 zfBp3^4CyHC+Kw18q8W*h`616g|Gcppc|k(4kLwPTjTW}b24Mf1Yt{fdL_b6g_RVVT zh}Qkv-~KkUZp|FisyE+!b13$u#aTeFIHX)a?aInZw{G3q%!@LA@|SW~5cZEa;s|VB zL>NLFF?!??M`S*;#bX>$FlVGNbcnG76!1E}{`%`N>59wlzyE$3DhT`7$p~%_guyS- zVkI*#m_B_vH0aXg+H0=K{HgilH(q}OmJHZwB`*&Av2|nr@P|J%e{bVoVI$b=RO8eY zD}wqfdK8Ki{u6At=9?Pq(^gd2n3dTk9Xeii>19nR_R;ejAH4tm`}CA=LD;|Tw%foU zKxTj7fd@8U;pn}WUw*kPLc8mcM;?jJ$2owW=^Y$S=tWdG<{No37Bun~LFVvB+ozY# zJnPK(Pv9X~E91tE3zGZN0I4ekL6Hln?L!Ye6z5B$Yk1x2)vJSO>F~o2qm_`|&;|!b z9DaE8v^WRHA9p-*1TorKo)8#qhm%iU5=2+%#xrKj2*N(i0}O@z_8s8xaq1aeGi};5 z@mQ&YS=WSMABF}?2D9e!(YOtm!r%MlmtRH?kAJ|UsKeyRlcAINcQv>Yt@yzQA8c^4 z!Jq5a-Hh9d!6-Q;sCCDVBZiL%QuU(;CdU5lcib+HA$_6sg%@5JJ+S$MMT-_C#y<26 zS%Q7~3x*RPxnSQs&l-Dzm;pXi7GM5GU-Z+T{xrTW_oqMoons%WRB785Gd~E${$5n# z_+QE`haW2Jn`0d8cRBp9Fzg?9>~UtGv`6NfwbHQ}H+9r94GvIW8~6hoT#)%EA1Lgb z&5iU`U+wEHd`-^seMjnAdvA&Gx`+RKpE`|p<_UX&eMCMW z>d3s_=56iFZ)|W(Lfz`+eyi56zVd4P+B)`u zKifwieH7^b)vPUwbo|@nd|Kmc4EtK1 zRwXNokelE9<~NNm+vjHvJM=I_7_H=9piu1N+;$Bd+QNVJ!yo?eyKbm7_Cda5zk5)d zOf&4mViRE>TLXCy@@1_bddQ(cug<)fFfs-@GtYcS$0qk>s!&jt;8*c=(FRpAU!UR) z6UcqC?*V)$B&I66Y15|20_GqA_W543%Q~SP#-~YCLCA`c5U~v{Xx{em#~*Ld4*UF1 zuuro}?KkW*6cXPA-kZt@`}l&0J&^}E*xm>GFlP}Kl-0idJ@?$RH-kWQ{J`9z1mDJD5s;sPx&#d<5ylEB@Lh>zXwIetvv?q6&F{;=G-g5)(yjt+iEc!kgpV7+u34 zEZE1)3B^8Re8_tF`9#s}iSYks+c(i;n?Jy7O~eQT8MoYeOOT5o1N%58+u#9i{zVOb z{q)mMk390ov(7#%C`5#Z9D3-Xv=1E86{KM7A9G9^`-B{K`{Ii)8dR*QKY{Qqx7#wEgPXrT^H(&JlTSX`ZD*TDLPkg-5#=-p`wUk(_Ak5a zvdrfT6Hay|A*IsMt z>l&XoonHa2HlEq~po0%;d`(klU^2wS%9GNn)kPOw6#q+k;J2~gks-gP-m=H@#~yPG zVg-qha>m-t@qJ$ybly-%U}VZ3AHK(vDeT*Xi9~W=zCNfo3HEI+tp4%~|9IF) zUnq|#Iz!ubd;GLLo}@SLz4u{OQ3h%4yJ%`X|Fao!e5RuZTwM9oiXe~%u)#V4*P9_ zb$-LXiGE=}$Qes?3Oq++A~rhN)YmmWPnbQ~0X441)~Yq}JdLku=8VaGHJ5l!yLP9X zd~!1vH~4ASu3gL=G{c$6P?{mdv?e$JtnZhv2E_Bb+^4$;MQ-qlJ^sn{VBg-0)-Jc| zhDXEV`I(cab{6&((;tHUYiKF%L+<0hBCqHkYq-agqeqWU^-`-=j1KJanmwMZ$3CCk zrVVog_juVJPvS8qw%v)Nyu3Wf)^P08!q&*`G5`1Yil$D2eYuik0kng^ z(g;D09mhUttswHIJ~Q(?^Sq!7!@e|)5+gsIL#>2nacj_5%B`fwman%~v(^SAzDbt-DqT zq0IcL`C|+sS_x()J0&ZNBq4F;cSO(3#6C|rbwz+oLHB0#hk$K>?^A*H2KysNj*O#` z!r3NHoCqaDmbjo{H5`P?{ce)`vKM4w#mBze`v`rhvci68)RHY8kjIUla_paQ!U-*2 zA6+2qBL-#fi@ifC(N8TN5FRGj!*JTPCOb#WAyj{CB=^HN~a!?0gb zQK5D+Mt4w%1F8Y`NtY2-CeA?$`_{k4%JU%Oel*g)&TpfR;`MDYVlz6=bHflQ-CdRhVq>F$?32mUNm6zMJb&>iQJd35A18qx&yP)0^~lrw@BK+AM<0q!jarJ zFH|z@>$Q|H>>qL{#B7SFjH6KOYkc43etQN)dPj}l;sO0Dw`NurH9p{g16sU3x**c| z4TJ5-1QY!f=ODs995&S6Z-9M#?{VJf*Pv{xoo(s_)9B*g$Y==i9^fOtC~%Ko{pwfo zZ@(#}^TV)YrvzdD^Upu$E&zzoC&`0P{D&1FPlK>ecnp62m|B)1h}>JA)z-1k>k?oe z=;ETJ&1t$oxz8A%`9?c-EDe$Svb#-{1%ss4iQh+H-;ir^KP&5k*i#;OMM~}?_H7Hr zKCPrYUZ&-Q>iojK*+)1=g6w_6eoDzX;J{$)o6}6wVG?0qOoygQCiiJB%dD3bihWos z#7r`XAnY^Q5MABUdI|fqbI|yau&;$Ttwuuob8u>ah1 z&&7emU3cB(UTdTR&BYX?y~4h9{!r|vC-Srp)A>m-l*WFWQ0z->hi`}~lgNGUGLp^; z#Xd}q=B2>o6JQ^za;IpJy)W!rjoBr~emk(ANawd!jwy+pVx{vNh!nqQ(*VPdeoY$taA+RWcJ-&s^zx><7wyun(NcY7(c~ zOP4N{>MQ%T4R)x9nkSI^PSx!93i}~CKV#exeiLF}kVt}ktU@;K4adGshnHgn?1#mu zM{+;Z-e=rO-)W345%yDdcOvYkO;^$`|!^F4fIsx{@?0`d7F!lwb#Mn=#!bpgH?0v{O(AQU0 zRi!pElb`6m_*lT+H@jN{<&Z}L?CZ70bb_((V|-vgncPQ)V()k9N@n2r-wXB~y!O4p z*f+UvaRtUVl3-uyu35uDiH>piA&BYiQ0(Knk2Ca)Z2QzaS-#l9u` zvIr2ENFtqI!`AH;+Y>(mv%0!Ejs4cazCP|u+w7r!{=LP1N7wli`1&li#~O-^J@JE} zYRA;NdxB36_4P&A$IX}k`|>a*!+zRuPJ;bq%jH+;DD1Q9LJ0P+-fQfeyFj`MI)9*_ z-{yLNeS8$CD=7bl{a|~aP_8(i{N|f);@O85aX*o600@(n5oIX>_L2K0k;1T#``7&Z zKwsELl*bQ}Cl0?=I>A9w>79GR^qaHxo`RgvuTpp`<4zW9Q&UGAM;Nt zK{4q`VFLV#HXJJVQ9t5*GJ_iE+8Wp+S#G)zmLHm@7dr(dR@ldM2$B0>A8P|=CgUk6 zTCC_5(@nwGop|Dj7O5(d{)=EAiy_$FH=SSHHi@rKa-TM4W9WIymbgRSr=Nbh+_?(r zZMFaY`^O;+zeYNL3WJIL{E4vt+Upoq5E6z)Lb=}#x!1VJ^xK7NL9>}OG7 z^HGG!eH-IT`6xU!aT@z!IzM@t;?$XuI6~RMKDi*24%OD12Kb5OepeujKnlhFRX@JU zvCn(q0Pv9bSvoUQl;8)8PtqFSXG@I_CYSqYv%%O`8amhc6Z!h&nNg@KTYPaqe7j(8 zTR8TWct_r#gkyY${nmjoy-3@E_?4vC*SBBiZY;3e0HMwN3HG4}J{jQ4McFzG`>8QL ziwR69_tV%nlfOIIr}2ZZA5#%UX_SY*b0=Ug?89jAEVAZwdvbS#&EvyK2yJM?MhnT; zN9Z+lCG7K`nfybC3_&4jrh_tnBEbUdF+tVA*!S2Tsr||2K6DbZslCo5oddUcZ#0}K zdp~r1{}_}}3k?wVD=I3ObDG&2%^cIDMA*_$q1Xon^eol>zOj$)jWmJH?G5(3FfS&+ z-oN^)t5UY0=t7gk!sWjF{K7$2RuZ|d02^D4$V5NBKAsbV{bpz&^QUXCyB4Q0etoTn zrG=`=8p5c8T#KRe_r;*nst(d2zC1eZ)YHJeI~k(1KSB8=(ELkFOA}#VLZALkpz}*E zm`)go{X-1T2(zULWarFDK`AM!|wedg}6tWRnY2T1h0 zz&^{wpz!141M}tNZ;U7gLHo8^054-EsLoY>06|S=mKG1Ic{48r3#5| z2m97sdxd>s5%Ka_Q64;+Zl7bH5Sqb*2Q#z=-qH5uSTyXr2OtUdv63wB?T{fu;#1>+ z2OJ0xKs}#~&Jn?X^r+DY^!U$-pTvfqs>i-!nUlzUx>y7qjQs{We^~Ag76g~_jTVd^ zYz1yhfKw3sgBmK0{gnI-#{QJ4iLsAnh4Sod4nB|!`$$g)$|G#HcmVd@E3$Xkr(2Tf z{Cq63&vgFoO5M}78!Y#`z&`nBmcj|?pSxGVoj#7 z-(JaFiOeK*VVp3|2JO(|pK0vxh1?faXbaQ%HJ2w-?kB`PL484M2<{W?+b}^A?1OX^ zVCWx~@anW@9T<$Z=57h*iqvVqvmpP$Y376%~yJ*dqQ0&7)+)2Vdeg;dDq7>xi zcV-enXJOx$TIN9TH@_cl1QJ&4*s&ufGii~S{eq0~ed-%7_dV^05p^Q$D)|_3Z=fTXWG%)IZ3)(imR~`{wIo31aMMv5f5(3NzY=EG#*78i|N$8OE={y>W#{DCl0i>&#(+^glX_$gn z>pp#Y!+7HdMYq^cX}K>g0}g<7*y1gj7cjDr82gdj=hYks>ijDd+tV438F78ztIkhV z&GFR^@(9H~Uy8^>@X6Tk(v27}GotrB_V?d^5x_$319sqVV|*Is6XwFP&p_LpIdgEB z#}_>)^JDAkz`o}zAnrO7`!f7-Mwon*S1T*4xTyHnTW%g z?t^_W1n`}wg|0J&+>fvi)9y&#FBU;cCAUbyA2o(4oaEvE;~)RH_c}lt`%*VxF~QiM zGKD76*1Glgs`H!NCrf8H!9Rds?Gw3gIzI%P1pC;(s&*H-`hlSl{yxS&RKbinNDz1l z`$Y7iXyfD8?19e6lu(^N6oXV?<71DZL; zkg~Ee<{-qJp4?Qez`j6a8wcMX_76UUZ>h(Apw55vF-I%zghwDD80d#JQaW~^d0>vF zhA=!OH#J*PQ4vE_d#M8$_S-}T>|X-=@@>%jVRC<&YEq)i$xPN4HU_5nWhPuKFGeU5!rC{qm$`#AS-@k!!$(?mXI@}LiV zcXA(zzir#L3FF7pJ~H+#A-MyeK@A-Kj(YTg#=uX@v{n!{e1QD?FTC(Vf`I@6HsBm$0`_Tqr zPjVRv{^;2fxf)wjCb@R|7W>G0rjn8ToFxO0*GK_+6NLurLR^js#AH@O?da6GysW%2 z1vYgC-smPkZEY=7+SFx@&!c#x=-IFz9MhZ1^(Ao@=Icw@`*f+M#)SI$4f{4;o|Q!~ zM&s{l>P${fjyDw1T=7@&<7w^`HGZR(+AlOmcfGGK5%$fokp%mequW)=pu9c8J~6$m zTQdcobs?5ckSB6Eh)Dse*W(f z`!F0dLs}^r_8B39qUA#l(fKux&jb_5<-Jj{ny50WPkM_^x_)9(npkZW!+uzg)G8_HhC6aa5CGVl7&R!fzAlcq!kvZ?PW{6$$_Ws7n?vrfXN z#$x1X;Lp;KJiYKLD3i{Dt_c8$-qPa1C!c&G@Tz}hEB__1uYL_3lMcw>tCL+yRb(~BaDAsOW7_xbz0 z)Wx@3XTBGx%?E{AoC$L&&R!%%Z7RBjrBV4%ouBu1Vp^?4F&eQVxcL5kZtv5%@4x>( zl3u=IrtyNJZe39Rsr`3~eJ3i^0Ah!TBc_FEI$q3(p7fmd9ZEvh;$*rpMCFCFR1w9t zctEr^^ys|Zupb=TBMh--1fo3mb(JLu3u4r#HfnHMT+B_W4lBxFp!0N)uT_1!Dj8RL(A_-vJP2?`tlP0>wkIkJ1iiMV3CV+_21elZp*I zaS$OrI`3M#@BK0;nQAA)KF=Yl2ks+xZn(~mc+I4lJsuM#SKhyz=L_=q_W2}R7)#i1 zoAn*Z{ir6?aP19i`8I$+OfT@KB@O!_YjSnzYU;Oq+$6Gy9^T@?8*jXkviHr8l)%>~ zdKUCU?frvAT4u(ACfxolRyFeiurDpgxLqjry|z?1$jR{0M_PQ4dy`>*O1-@w?B{o7 z0|X?%zUJ_1a;So22$0Kc!{(2{K3@tQf(8gO1!qEF4Eqtr`4hlM%EJ&$Z1IyH+fNF( zq}oAwdeb_;o{0oD?91i}wfAx7#(_WU!SQ5SC%j5}40~xBocyk_PZ!|xudb>B2;#Ax z&Z1rWqQav1Ls7mSA1H=6k3a7C=DRiXR}wM^`)XSGkVCQWaDd??!#-32Nz!UFIjKmNdPATCN2H#f_hay^(V$b`wlc$0=eJObbcw{_T{GYhxz*G zvfaCPH$x2m69IBigx(_a#zF~zqD&01|456s@BIarTzoM!<(j{~w_W>!{DSzCOi}7& zwTEN>>8GBq$A0+uzTr>OmxCZb|G@_zYz)U`*h`DMP6 z8=>R`*q=Uqy0=wg?3>U}FvhnWS^|l<`-qK?*-BgngA(>rITba`W<{YUz`0RDV5Uy` z1<_@^9TX+PKI$*;Mxh$&B*zWW9nWmNZiF*99en;YCk9_0%tZ z@e3GE9DO6I7z-W3;P^^!G^&u@FFXDiv|%6QOQI1G5IwfV17^I|V?W&2m&QK-Cc(b- zs`_55UIhITCUqyglp zO)`&_HOP5`~Y(+$peDTE>F+Z8ue%09m|A_X9c^8-6fB${DPui7Cq)0#TD89Q zmRsY$m!xgk5%!V06tvKHGq>Nq`)=csaltufpW|Rs zwcE5=bxy##JlOk2lg>Kev?})Lt%Jt+o_+RN;$2(Jcu9oMA%{o{O~c=~n9$HB0rnLX zVG$%KPA#$>?+z^10RskzVVb{zJ))ceh?t#~otVP#*FT3nA+SNF=zLtEU zG2h5#AAkHYT_Tch)f$~85%%@HG~q0Kknd^2XX`&ab$(S7zm{NWE{ z-o<5W*RItI1%AOl6#GXXb+l21=wki)_3__Jw1#7!mx>%)w+@s0KU_xt(#VK@H2?VT zO<~^%60!^$apo=L?6Rgx*@Netb58tMgMIEb)*;40{Zr6y6NY`(;)E;-rEBrkNI-y( zg2A4b`JUzuwf8-DhbXgE>zOlV#>76=n?7y2^c5i6}) z-EhMVG4JBC6)RToxxAOc!OE2@=PB>vt(g9Zi7By#Knd?T%3>}!k(=a-{pJEXSKY{ z*(BJ9>$do6CTWruFU4dG_2RQ_T0au#=Oyc=65`M4pecwNB*tVNOO`Bg z(!!S&-|@x6Yev`9W4~?NBMv{J#fFM5z>-Rd(Z6bS!U-qDtg7GcetzdUkXES9f6OtO z$0vej95yQp*GkN{pLNz*f2BDpf7PQ$kN6<;KxSAxKvvQwD}2Wm*SmXnY;xo@NnBYha5>liYa0`RCKr_%>~bU&DMctIfTv zI|h6vO`1gR!T4`vGD~4Wp$lL14cWH}ABPfFLtR}RPW~VT3BbZKfdAen zYP;v2d*XL4*r$2v8gsO_4aGj6xzDiQb+51wmnMT&iyw}q3?cE!@OB$l5PXwip9jb@ z6M}uE4Xnq$vex7C4^oy|{J;hm;3C620rsVAXgsC;&>=(PBcj36g8p>uyI4u!>yzA1 zaab_`o`8=zr~*M3fAGNvN*k5h=+!o%*bjPLbTNH#)KNzPBDTH9e*9Gnset17(@tPC zo#LXRs8(?fAZF^kZ{&Vg=BlKuo&<8AYQ!<_pdYBnXz4G2eT$|rxo@#j;1Os91=_{w zl+0_+Is0s0k+L!f!D`n&HzzlKoJ{7ULXXpOUn(VhGF0cs-goue{4?URSWKaHh=2X1 zmtM;2s3;aj`}SExv0yU9zdiAlVBgnS`~MyGFTC)=eT98i$20Q|NgAf}6Out4*vM;Aopz@B5&YOxzB_$s=ZI-K6$gT4B@x*b^OKF zY4e94e%PCiMO#|z0@&=L{TBgv6aC5$mH_+a>q}$5b%4F!0Q<@jEN_2XhT&SQdFTav zDN}vnE#_X(qzU4_U;>Hlu;ky!4rkPuK;2pQ$ z-nUQhoPK=@^0G?`^NNe|@^Z8Da&wxwtMO0CDu%5gN1uF?e+ix6M3|&lR#s3Z89tw3 zk$sDOpkUa4wQ<`vb%y*5?k2@axNFz0rY?)luU#vXpUtHS2ch7TB*O_82V5h$s541_$rG_Z~UHP%RMmnv>-#03!O^wVyp}cKkU+4IUf+ z&j#2Jox`g&x!R$7w_`qusr+GMdVWYG66TC`~2V&CO{kiC!G$58+X z@z`_RV*LrF<1OeHQn=;$4$j@7@qLX03-}-_evhBK`doOMW1n;-WG#r`-=|L>CMLvx zRP&!nZeG^-OTSAJpNSq6q(5o^6$ol!e;599T-nXPH}lsB`_kmH+J>&hNgl&Ky?d4v z7gSc14IEHWRZ&(}QiM1zD=B{A`RDglL;dMbe>Z#9%)Whk0sOM!!tzo|QE6#WZf-fGqQd&d6lx8Du{gL-Xk+K^(d#R_L$z=#IA%0AKg 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 diff --git a/radiant/gtkmisc.cpp b/radiant/gtkmisc.cpp index 885bfc2f..b80e7e3f 100644 --- a/radiant/gtkmisc.cpp +++ b/radiant/gtkmisc.cpp @@ -726,6 +726,24 @@ GtkWidget* new_pixmap( GtkWidget* widget, const char* filename ){ return pixmap; } +GtkWidget* new_image_icon(const char* filename) { + GError* gErr; + GdkPixbuf* iconPB; + + /* + iconPB = gdk_pixbuf_new_from_file(filename, &gErr); + + if(iconPB) + return gtk_image_new_from_pixbuf(iconPB); + else + return NULL; + */ + + CString str = g_strBitmapsPath; + str += filename; + return gtk_image_new_from_file(str); +} + // ============================================================================= // Menu stuff diff --git a/radiant/gtkmisc.h b/radiant/gtkmisc.h index 3c9c6b29..b939755a 100644 --- a/radiant/gtkmisc.h +++ b/radiant/gtkmisc.h @@ -61,6 +61,7 @@ gint widget_delete_hide( GtkWidget *widget ); bool WINAPI load_plugin_bitmap( const char* filename, void **gdkpixmap, void **mask ); void load_pixmap( const char* filename, GtkWidget* widget, GdkPixmap **gdkpixmap, GdkBitmap **mask ); GtkWidget* new_pixmap( GtkWidget* widget, const char* filename ); +GtkWidget* new_image_icon(const char* filename); GtkWidget* menu_separator( GtkWidget *menu ); GtkWidget* menu_tearoff( GtkWidget *menu ); diff --git a/radiant/main.cpp b/radiant/main.cpp index 19297d7f..1affc95e 100644 --- a/radiant/main.cpp +++ b/radiant/main.cpp @@ -71,8 +71,9 @@ int CALLBACK WinMain( #endif -// ============================================================================= -// Splash screen +//============================================================================ +// Splash Screen +//============================================================================ // get rid of it when debugging #if defined ( _DEBUG ) @@ -81,7 +82,8 @@ int CALLBACK WinMain( static GtkWidget *splash_screen; -// called based on a timer, or in particular cases when we don't want to keep it around +// called based on a timer, or in particular cases when we don't want to +// keep it around gint try_destroy_splash( gpointer data ){ if ( splash_screen ) { gtk_widget_destroy( splash_screen ); @@ -90,56 +92,23 @@ gint try_destroy_splash( gpointer data ){ return FALSE; } -static void create_splash(){ - GtkWidget *alert_frame, *alert_frame1, *pixmap; +static void create_splash() { + splash_screen = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(splash_screen), "Splash Screen"); + gtk_container_set_border_width(GTK_CONTAINER(splash_screen), 0); + gtk_widget_set_size_request(splash_screen, 640, 384); + gtk_window_set_keep_above(GTK_WINDOW(splash_screen), TRUE); + gtk_window_set_decorated(GTK_WINDOW(splash_screen), FALSE); + gtk_window_set_position(GTK_WINDOW(splash_screen), GTK_WIN_POS_CENTER); + gtk_window_set_resizable(GTK_WINDOW(splash_screen), FALSE); - splash_screen = gtk_window_new( GTK_WINDOW_POPUP ); - gtk_window_position( GTK_WINDOW( splash_screen ), GTK_WIN_POS_CENTER ); - gtk_widget_realize( splash_screen ); + CString str = g_strBitmapsPath; + str += "splash.png"; + GtkWidget *image = gtk_image_new_from_file(str.GetBuffer()); + gtk_container_add(GTK_CONTAINER(splash_screen), image); + gtk_widget_show_all(splash_screen); - alert_frame1 = gtk_frame_new( NULL ); - gtk_widget_show( alert_frame1 ); - gtk_container_add( GTK_CONTAINER( splash_screen ), alert_frame1 ); - gtk_frame_set_shadow_type( GTK_FRAME( alert_frame1 ), GTK_SHADOW_OUT ); - - alert_frame = gtk_frame_new( NULL ); - gtk_widget_show( alert_frame ); - - gtk_container_add( GTK_CONTAINER( alert_frame1 ), alert_frame ); - gtk_frame_set_shadow_type( GTK_FRAME( alert_frame ), GTK_SHADOW_IN ); - gtk_container_border_width( GTK_CONTAINER( alert_frame ), 3 ); - - pixmap = gtk_preview_new( GTK_PREVIEW_COLOR ); - gtk_widget_show( pixmap ); - gtk_container_add( GTK_CONTAINER( alert_frame ), pixmap ); - - CString str; - guint16 width, height; - unsigned char *buf; - - str = g_strGameToolsPath; - str += "bitmaps/splash.bmp"; - - unsigned char* load_bitmap_file( const char* filename, guint16* width, guint16* height ); - buf = load_bitmap_file( str.GetBuffer(), &width, &height ); - - if ( !buf ) { - str = g_strBitmapsPath; - str += "splash.bmp"; - - buf = load_bitmap_file( str.GetBuffer(), &width, &height ); - } - - if ( buf ) { - GtkPreview *preview = GTK_PREVIEW( pixmap ); - gtk_preview_size( preview, width, height ); - for ( int y = 0; y < height; y++ ) - gtk_preview_draw_row( preview, buf + y * width * 3, 0, y, width ); - } - - gtk_widget_show_all( splash_screen ); - - while ( gtk_events_pending() ) + while(gtk_events_pending()) gtk_main_iteration(); } diff --git a/radiant/mainframe.cpp b/radiant/mainframe.cpp index f276bbda..ddf81510 100644 --- a/radiant/mainframe.cpp +++ b/radiant/mainframe.cpp @@ -1664,87 +1664,87 @@ void MainFrame::create_main_toolbar( GtkWidget *window, GtkWidget *vbox ){ gtk_widget_show( toolbar ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), _( "Open" ), _( "Open an existing map" ), "", - new_pixmap( window, "file_open.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("file_open.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_FILE_OPEN ) ); g_object_set_data( G_OBJECT( window ), "tb_file_open", w ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), _( "Save" ), _( "Save the active map" ), "", - new_pixmap( window, "file_save.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("file_save.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_FILE_SAVE ) ); g_object_set_data( G_OBJECT( window ), "tb_file_save", w ); gtk_toolbar_append_space( GTK_TOOLBAR( toolbar ) ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "x-axis Flip" ), "", - new_pixmap( window, "brush_flipx.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("brush_flipx.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_BRUSH_FLIPX ) ); g_object_set_data( G_OBJECT( window ), "tb_brush_flipx", w ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "x-axis Rotate" ), "", - new_pixmap( window, "brush_rotatex.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("brush_rotatex.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_BRUSH_ROTATEX ) ); g_object_set_data( G_OBJECT( window ), "tb_brush_rotatex", w ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "y-axis Flip" ), "", - new_pixmap( window, "brush_flipy.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("brush_flipy.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_BRUSH_FLIPY ) ); g_object_set_data( G_OBJECT( window ), "tb_brush_flipy", w ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "y-axis Rotate" ), "", - new_pixmap( window, "brush_rotatey.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("brush_rotatey.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_BRUSH_ROTATEY ) ); g_object_set_data( G_OBJECT( window ), "tb_brush_rotatey", w ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "z-axis Flip" ), "", - new_pixmap( window, "brush_flipz.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("brush_flipz.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_BRUSH_FLIPZ ) ); g_object_set_data( G_OBJECT( window ), "tb_brush_flipz", w ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "z-axis Rotate" ), "", - new_pixmap( window, "brush_rotatez.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("brush_rotatez.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_BRUSH_ROTATEZ ) ); g_object_set_data( G_OBJECT( window ), "tb_brush_rotatez", w ); gtk_toolbar_append_space( GTK_TOOLBAR( toolbar ) ); if ( g_PrefsDlg.m_bWideToolbar ) { w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "Complete Tall" ), "", - new_pixmap( window, "selection_selectcompletetall.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("selection_selectcompletetall.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SELECTION_SELECTCOMPLETETALL ) ); g_object_set_data( G_OBJECT( window ), "tb_selection_selectcompletetall", w ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "Select Touching" ), "", - new_pixmap( window, "selection_selecttouching.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("selection_selecttouching.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SELECTION_SELECTTOUCHING ) ); g_object_set_data( G_OBJECT( window ), "tb_selection_selecttouching", w ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "Select Partial Tall" ), "", - new_pixmap( window, "selection_selectpartialtall.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("selection_selectpartialtall.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SELECTION_SELECTPARTIALTALL ) ); g_object_set_data( G_OBJECT( window ), "tb_selection_selectpartialtall", w ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "Select Inside" ), "", - new_pixmap( window, "selection_selectinside.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("selection_selectinside.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SELECTION_SELECTINSIDE ) ); g_object_set_data( G_OBJECT( window ), "tb_selection_selectinside", w ); } else { w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "Selection" ), "", - new_pixmap( window, "popup_selection.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("popup_selection.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_POPUP_SELECTION ) ); g_object_set_data( G_OBJECT( window ), "tb_popup_selection", w ); } gtk_toolbar_append_space( GTK_TOOLBAR( toolbar ) ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "CSG Subtract" ), "", - new_pixmap( window, "selection_csgsubtract.bmp" ), + new_image_icon("selection_csgsubtract.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SELECTION_CSGSUBTRACT ) ); g_object_set_data( G_OBJECT( window ), "tb_selection_csgsubtract", w ); if ( g_PrefsDlg.m_bWideToolbar ) { w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "CSG Merge" ), "", - new_pixmap( window, "selection_csgmerge.bmp" ), + new_image_icon("selection_csgmerge.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SELECTION_CSGMERGE ) ); g_object_set_data( G_OBJECT( window ), "tb_selection_csgmerge", w ); } w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "Hollow" ), "", - new_pixmap( window, "selection_makehollow.bmp" ), + new_image_icon("selection_makehollow.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SELECTION_MAKEHOLLOW ) ); g_object_set_data( G_OBJECT( window ), "tb_selection_makehollow", w ); if ( g_PrefsDlg.m_bWideToolbar ) { w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Clipper" ), "", new_pixmap( window, "view_clipper.bmp" ), + "", _( "Clipper" ), "", new_image_icon("view_clipper.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_VIEW_CLIPPER ) ); g_object_set_data( G_OBJECT( window ), "tb_view_clipper", w ); } @@ -1752,7 +1752,7 @@ void MainFrame::create_main_toolbar( GtkWidget *window, GtkWidget *vbox ){ gtk_toolbar_append_space( GTK_TOOLBAR( toolbar ) ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "Change views" ), "", - new_pixmap( window, "view_change.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("view_change.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_VIEW_CHANGE ) ); g_object_set_data( G_OBJECT( window ), "tb_view_change", w ); @@ -1761,14 +1761,14 @@ void MainFrame::create_main_toolbar( GtkWidget *window, GtkWidget *vbox ){ } w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "Texture view mode" ), "", - new_pixmap( window, "textures_popup.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("textures_popup.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_TEXTURES_POPUP ) ); g_object_set_data( G_OBJECT( window ), "tb_textures_popup", w ); if ( g_PrefsDlg.m_bWideToolbar ) { w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, "", _( "Cubic clip the camera view" ), "", - new_pixmap( window, "view_cubicclipping.bmp" ), + new_image_icon("view_cubicclipping.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_VIEW_CUBICCLIPPING ) ); g_object_set_data( G_OBJECT( window ), "tb_view_cubicclipping", w ); } @@ -1777,50 +1777,50 @@ void MainFrame::create_main_toolbar( GtkWidget *window, GtkWidget *vbox ){ if ( !g_PrefsDlg.m_bWideToolbar ) { w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Camera preview" ), "", new_pixmap( window, "view_cameratoggle.bmp" ), + "", _( "Camera preview" ), "", new_image_icon("view_cameratoggle.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_VIEW_CAMERATOGGLE ) ); g_object_set_data( G_OBJECT( window ), "tb_view_cameratoggle", w ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", "Update Camera", "", - new_pixmap( window, "view_cameraupdate.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("view_cameraupdate.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_VIEW_CAMERAUPDATE ) ); g_object_set_data( G_OBJECT( window ), "tb_view_cameraupdate", w ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, "", _( "Cubic clip the camera view" ), "", - new_pixmap( window, "view_cubicclipping.bmp" ), + new_image_icon("view_cubicclipping.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_VIEW_CUBICCLIPPING ) ); g_object_set_data( G_OBJECT( window ), "tb_view_cubicclipping", w ); gtk_toolbar_append_space( GTK_TOOLBAR( toolbar ) ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Entity inspector" ), "", new_pixmap( window, "view_entity.bmp" ), + "", _( "Entity inspector" ), "", new_image_icon("view_entity.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_VIEW_ENTITY ) ); g_object_set_data( G_OBJECT( window ), "tb_view_entity", w ); gtk_toolbar_append_space( GTK_TOOLBAR( toolbar ) ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Clipper" ), "", new_pixmap( window, "view_clipper.bmp" ), + "", _( "Clipper" ), "", new_image_icon("view_clipper.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_VIEW_CLIPPER ) ); g_object_set_data( G_OBJECT( window ), "tb_view_clipper", w ); gtk_toolbar_append_space( GTK_TOOLBAR( toolbar ) ); } w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Free Rotation" ), "", new_pixmap( window, "select_mouserotate.bmp" ), + "", _( "Free Rotation" ), "", new_image_icon("select_mouserotate.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SELECT_MOUSEROTATE ) ); g_object_set_data( G_OBJECT( window ), "tb_select_mouserotate", w ); gtk_toolbar_append_space( GTK_TOOLBAR( toolbar ) ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Free Scaling" ), "", new_pixmap( window, "select_mousescale.bmp" ), + "", _( "Free Scaling" ), "", new_image_icon("select_mousescale.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SELECT_MOUSESCALE ) ); g_object_set_data( G_OBJECT( window ), "tb_select_mousescale", w ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Disable Scaling Along X" ), "", new_pixmap( window, "scalelockx.bmp" ), + "", _( "Disable Scaling Along X" ), "", new_image_icon("scalelockx.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SCALELOCKX ) ); g_object_set_data( G_OBJECT( window ), "tb_scalelockx", w ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Disable Scaling Along Y" ), "", new_pixmap( window, "scalelocky.bmp" ), + "", _( "Disable Scaling Along Y" ), "", new_image_icon("scalelocky.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SCALELOCKY ) ); g_object_set_data( G_OBJECT( window ), "tb_scalelocky", w ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, - "", _( "Disable Scaling Along Z" ), "", new_pixmap( window, "scalelockz.bmp" ), + "", _( "Disable Scaling Along Z" ), "", new_image_icon("scalelockz.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SCALELOCKZ ) ); g_object_set_data( G_OBJECT( window ), "tb_scalelockz", w ); @@ -1828,7 +1828,7 @@ void MainFrame::create_main_toolbar( GtkWidget *window, GtkWidget *vbox ){ gtk_toolbar_append_space( GTK_TOOLBAR( toolbar ) ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, "", _( "Don't select model brushes" ), "", - new_pixmap( window, "dontselectmodel.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("dontselectmodel.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_DONTSELECTMODEL ) ); gtk_toolbar_append_space( GTK_TOOLBAR( toolbar ) ); g_object_set_data( G_OBJECT( window ), "tb_dontselectmodel", w ); @@ -1836,7 +1836,7 @@ void MainFrame::create_main_toolbar( GtkWidget *window, GtkWidget *vbox ){ if ( !g_pGameDescription->mNoPatch ) { w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, "", _( "Don't select curved brushes" ), "", - new_pixmap( window, "dontselectcurve.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("dontselectcurve.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_DONTSELECTCURVE ) ); g_object_set_data( G_OBJECT( window ), "tb_dontselectcurve", w ); } @@ -1846,31 +1846,31 @@ void MainFrame::create_main_toolbar( GtkWidget *window, GtkWidget *vbox ){ if ( g_PrefsDlg.m_bPatchToolbar ) { w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, "", _( "Show patch bounding box" ), "", - new_pixmap( window, "patch_showboundingbox.bmp" ), + new_image_icon("patch_showboundingbox.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_PATCH_SHOWBOUNDINGBOX ) ); g_object_set_data( G_OBJECT( window ), "tb_patch_showboundingbox", w ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, "", _( "Show patches as wireframes" ), "", - new_pixmap( window, "patch_wireframe.bmp" ), + new_image_icon("patch_wireframe.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_PATCH_WIREFRAME ) ); g_object_set_data( G_OBJECT( window ), "tb_patch_wireframe", w ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, "", _( "Patch Bend mode" ), "", - new_pixmap( window, "patch_bend.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("patch_bend.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_PATCH_BEND ) ); g_object_set_data( G_OBJECT( window ), "tb_patch_bend", w ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "Put caps on the current patch" ), "", - new_pixmap( window, "curve_cap.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("curve_cap.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_CURVE_CAP ) ); g_object_set_data( G_OBJECT( window ), "tb_curve_cap", w ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, "", _( "Welds equal patch points during moves" ), "", - new_pixmap( window, "patch_weld.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("patch_weld.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_PATCH_WELD ) ); g_object_set_data( G_OBJECT( window ), "tb_patch_weld", w ); w = gtk_toolbar_append_element( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, "", _( "Selects drill down rows and columns" ), "", - new_pixmap( window, "patch_drilldown.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("patch_drilldown.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_PATCH_DRILLDOWN ) ); g_object_set_data( G_OBJECT( window ), "tb_patch_drilldown", w ); } @@ -1878,7 +1878,7 @@ void MainFrame::create_main_toolbar( GtkWidget *window, GtkWidget *vbox ){ if ( g_PrefsDlg.m_bWideToolbar ) { gtk_toolbar_append_space( GTK_TOOLBAR( toolbar ) ); w = gtk_toolbar_append_item( GTK_TOOLBAR( toolbar ), "", _( "Show Entities as" ), "", - new_pixmap( window, "show_entities.bmp" ), GTK_SIGNAL_FUNC( HandleCommand ), + new_image_icon("show_entities.png"), GTK_SIGNAL_FUNC( HandleCommand ), GINT_TO_POINTER( ID_SHOW_ENTITIES ) ); g_object_set_data( G_OBJECT( window ), "tb_show_entities", w ); } From 1b1a7ed1082087b2fb16f4e43b75478892991465 Mon Sep 17 00:00:00 2001 From: freemancw Date: Wed, 19 Dec 2012 01:37:36 -0500 Subject: [PATCH 8/9] changed bmps to pngs --- install/bitmaps/brush_flipx.bmp | Bin 238 -> 0 bytes install/bitmaps/brush_flipx.png | Bin 0 -> 313 bytes install/bitmaps/brush_flipy.bmp | Bin 238 -> 0 bytes install/bitmaps/brush_flipy.png | Bin 0 -> 310 bytes install/bitmaps/brush_flipz.bmp | Bin 238 -> 0 bytes install/bitmaps/brush_flipz.png | Bin 0 -> 313 bytes install/bitmaps/brush_rotatex.bmp | Bin 238 -> 0 bytes install/bitmaps/brush_rotatex.png | Bin 0 -> 315 bytes install/bitmaps/brush_rotatey.bmp | Bin 238 -> 0 bytes install/bitmaps/brush_rotatey.png | Bin 0 -> 312 bytes install/bitmaps/brush_rotatez.bmp | Bin 238 -> 0 bytes install/bitmaps/brush_rotatez.png | Bin 0 -> 314 bytes install/bitmaps/cap_bevel.bmp | Bin 154 -> 0 bytes install/bitmaps/cap_bevel.png | Bin 0 -> 286 bytes install/bitmaps/cap_endcap.bmp | Bin 154 -> 0 bytes install/bitmaps/cap_endcap.png | Bin 0 -> 288 bytes install/bitmaps/cap_ibevel.bmp | Bin 154 -> 0 bytes install/bitmaps/cap_ibevel.png | Bin 0 -> 286 bytes install/bitmaps/cap_iendcap.bmp | Bin 154 -> 0 bytes install/bitmaps/cap_iendcap.png | Bin 0 -> 288 bytes install/bitmaps/curve_cap.bmp | Bin 238 -> 0 bytes install/bitmaps/curve_cap.png | Bin 0 -> 343 bytes install/bitmaps/dontselectcurve.bmp | Bin 238 -> 0 bytes install/bitmaps/dontselectcurve.png | Bin 0 -> 333 bytes install/bitmaps/dontselectmodel.bmp | Bin 238 -> 0 bytes install/bitmaps/dontselectmodel.png | Bin 0 -> 332 bytes install/bitmaps/file_open.bmp | Bin 238 -> 0 bytes install/bitmaps/file_open.png | Bin 0 -> 338 bytes install/bitmaps/file_save.bmp | Bin 238 -> 0 bytes install/bitmaps/file_save.png | Bin 0 -> 310 bytes install/bitmaps/icon.bmp | Bin 630 -> 0 bytes install/bitmaps/icon.png | Bin 0 -> 407 bytes install/bitmaps/patch_bend.bmp | Bin 238 -> 0 bytes install/bitmaps/patch_bend.png | Bin 0 -> 337 bytes install/bitmaps/patch_drilldown.bmp | Bin 238 -> 0 bytes install/bitmaps/patch_drilldown.png | Bin 0 -> 302 bytes install/bitmaps/patch_insdel.bmp | Bin 238 -> 0 bytes install/bitmaps/patch_insdel.png | Bin 0 -> 321 bytes install/bitmaps/patch_showboundingbox.bmp | Bin 238 -> 0 bytes install/bitmaps/patch_showboundingbox.png | Bin 0 -> 328 bytes install/bitmaps/patch_weld.bmp | Bin 238 -> 0 bytes install/bitmaps/patch_weld.png | Bin 0 -> 337 bytes install/bitmaps/patch_wireframe.bmp | Bin 238 -> 0 bytes install/bitmaps/patch_wireframe.png | Bin 0 -> 321 bytes install/bitmaps/popup_selection.bmp | Bin 238 -> 0 bytes install/bitmaps/popup_selection.png | Bin 0 -> 338 bytes install/bitmaps/scalelockx.bmp | Bin 238 -> 0 bytes install/bitmaps/scalelockx.png | Bin 0 -> 303 bytes install/bitmaps/scalelocky.bmp | Bin 238 -> 0 bytes install/bitmaps/scalelocky.png | Bin 0 -> 305 bytes install/bitmaps/scalelockz.bmp | Bin 238 -> 0 bytes install/bitmaps/scalelockz.png | Bin 0 -> 306 bytes install/bitmaps/select_mouserotate.bmp | Bin 238 -> 0 bytes install/bitmaps/select_mouserotate.png | Bin 0 -> 358 bytes install/bitmaps/select_mousescale.bmp | Bin 238 -> 0 bytes install/bitmaps/select_mousescale.png | Bin 0 -> 318 bytes install/bitmaps/selection_csgmerge.bmp | Bin 238 -> 0 bytes install/bitmaps/selection_csgmerge.png | Bin 0 -> 336 bytes install/bitmaps/selection_csgsubtract.bmp | Bin 238 -> 0 bytes install/bitmaps/selection_csgsubtract.png | Bin 0 -> 331 bytes install/bitmaps/selection_makehollow.bmp | Bin 238 -> 0 bytes install/bitmaps/selection_makehollow.png | Bin 0 -> 301 bytes .../bitmaps/selection_selectcompletetall.bmp | Bin 238 -> 0 bytes .../bitmaps/selection_selectcompletetall.png | Bin 0 -> 317 bytes install/bitmaps/selection_selectinside.bmp | Bin 238 -> 0 bytes install/bitmaps/selection_selectinside.png | Bin 0 -> 322 bytes .../bitmaps/selection_selectpartialtall.bmp | Bin 238 -> 0 bytes .../bitmaps/selection_selectpartialtall.png | Bin 0 -> 341 bytes install/bitmaps/selection_selecttouching.bmp | Bin 238 -> 0 bytes install/bitmaps/selection_selecttouching.png | Bin 0 -> 317 bytes install/bitmaps/show_entities.bmp | Bin 238 -> 0 bytes install/bitmaps/show_entities.png | Bin 0 -> 319 bytes install/bitmaps/splash.png | Bin 0 -> 272384 bytes install/bitmaps/textures_popup.bmp | Bin 238 -> 0 bytes install/bitmaps/textures_popup.png | Bin 0 -> 366 bytes install/bitmaps/view_cameratoggle.bmp | Bin 238 -> 0 bytes install/bitmaps/view_cameratoggle.png | Bin 0 -> 372 bytes install/bitmaps/view_cameraupdate.bmp | Bin 238 -> 0 bytes install/bitmaps/view_cameraupdate.png | Bin 0 -> 355 bytes install/bitmaps/view_change.bmp | Bin 126 -> 0 bytes install/bitmaps/view_change.png | Bin 0 -> 329 bytes install/bitmaps/view_clipper.bmp | Bin 238 -> 0 bytes install/bitmaps/view_clipper.png | Bin 0 -> 316 bytes install/bitmaps/view_cubicclipping.bmp | Bin 238 -> 0 bytes install/bitmaps/view_cubicclipping.png | Bin 0 -> 350 bytes install/bitmaps/view_entity.bmp | Bin 238 -> 0 bytes install/bitmaps/view_entity.png | Bin 0 -> 303 bytes install/bitmaps/window1.bmp | Bin 614 -> 0 bytes install/bitmaps/window1.png | Bin 0 -> 377 bytes install/bitmaps/window2.bmp | Bin 614 -> 0 bytes install/bitmaps/window2.png | Bin 0 -> 383 bytes install/bitmaps/window3.bmp | Bin 614 -> 0 bytes install/bitmaps/window3.png | Bin 0 -> 364 bytes install/bitmaps/window4.bmp | Bin 614 -> 0 bytes install/bitmaps/window4.png | Bin 0 -> 375 bytes radiant/gtkdlgs.cpp | 8 ++++---- radiant/gtkmisc.cpp | 12 ------------ radiant/preferences.cpp | 8 ++++---- 98 files changed, 8 insertions(+), 20 deletions(-) delete mode 100644 install/bitmaps/brush_flipx.bmp create mode 100644 install/bitmaps/brush_flipx.png delete mode 100644 install/bitmaps/brush_flipy.bmp create mode 100644 install/bitmaps/brush_flipy.png delete mode 100644 install/bitmaps/brush_flipz.bmp create mode 100644 install/bitmaps/brush_flipz.png delete mode 100644 install/bitmaps/brush_rotatex.bmp create mode 100644 install/bitmaps/brush_rotatex.png delete mode 100644 install/bitmaps/brush_rotatey.bmp create mode 100644 install/bitmaps/brush_rotatey.png delete mode 100644 install/bitmaps/brush_rotatez.bmp create mode 100644 install/bitmaps/brush_rotatez.png delete mode 100644 install/bitmaps/cap_bevel.bmp create mode 100644 install/bitmaps/cap_bevel.png delete mode 100644 install/bitmaps/cap_endcap.bmp create mode 100644 install/bitmaps/cap_endcap.png delete mode 100644 install/bitmaps/cap_ibevel.bmp create mode 100644 install/bitmaps/cap_ibevel.png delete mode 100644 install/bitmaps/cap_iendcap.bmp create mode 100644 install/bitmaps/cap_iendcap.png delete mode 100644 install/bitmaps/curve_cap.bmp create mode 100644 install/bitmaps/curve_cap.png delete mode 100644 install/bitmaps/dontselectcurve.bmp create mode 100644 install/bitmaps/dontselectcurve.png delete mode 100644 install/bitmaps/dontselectmodel.bmp create mode 100644 install/bitmaps/dontselectmodel.png delete mode 100644 install/bitmaps/file_open.bmp create mode 100644 install/bitmaps/file_open.png delete mode 100644 install/bitmaps/file_save.bmp create mode 100644 install/bitmaps/file_save.png delete mode 100644 install/bitmaps/icon.bmp create mode 100644 install/bitmaps/icon.png delete mode 100644 install/bitmaps/patch_bend.bmp create mode 100644 install/bitmaps/patch_bend.png delete mode 100644 install/bitmaps/patch_drilldown.bmp create mode 100644 install/bitmaps/patch_drilldown.png delete mode 100644 install/bitmaps/patch_insdel.bmp create mode 100644 install/bitmaps/patch_insdel.png delete mode 100644 install/bitmaps/patch_showboundingbox.bmp create mode 100644 install/bitmaps/patch_showboundingbox.png delete mode 100644 install/bitmaps/patch_weld.bmp create mode 100644 install/bitmaps/patch_weld.png delete mode 100644 install/bitmaps/patch_wireframe.bmp create mode 100644 install/bitmaps/patch_wireframe.png delete mode 100644 install/bitmaps/popup_selection.bmp create mode 100644 install/bitmaps/popup_selection.png delete mode 100644 install/bitmaps/scalelockx.bmp create mode 100644 install/bitmaps/scalelockx.png delete mode 100644 install/bitmaps/scalelocky.bmp create mode 100644 install/bitmaps/scalelocky.png delete mode 100644 install/bitmaps/scalelockz.bmp create mode 100644 install/bitmaps/scalelockz.png delete mode 100644 install/bitmaps/select_mouserotate.bmp create mode 100644 install/bitmaps/select_mouserotate.png delete mode 100644 install/bitmaps/select_mousescale.bmp create mode 100644 install/bitmaps/select_mousescale.png delete mode 100644 install/bitmaps/selection_csgmerge.bmp create mode 100644 install/bitmaps/selection_csgmerge.png delete mode 100644 install/bitmaps/selection_csgsubtract.bmp create mode 100644 install/bitmaps/selection_csgsubtract.png delete mode 100644 install/bitmaps/selection_makehollow.bmp create mode 100644 install/bitmaps/selection_makehollow.png delete mode 100644 install/bitmaps/selection_selectcompletetall.bmp create mode 100644 install/bitmaps/selection_selectcompletetall.png delete mode 100644 install/bitmaps/selection_selectinside.bmp create mode 100644 install/bitmaps/selection_selectinside.png delete mode 100644 install/bitmaps/selection_selectpartialtall.bmp create mode 100644 install/bitmaps/selection_selectpartialtall.png delete mode 100644 install/bitmaps/selection_selecttouching.bmp create mode 100644 install/bitmaps/selection_selecttouching.png delete mode 100644 install/bitmaps/show_entities.bmp create mode 100644 install/bitmaps/show_entities.png create mode 100644 install/bitmaps/splash.png delete mode 100644 install/bitmaps/textures_popup.bmp create mode 100644 install/bitmaps/textures_popup.png delete mode 100644 install/bitmaps/view_cameratoggle.bmp create mode 100644 install/bitmaps/view_cameratoggle.png delete mode 100644 install/bitmaps/view_cameraupdate.bmp create mode 100644 install/bitmaps/view_cameraupdate.png delete mode 100644 install/bitmaps/view_change.bmp create mode 100644 install/bitmaps/view_change.png delete mode 100644 install/bitmaps/view_clipper.bmp create mode 100644 install/bitmaps/view_clipper.png delete mode 100644 install/bitmaps/view_cubicclipping.bmp create mode 100644 install/bitmaps/view_cubicclipping.png delete mode 100644 install/bitmaps/view_entity.bmp create mode 100644 install/bitmaps/view_entity.png delete mode 100644 install/bitmaps/window1.bmp create mode 100644 install/bitmaps/window1.png delete mode 100644 install/bitmaps/window2.bmp create mode 100644 install/bitmaps/window2.png delete mode 100644 install/bitmaps/window3.bmp create mode 100644 install/bitmaps/window3.png delete mode 100644 install/bitmaps/window4.bmp create mode 100644 install/bitmaps/window4.png diff --git a/install/bitmaps/brush_flipx.bmp b/install/bitmaps/brush_flipx.bmp deleted file mode 100644 index 91f3d669dda151af543b10ab28f4e294cc86c05e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!uh(7^B?2pbw27#eUX!6PmxC@7!^ByiEfg2Dod fhJrw7D4-}TEGQ@pQg4W$q2h3L1k3~31F{SNlY0=` diff --git a/install/bitmaps/brush_flipx.png b/install/bitmaps/brush_flipx.png new file mode 100644 index 0000000000000000000000000000000000000000..39a98da36e3b9e0e0c9486525ad8d83a09f26797 GIT binary patch literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakhs5AX?b{m<~fgMp!=14R7) z|NmIy?khkk#*!evUYmi~;Jfk2Ldr;B3<$Mxg{AV_Ioa${xUNn=ZM zV`XMzW@BS!c4JLT+aM5dieV<>WG4-^93zHzTFmFt><+O5^{SS*MwFx^mZVxG7o`Fz z1|tJQBV7=(3^6dUGPSTWFxNFOwK6b>Y+ql5q9HdwB{QuOMT4Q0AqYVYmi~;Jfk2L~r;B3<$Mxg{AV_Ioa${xUNn=Y> zILy#_Fd=~<)qwe`fw8kBn}BNoTjFHKJ1xw!O>#LyfEraxTq8_W0QiA{h literal 0 HcmV?d00001 diff --git a/install/bitmaps/brush_flipz.bmp b/install/bitmaps/brush_flipz.bmp deleted file mode 100644 index 38c20ff2519d5f02b80269ef76928be6b6d93f65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!uh(7^B?2pbw27#eUX!6PmxC@7!^BnZ*M#>N7Q f#>R$%h9LFAfYmi~;Jfk2Ldr;B3<$Mxg{AV_Ioa${xUNlQ+4 zV@*zGR=C^{*eI#PGKq=1LE?-?7Kb!PGSHQ$S<-K5%{>6rt6Jh3QIe8al4_M)lnSI6 zj0_BobV0~6#K6GH)WXWZT-U(V%D^D9eSHy%hTQy=%(O}r4Te^RAOz7M_G|HSpaup{ LS3j3^P6K|>wkv-3=AC|3=IGO z|6eWKdKDY3CJ<^ba4#fxSpKAz$hlBa8P3lgNIc? z1BaSIxI;E$K+|f*6X{6}jO>gGiVql6A|w-ZHb^itGb}J>{-qZzb_8gEYKdz^NlIc# zs#S7PDv)9@GB7mK1tH520|P5l3o8S2T?11q1B1x+^+hNea`RI%(<)Ik7+M*E5JZF6 Tuf@xO8W=oX{an^LB{Ts5W7tw7 literal 0 HcmV?d00001 diff --git a/install/bitmaps/brush_rotatey.bmp b/install/bitmaps/brush_rotatey.bmp deleted file mode 100644 index efcfec247e921f3e77c190221abfb2c9db02c3c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!uh(7^B?2pe!o6A%|f0ZI^_5QI`vgi1pBf`U+a jm>vNT1E`3B5ljQ!3*m#rg@BqM!ipeKpfZ?gAf6xq8^jHU diff --git a/install/bitmaps/brush_rotatey.png b/install/bitmaps/brush_rotatey.png new file mode 100644 index 0000000000000000000000000000000000000000..e8ead705fa19147d884f6aa92815e6d4159a0654 GIT binary patch literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q#0(@S$S&Ulq!^2X+?^QKos)S9K|>wkv-3=AC|3=IGO z|6eWKdKDY3CJ<>ba4#fxSpKAz$hkWu(6@BF|dL8 zL`edZV3fl?=5Xc-Cyy|&Gdi5%NNO=S5RqWW#>O!J0`m;M1& literal 0 HcmV?d00001 diff --git a/install/bitmaps/brush_rotatez.bmp b/install/bitmaps/brush_rotatez.bmp deleted file mode 100644 index fcea0814fb8ebcd4c8faae2fc244f9f857e8be36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!uh(7^B?2pe!o6A%|f0z!g9V4jem5`-3l@)cp? n5E`yU0Lo%u6ckiaQUtnJ5NrfkTv1REC=3z+ih|`q6bt|W69Ely diff --git a/install/bitmaps/brush_rotatez.png b/install/bitmaps/brush_rotatez.png new file mode 100644 index 0000000000000000000000000000000000000000..a849fbfd4726ff11d5d4bce5bd93cc2fa718f105 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q#0(@S$S&Ulq!^2X+?^QKos)S9K|>wkv-3=AC|3=IGO z|6eWKdKDY3CJ<=ba4#fxSpKAz<5nSKyw3whed&b zfq{`h?)FK#IZ0z|cq+ge*f046IBotPIR`4NR>J3?kdt7olj#%}>cpt3=UYXk`dO5Dj9# S7B2^CVDNPHb6Mw<&;$UCRZ_M9 literal 0 HcmV?d00001 diff --git a/install/bitmaps/cap_bevel.bmp b/install/bitmaps/cap_bevel.bmp deleted file mode 100644 index 0de324422fd7dba22730213de7b6f25766141550..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154 zcmZ?roy7nFc0fu4h$Vqo9Ecf#5Ep0wO8o!-pWz3PM#q1EV#xR(kPpKDLAU{m4*=;0 VK>PuSfr|eF75oPZ0L^TGVE}qrD%t=5 diff --git a/install/bitmaps/cap_bevel.png b/install/bitmaps/cap_bevel.png new file mode 100644 index 0000000000000000000000000000000000000000..d190d1c98b9cff8f35fc737d9bf87d167cbe6f6d GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoZ$P6Tp=#~WlDaPU;cPEB*=VV?2ISR=hLB0$O zRcZ_j4J`}|zkoswFBlj~4Hy_+B``2p%>ZgHn6oR;1}MQ6;1lBd|NnoWk{wk6aX=Bq zByV>Yh7ML)4xpKJkzeAItpFy(X zjY0tiyLhdIy$MX|Ky9iet`Q|Ei6yC4$wjF^iowXh&`1}AEJF+otV}Je49s;6OsxzI pBHPy&p=ij>PsvQHMA2YqWe7qL4Pw6*F9&L1@O1TaS?83{1OR6nOs)U` literal 0 HcmV?d00001 diff --git a/install/bitmaps/cap_endcap.bmp b/install/bitmaps/cap_endcap.bmp deleted file mode 100644 index 8e6c7f2a36d1b44edd04cbdcc55cbc5845ba75f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154 zcmZ?roy7nFc0fu4h$Vqo9Ecf#5Ep0wO8o!-pWy%lV*{!90RwXb41Zu?Z}`E$-|&Y) SzTqE(eZ&8Q{~Lhjf-wN=L@Go8 diff --git a/install/bitmaps/cap_endcap.png b/install/bitmaps/cap_endcap.png new file mode 100644 index 0000000000000000000000000000000000000000..45e5c0d7760daaf3d2c84a1ae62763be678cff24 GIT binary patch literal 288 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoZ$P6Tp=#~WlDaPU;cPEB*=VV?2ISR=hLB0$O zRcZ_j4J`}|zkoswFBlj~4Hy_+B``2p%>ZgHn6oR;1}MQ6;1lBd|NnoWk{wk6aX=Bq zByV>Yh7ML)4Sujnc3LB{g;=J`0@XLLc)aq|Ji45 z`2W9OiHX65Tig6s->DTqjjAQC5hW>!C8<`)MX5lF!N|bSNEd`GLktY8Of9So%ykV+ stqcqz+t(MNXvob^$xN$6(O_t02tp7IV!swI2WnvOboFyt=akR{0I5V$hX4Qo literal 0 HcmV?d00001 diff --git a/install/bitmaps/cap_ibevel.bmp b/install/bitmaps/cap_ibevel.bmp deleted file mode 100644 index ccab98daa2eacb91123dba59b83ff5837d279634..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154 zcmZ?roy7nFc0fu4h$Vqo9Ecf#5Ep0wO8o!-pMm}V{|0o-3=~7gj6gmJ12r@-)I+g7 Vkd_BxejsKBVn!flkOvtH2LR8SFW&$F diff --git a/install/bitmaps/cap_ibevel.png b/install/bitmaps/cap_ibevel.png new file mode 100644 index 0000000000000000000000000000000000000000..cd84c785d5e8dc0763d5385e70503aece51ec82f GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoZ$P6Tp=#~WlDaPU;cPEB*=VV?2ISR=hLB0$O zRcZ_j4J`}|zkoswFBlj~4Hy_+B``2p%>ZgHn6oR;1}MQ6;1l8sl>h(#zej`SGoT1# zlDE4H<9{aIdq9r1r;B3<$Mxg{2lh|AJUl)B8(EAy9=lCO~3#E literal 0 HcmV?d00001 diff --git a/install/bitmaps/cap_iendcap.bmp b/install/bitmaps/cap_iendcap.bmp deleted file mode 100644 index 9640c9ae8c97b9c5d183743b93a805bbbabc9f48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154 zcmZ?roy7nFc0fu4h$Vqo9Ecf#5Ep0wO8o!-pTYkBKa#Qh|33^c%>Vxf1N;9E49x!@ PFfjfZgHn6oR;1}MQ6;1l8sl>h(#zej`SGoT1# zlDE4H<9{aIdq9q^r;B3<$Mxg{2L@|}v_JI?jg3G5GqbUM`!6pc@#FvhgoFwI|Fh5B z@c)0m5)*?9x3>AOzEdlJ8dXbNBT7;dOH!?pi&B9UgOP!ukuC^Xh8P%FnOayGnClvt sS{WEbwy!Tj(U6;;l9^VCqQTJ05QHEa#C|Pa4%EQl>FVdQ&MBb@098LznE(I) literal 0 HcmV?d00001 diff --git a/install/bitmaps/curve_cap.bmp b/install/bitmaps/curve_cap.bmp deleted file mode 100644 index e9d00a21abb96d045cf066f79423f70e13310a76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ{bu?>JQ3M!%P$U zNR?V^^r9+|>V3ri?@?k%5JwhEi9*44#)MSiviC!AQhE0sKNck;D4f}dq&aVc^!G%@ FcYQaM77YLZ diff --git a/install/bitmaps/curve_cap.png b/install/bitmaps/curve_cap.png new file mode 100644 index 0000000000000000000000000000000000000000..52ff4fdcaa3882aaf592df5f5f5b3125e72cb63b GIT binary patch literal 343 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakfG3h)VW{m<~f1IT9R=wRqL zbLPzd|NkXYG}C|rj3q&S!3+-1Zh$#S-tI1}Kk{E52g-PRx;Tb#Tu)ALU~Fmr;?aP~`}Gq{Jka zP*BLCoyx#4e+!4>yHX}Eps}hYt`Q|Ei6yC4$wjF^iowXh&`1}AEJF+otV}Je49s;6 tOsxzIBHPy&p=ij>PsvQHMA2YqWe7qL4Pw6*F9&L1@O1TaS?83{1OVl>T`B+o literal 0 HcmV?d00001 diff --git a/install/bitmaps/dontselectcurve.bmp b/install/bitmaps/dontselectcurve.bmp deleted file mode 100644 index a7b9795453902fefcccac030a4fea52bed91b66e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmaitK@Na02n3hLyV4f`C*R@S?|AmbexWXHk0z49vO|F5d@$mo?97e1G6f6kcQiB( z8t(7$FV%d)SE4BbbU>JZwgcKQS3!iqw3dRYW^h&daIIcTK)Kh~vy3wdgmxax1}}ue B3CaKf diff --git a/install/bitmaps/dontselectcurve.png b/install/bitmaps/dontselectcurve.png new file mode 100644 index 0000000000000000000000000000000000000000..7c4373d7ffebb1feed5fff1f496eae9948238d4c GIT binary patch literal 333 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q#0(@S$S&Ulq!^2X+?^QKos)S9K|>wkv-|AB0Vj{pDv zH?LOB0x}s(g8YIR9G=}s196hP-CdXkd3-(rIqse=jv*Y^lM@&i1=-jb*#g7 zxHTr-X-owgsaoP1QIe8al4_M)lnSI6j0_BobV0~6#K6GH)WXWZT-U(V%D^D9eSHy% jhTQy=%(O}r4Te^RAOz7M_G|HSpaup{S3j3^P6=)|NdTb&IEIR}^&c{Yvl3kgVtI};zokz2# zL9=^4{-uZ~dgTalvkn9U$aWx0I%?uZ5+%uYWdI<2MfO4(wAa_8j57gT_B$Ajy{K|>wkv-|AB0Vj{pDv zH?LOB0x}s(g8YIR9G=}s196hP-CdXkd3-(rIc}aVjv*Y^lM@&i1=-jb*#gJQ3;-zp4B;SZi0+#q2Okt^9FTKQB9X{^CM!MpnV*WX!6&CX3>cf* E1G1zTbpQYW diff --git a/install/bitmaps/file_open.png b/install/bitmaps/file_open.png new file mode 100644 index 0000000000000000000000000000000000000000..db1fb7bf1e54e567e2aaa267eb177b9e849bde18 GIT binary patch literal 338 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakfG3h)VW{m<|p$UJlA%>Vxk z9UTn+|Ns9}UIf(1z*rLG7tG-B>_!@hljQC0!ulis<#8a#&C|s(gyVW@&q2-u20YA5 zd6ix;9TeUo&9dpg&(z?*Rr2!b0v9soFgLjb7_mx)n{V;1 literal 0 HcmV?d00001 diff --git a/install/bitmaps/file_save.bmp b/install/bitmaps/file_save.bmp deleted file mode 100644 index 651afb68952db64d2954394aa6252f2fb3bccf3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!vMaNxiJhW|jw(9nQe5e^w87=Xw#DHsD8g3Q=y e29P)yK;#*WnG_5JfdD9umu7&zhDN3XE)M7oFs2|7nc5vn}I-%uBVG*2*>s01Q8ZyMm9IsWCejX z!v{)EKP0^r?s_S(NgQxV2s#!a$z$N1$iR?m#qDQMyy7iTqiTt3L`h0wNvc(HQ7VvP zFfuSS(gh*Q5Ca1%Qwu8tb6o>dD+7bb_Vq<58glbfGSeziG#FYLf)GT5*ssOQff^V* MUHx3vIVCg!0B~PXHUIzs literal 0 HcmV?d00001 diff --git a/install/bitmaps/icon.bmp b/install/bitmaps/icon.bmp deleted file mode 100644 index 4224da83bd4e52d7dbcec18590434482522700b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 630 zcmbtQK@!3s3=H4{68Hdc^2+c7?|!G}{#-Xem3FKiT!x+OCM3Y^zF2Blz*p=zwxJ86 zGpzK$BJB07 z6Ol&ZV|$oSI-YIv{Tf}1r125l@&eV3!LLV-vVK@QZQm6ngjzy%;1WVnEr?uWC@G>D h*BH7MY5B&Qf1j@fo!|hW_aF6`=hU6B&7Z6mJ^@8kE3g0n diff --git a/install/bitmaps/icon.png b/install/bitmaps/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b42e1c9a882ca12b9030c834eabb34a1076581be GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv#^NA%Cx&(BWL|<~l0AZa85pY6 z7#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=Y_eC&cwX!~YH-o1x=B1H=FS z|HFT8`U2!JmIV0)GdMiEkp|)`*1akU3T^vI+&d;6D&Fiel;p!^(;1$y` zPLp^0qaEen&)$})xbp#HtHdE|VgDpuF^#tlcbjt5nQw_?2bso)dv#{MdZV$>FF@8kal^^MTCX zGi&3&{bIks%%R5+&p7c!|AWW#@4aEQbLHpU9F)Hu=rYw3*NBpo#FA92Cx(QUsZa8OnVx(dw`FFq~qt)ZmIHTwrJ=AM#VMe-Z7j5w?pK7Igc!VQ z|NsB(mgnvP@)=8l{DK)Ap4~_Tagw~58U0>a5w@rM*dJ}Yonqd=o!#(c?)FK#IZ0z|cq+ge*f046IBotPIR`4NR>J p3?kdtqiV>_PsvQHMA2YqWe7qL4Pw6*F9+Jd;OXk;vd$@?2>=?lWzhft literal 0 HcmV?d00001 diff --git a/install/bitmaps/patch_drilldown.bmp b/install/bitmaps/patch_drilldown.bmp deleted file mode 100644 index 01ebcf395e8e5ba22fd2cd8c8593a19e962a9ce9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!uh(7^B?2pfRX|8Xh7BQA&v7{Od&Wr(Pd5`>2F ViIEi)faozYgwRTgsC)=d5CGF44;ugg diff --git a/install/bitmaps/patch_drilldown.png b/install/bitmaps/patch_drilldown.png new file mode 100644 index 0000000000000000000000000000000000000000..cdb6458be1ae3d2871cd4478e91596ef9772a09f GIT binary patch literal 302 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakhs5AX?b{m<~90fag_fYks0 z#{wMNfl`bmL4Lsu4$p3+fjCLt?k+6-88-ug992&j#}JO|$q5b2ii{f0o{X_Ajgsq5 zF&s+>X)&5H<$|k6*iiFVdQ&MBb@01FaP AE&u=k literal 0 HcmV?d00001 diff --git a/install/bitmaps/patch_insdel.bmp b/install/bitmaps/patch_insdel.bmp deleted file mode 100644 index b60b76b033a9991dff98b807ccafbae1a25c6298..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmaKju@QhU3=ad$qg-d1A5k>&cv($QrrTclNHIyeq1RyX$v(`Bh{sW^N*Tt rp8EIXV+WK1gXrN-Q;q^!m^$Aud(PQcWmAq;X6>6UN~F1yh_CYj3O*JL diff --git a/install/bitmaps/patch_insdel.png b/install/bitmaps/patch_insdel.png new file mode 100644 index 0000000000000000000000000000000000000000..bd104d6959f242da088f7cb424a6f566b14a4e28 GIT binary patch literal 321 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakhs5AX?b{m<~9fuW-V$Yo&o z|Np;RkiQ_1#aI&L7tG-B>_!@hljQC0!qT5{GZ4tJ@N{tu;kcfhaDbVmk>OaHOS-|K zr!1@rH48*KR%)zRvP4CLMXb^D&=*5vV*@81gM@|`?41Tc>vQ6n_=Wc_u?L!=TH+c} zl9E`GYL#4+3Zxi}3=EBQLC7-1z`)AX!pgv0*TB@uz#y`HeG!U=-29Zxv`Q2WhE|3k W1koV&Yw>cR1_n=8KbLh*2~7YxGF3JJ literal 0 HcmV?d00001 diff --git a/install/bitmaps/patch_showboundingbox.bmp b/install/bitmaps/patch_showboundingbox.bmp deleted file mode 100644 index 357cfaf16f93ebcdc1930455a13d7da75d9a7c78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!uh(7mdKI;Vst07v{wDicVgu=Z{6de^abVpq`M=WIN~g^<8e=1ndvl#y)7hODG!|HCRf zYvsxQclNQv4SNRVkdQ0_>Dd@aqMAb_yiyYkHqk+S$j+@|$l(oo=I9(Hf_O;8zgKVo D3GfuK diff --git a/install/bitmaps/patch_weld.png b/install/bitmaps/patch_weld.png new file mode 100644 index 0000000000000000000000000000000000000000..d0319d781218c8b09911f09103033edca1d07b8a GIT binary patch literal 337 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakfG3h)VW{m<~f1IT9h&%gkp z|NsBLTw{JUki}RMH9Lyd0>3!PC{xWt~$(69AB>TEhSU literal 0 HcmV?d00001 diff --git a/install/bitmaps/patch_wireframe.bmp b/install/bitmaps/patch_wireframe.bmp deleted file mode 100644 index 9804274f144f6629015ccfae9a60d24dc560d995..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmbV@u@!(o2txzUUfAFsm2=p;jK|>wkv-9SjU-&Yb!G z|G(#j`J6y;#*!evUYWmkeI;0pkvGVDeKg|g+OCe zOI#yLQW8s2t&)pUffR$0fuWHu2w8?07+9HFSQ(h>8kkxc7(}+OFGA6fo1c=IR*9m) a(8>^mAR5GeEnW`Pz~JfX=d#Wzp$PyEcUl_& literal 0 HcmV?d00001 diff --git a/install/bitmaps/popup_selection.bmp b/install/bitmaps/popup_selection.bmp deleted file mode 100644 index 676c4d98ab9c0920fe99b069a4aab4519718df59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmah>u?>JQ3=0xFRGiR-v68DE05c;O@B?!%=7nOnLTup3cI-%5w#yn$+Dwk*Kz7zs zzmTmIv%-<)tgQVXs0jPxan%HC`PB%d30UcHbVfK9fcK7t`c&^!uRCx^l!*2>)?WDr DEQ=%; diff --git a/install/bitmaps/popup_selection.png b/install/bitmaps/popup_selection.png new file mode 100644 index 0000000000000000000000000000000000000000..2c5aaa40ace37e8f125bdc5728852c084bec7b90 GIT binary patch literal 338 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakfG4e$wZ{m=0KKal+&1Ui5W z28ND~j+w_leh0D`OM?7@862M7NCR<_yxm=x1$lfv0Xfc|E{-7_*OL<(7~L2-ST>42 zSj3i~$(A-@2E&Ad3`tE_G&FYXXb3eh-eJ*j)xcnffx{I8YlkZfm^)b4EDX@gmYT!J z@a8>>#U)RcKA>@`C9V-ADTyViR>?)FK#IZ0z|cq+ge*f046IBotPIR`4NR>J3?kdt m7olj#%}>cpt3=UYXk`dO5Dj9#7B2^CVDNPHb6Mw<&;$S?cVQF& literal 0 HcmV?d00001 diff --git a/install/bitmaps/scalelockx.bmp b/install/bitmaps/scalelockx.bmp deleted file mode 100644 index 61363da20402f127cdf8042fabb5731217064d0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!vM@Sgz)|AR=;eKt0VOa%qLq!5jg)|V b7_DS%tOVi%1t2t(50i({Xy!o8huH@JK|>wkv-K<5AdK=Qx6 zfXHnihp{BcFPOpM*^M+1C&}C0g(;1@CI!gR^mK6y;kcfhz`%I%!UX}gm7%MrIdsr(>ZYG9nx1=gc)b~FEYEmt6jVMV;EJ?LWE=mPb3`PcqM!Fzm z8Dd~yWolt%V6JOmYGq&$*}lFAMMG|WN@iLmiUva~LlA;!5c{=wIZy+Gr>mdKI;Vst E01|;y@c;k- literal 0 HcmV?d00001 diff --git a/install/bitmaps/scalelocky.bmp b/install/bitmaps/scalelocky.bmp deleted file mode 100644 index 812394dbf423ea31e87bc98e26c2afcbc28d8400..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!vM@Sgz)|AR=;eKt0VOa1(aJ_jO897F bW3ark5l|se9gt^?OvB9qF&JUy!|Ve9-)IxJ diff --git a/install/bitmaps/scalelocky.png b/install/bitmaps/scalelocky.png new file mode 100644 index 0000000000000000000000000000000000000000..50d1965e1723dfe2c5e826458af7a6f103d0eae5 GIT binary patch literal 305 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q#0(@S$S&Ulq!^2X+?^QKos)S9K|>wkv-K<5AdK=Qx6 zfXHnihp{BcFPOpM*^M+1C&}C0g(;1@CI!gR_H=O!;kcfhz`%I%!UX}gm7%MrIdsOq zPCT-&)*|qH2smJlD9H7+&p{mbfV3a}KCYwZt`|BqgyV)hf9t6-Y4{85kPr zf{;eKt0VOa%qLqz}m6U*d cWh0;rNF2yTp+OR0^-y(C^=Rfm&4<|s0M#B7h5!Hn diff --git a/install/bitmaps/scalelockz.png b/install/bitmaps/scalelockz.png new file mode 100644 index 0000000000000000000000000000000000000000..c469b7fcfb28ff2e46d3ab49f53eb070e81fff36 GIT binary patch literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q#0(@S$S&Ulq!^2X+?^QKos)S9K|>wkv-K<5AdK=Qx6 zfXHnihp{BcFPOpM*^M+1C&}C0g(;1@CI!gR@pN$v;kcfhz`%I%!UX}gm7%MrIduNr z@1P^F@t5d?i2Yn{Z=6pY5^4V0#K3TUlT_OL&IPZ5`cz9?BT7;dOH!?pi&B9UgOP!u zkuC^Xh8P%FnOayGnClvtS{WEbwy!Tj(U6;;l9^VCqQTJ05QHEa#C|Pa4%EQl>FVdQ I&MBb@0Bt%~rT_o{ literal 0 HcmV?d00001 diff --git a/install/bitmaps/select_mouserotate.bmp b/install/bitmaps/select_mouserotate.bmp deleted file mode 100644 index 0139fd55cf70da5ae777eaed7033ec3e729f2039..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcma)!u?@p83}n`w4S(^^c&gCr)9x0B1Q|rS-ra1vzi7#(CCN{FwyjHL zyCtl%Z0B=_rdPY&HJ3~Gh1&ndD+7bb_Vq<58glbfGSeziG#FYLf)GT5*ssOQff^V*UHx3v IIVCg!0E5?WX8-^I literal 0 HcmV?d00001 diff --git a/install/bitmaps/select_mousescale.bmp b/install/bitmaps/select_mousescale.bmp deleted file mode 100644 index 7cac288119fdae8795eca9c793c2c08a6bc4f11e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!vM@Sov7LqkIY!~g#v7HL2b3WSuDloWw%A!B1> q1)vciTG`mx01Isb76+<#HZe936fib6aY3bBpnRYaV0ly;;ywTvWg7zk diff --git a/install/bitmaps/select_mousescale.png b/install/bitmaps/select_mousescale.png new file mode 100644 index 0000000000000000000000000000000000000000..c65b9c8cff2aa4b9c313282b1944082eb1a75f3b GIT binary patch literal 318 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakhs5AX?b{m<|p$o&5wNdE8W z=-4!S`Vt_Yu_VYZn8D%MjWiG^$=lt9DUG`(1;{b=ba4#fxSpJlAi~1T#^$Cf5OCz6 zfmxf;lt2j`Jv}|QQwnSW2Ru0s1spnDz;cuI($fPCEDVX0cm;coeeD4npjzS@QIe8a zl4_M)lnSI6j0_BobV0~6#K6GH)WXWZT-U(V%D^D9eSHy%hTQy=%(O}r4Te^RAOz7M T_G|HSpaup{S3j3^P6YV$Y8fGXK>=oCW1u+1Y`ARzVLB4u diff --git a/install/bitmaps/selection_csgmerge.png b/install/bitmaps/selection_csgmerge.png new file mode 100644 index 0000000000000000000000000000000000000000..52f88d75da2191ba0c822a96adc46e0c856369f8 GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakhs5AX?b{m<~fgMs19nKKLw z9smFTkH{%m1e9Vd3GxeOaCmkj4a77dBL60n$c zjoJ&fAMdluf0(D8 z{O!M$VR9(zACKovK)V@KOI#yLQW8s2t&)pUffR$0fuWHu2w8?07+9HFSQ(h>8kkxc q7(}+OFGA6fo1c=IR*9m)(8>^mAR5GeEnW`Pz~JfX=d#Wzp$P!b)MaJ> literal 0 HcmV?d00001 diff --git a/install/bitmaps/selection_csgsubtract.bmp b/install/bitmaps/selection_csgsubtract.bmp deleted file mode 100644 index 902f32f55f7ed75ac5ed0af1a4a9b4199872292d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmb7-!3}^Q3`N_-v-YzAoSehE>llw6&UsuxKQx5In>4Q<`rESYx5POpD>1SVm5Ev8 xB-b3YLQ5_W!`(;e&&`w{sVEYVVF*QkE2;}iMFA^W>9EVbI{fJG^WS~dt-J+04*mcD diff --git a/install/bitmaps/selection_csgsubtract.png b/install/bitmaps/selection_csgsubtract.png new file mode 100644 index 0000000000000000000000000000000000000000..b010f87417bcb14db2f774601a4c8a9a75fd0ff8 GIT binary patch literal 331 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q#0(@S$S&Ulq!^2X+?^QKos)S9K|>wkv-9SjT|9UcGw z|4)&5HUTKkSQ6wH%;50sMjD8dY3CQ6r@Q5sCVBk9p!i>lBSEK+14Ln^O zLpZJ{bCmFwq!t7`NJvQl5)B(RdP`(kYX@0Ff`HyA~xn$e85q?!uJDU6TS*QQ#3-%)r2R7=#&*=dS<>DtWp%hHzX@PGDfXc;SKo8>6_R z7~8=M0pc9fIm9B^XK*tyoG{~YSg$cb7N}aa#5JNMC9x#cD!C{XNHG{07#it-A`}g|`6-!cl_(kvtqeg3qCxD};^jaM44$rjF6*2UngEVN BO|t+1 literal 0 HcmV?d00001 diff --git a/install/bitmaps/selection_selectcompletetall.bmp b/install/bitmaps/selection_selectcompletetall.bmp deleted file mode 100644 index 1f911e8c728ce13cfc8c7982dad0158aa507ba55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!ux(7c|%te=4a%r|CILZxBi#t3_ajRB;s82JDI diff --git a/install/bitmaps/selection_selectcompletetall.png b/install/bitmaps/selection_selectcompletetall.png new file mode 100644 index 0000000000000000000000000000000000000000..7c499175ee6b6655fe9ac32c4acc02cd9f34ed95 GIT binary patch literal 317 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakhs5AX?b{m<~f1IT7zICJLA z|NsB%CYiGVIgBMie!&b5&u*lFI7!~_E-d{SHv@qj6Hgb%5RU7~2?;3-OpFR{NgA4( z4icx1z2KQ4GFd3ZaI#=CQ_sSdlLAhnjIV^6ZmPCy5Ml7LU|Ba^>(xD=e$^7!h?11V zl2ohYqEsNoU}RuuqzgipAqECkrWRHP=DG%^Rt5% literal 0 HcmV?d00001 diff --git a/install/bitmaps/selection_selectinside.bmp b/install/bitmaps/selection_selectinside.bmp deleted file mode 100644 index 5670d3ad700284838f890bbeefab35194c60a0ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!ux(7$3~io^8*`Nj+k%pkrolM;l6@uB8J?E?U;nHg;W diff --git a/install/bitmaps/selection_selectinside.png b/install/bitmaps/selection_selectinside.png new file mode 100644 index 0000000000000000000000000000000000000000..eadd586d3eeae5f70116c5faeb51c6598870f8be GIT binary patch literal 322 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakhs5AX?b{m<~f1IT7zICJLA z|NsB%CYiGVIgBMie!&b5&u*lFI7!~_E-d{SHv@qjOHUWa5RU7~2_h_vY;0_9Y#Iko zELgIHAyGqGdqJ0hrsGuuNkfB!l3g=+SWN`k+*oInv8L=^u#K6)$cH6gXzl%QpdqRy zt`Q|Ei6yC4$wjF^iowXh&`1}AEJF+otV}Je49s;6OsxzIBHPy&p=ij>PsvQHMA2Yq ZWe7qL4Pw6*F9&L1@O1TaS?83{1OWP9Rg?e# literal 0 HcmV?d00001 diff --git a/install/bitmaps/selection_selectpartialtall.bmp b/install/bitmaps/selection_selectpartialtall.bmp deleted file mode 100644 index 77b418be076d393d10307188fedfa2c2e2fbbce8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmb7;!3}^Q3`L9eE|CS0lgE;7;N5jRI~o(Yf_^AoP5k8dr9aTJ?H8F2V1+$A!H!1M zFP&6Mp&RF~G%FMoOBm!~Nvz^uT8_Tm6R+w>NCFrob`svBZ*yvnpL1nOGf2j)miK)R DkZKh# diff --git a/install/bitmaps/selection_selectpartialtall.png b/install/bitmaps/selection_selectpartialtall.png new file mode 100644 index 0000000000000000000000000000000000000000..d51ea3b1ca5747e648a8507b3b68253e76ea334d GIT binary patch literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakhs5AX?b{m<~f1IT7zICJLA z|NsB%CYiGVIgBMie!&b5&u*lFI7!~_E-d{SHv@qjA5Ry@5RU7vXS+EM7;v~=lvd|> z$@HwWY1ZHJ?0qf*e0G5zUry~6RugU4%6==^(Jgy4PHuAZ2F{r|cNw)8XuU7ExBlmx zoF82$&i(wyZ2XVIV?D=TaiF=XC9V-ADTyViR>?)FK#IZ0z|cq+ge*f046IBotPIR` u4NR>J3?kdt7olj#%}>cpt3=UYXk`dO5Dj9#7B2^CVDNPHb6Mw<&;$S?cxW;J literal 0 HcmV?d00001 diff --git a/install/bitmaps/selection_selecttouching.bmp b/install/bitmaps/selection_selecttouching.bmp deleted file mode 100644 index 9a60815d2af9dbac34401625f72d0e8de0161108..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!ux(7OFVdQ&MBb@0K&sqd;kCd literal 0 HcmV?d00001 diff --git a/install/bitmaps/show_entities.bmp b/install/bitmaps/show_entities.bmp deleted file mode 100644 index 9188bee6767d6f7765250d80d6280a3f34c5d6b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!vM@Sov7Q1U-RLqh|@|NkI1@jws^1dI{13lIn} n2#P2vDY*beos^VRR0IV@fTB(y8YoOO?Fdm1G6tj{$b|y{jPDqp diff --git a/install/bitmaps/show_entities.png b/install/bitmaps/show_entities.png new file mode 100644 index 0000000000000000000000000000000000000000..d094bf267ad16abcdaa1db20df891e14565f32a3 GIT binary patch literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakhs5AX?b{m<|p1b|!yhK`O7 z(Y6;6Ko(<3kY6x^!?PP{AWo9Ey9?ugCf$2Lj+v*6V+hCf8kkxc7(}+OFGA6fo1c=IR*9m)(8>^mAR5Ge SEnW`Pz~JfX=d#Wzp$PzX6jhY~ literal 0 HcmV?d00001 diff --git a/install/bitmaps/splash.png b/install/bitmaps/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..0c9bf224aa5eaa4c5f1901979a8cde374209b556 GIT binary patch literal 272384 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®Qq+14rau~ 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 FV1kdmLLLAB literal 0 HcmV?d00001 diff --git a/install/bitmaps/textures_popup.bmp b/install/bitmaps/textures_popup.bmp deleted file mode 100644 index 5333382f9af910c054a511f319fe6e18159bdc3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ`yF%H5o40Nk9gXKOTau&uEmj{H*3{deE<~%Jg>KDSL-H>|L`E+N?emqy&@r7K$ z3wQ=Q8qkkXN}-C%Ia95b(2ke?Cc42H#P{0Nrl5|zjcmQ@6l1iYI*;%OxOoKg(0tGZ hUZ5!obIU)>EJ5$dI%dq57lNQZomg<)zn$Fh><4tzA4mWI diff --git a/install/bitmaps/textures_popup.png b/install/bitmaps/textures_popup.png new file mode 100644 index 0000000000000000000000000000000000000000..48ab40195af3a46bcf68c1532e6e7f177bffc693 GIT binary patch literal 366 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakfG3h)VW{m<~fqod>he;}XX z%$YO)|NnoxxEZLLfw3gWFPOpM*^M+1C&}C0h4n}N%i};!lBbJf2*-8Rv$uI03^-aJ zCPz5r{LbrTHi=_XSjcRW_&>6xYTmv-{0*th?11V zl2ohYqEsNoU}RuuqzgipAqECkrWRHP=DG%^Rt5%RJYX)F2yEyypy diff --git a/install/bitmaps/view_cameratoggle.png b/install/bitmaps/view_cameratoggle.png new file mode 100644 index 0000000000000000000000000000000000000000..94ac82669e9745b699b433160471b202cc6d5b09 GIT binary patch literal 372 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakgx2=EDU{m=0K%$YL`4F5Yi zIv5y$h~Ym7Ff_gTUI*kbmIV0)GdMiEkp|)U)%;U1Q-etOD$8LwT(#D_He(k+$8_yalGEw)kNj=au)e_f; zl9a@fRIB8oR3OD*WMF8d3qqD51_oB97FGu4x(23J1_qJs>x)n{|9=nx6d??hKtRC= zOu=Yn17l;508lp*SOO%k8U$9S%pMx7qyS{Hr-g##R9sxsg28lo7)U_HrMw(Oo4A*k XmjY>bAngL8OUlbZwul!42_OIf#dIZp diff --git a/install/bitmaps/view_cameraupdate.png b/install/bitmaps/view_cameraupdate.png new file mode 100644 index 0000000000000000000000000000000000000000..737b2cfbd92ca21d91b8430f3c925ccc23c6b502 GIT binary patch literal 355 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakgx2=EDU{m=0K%$YL`4F5Yi zIv5x_{(}Hej-e;9q8CUpmIV0)GdMiEkp|)XoM9LNduba4#fxSlK!l-9tY zE~=`^Y;f4q^CasL14g3|M;nEe7V^SZ**Tez{GRLb>TstHwt3e3;{P7 zvb1^v2OC7#SEE>4K1D zh=GBXsfCq+xvqh!m4QKI`}!gj4Y~O#nQ4_M8Vs!rK?tHj?APMuKn)C@u6{1-oD!M< DT|;E* literal 0 HcmV?d00001 diff --git a/install/bitmaps/view_change.bmp b/install/bitmaps/view_change.bmp deleted file mode 100644 index 31b609e5c32416e7540f43e2eb7c96462dac7b4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126 zcmZ?rtzv)xJ0PV2!~#If55$Z>2m&@he1s2z!2&?yKM*#+I6#{H00RRPkOu4M0P;@& bIUEcOf+7qITuKZKEFcUN;sRnpAcg?|fI$eZ diff --git a/install/bitmaps/view_change.png b/install/bitmaps/view_change.png new file mode 100644 index 0000000000000000000000000000000000000000..c13f467920b37307e889d9a61d940d1dff6c0c58 GIT binary patch literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q#0(@S$S&Ulq!^2X+?^QKos)S9wkv-KqdeG|KIx8 zeiBfSu_VYZn8D%MjWiG^$=lt9DUG`(1*E*dBeIx*f$uN~Gak=h0TMLyba4#fxSpKA z!06^C@RCW1x)n{T literal 0 HcmV?d00001 diff --git a/install/bitmaps/view_clipper.bmp b/install/bitmaps/view_clipper.bmp deleted file mode 100644 index 4ef205669234b733b1b14bcf4ea91de3398a2d2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmaiu!3}^g3+oh`Rwx(qLaKz7*r}aJ-H+Q|oXAEm^h_t4;4!*& vyNLQY{&&5tT5?}X49P9a;6;h>q+aK|>wkv-3=AC%4F7?k z{j2Cipg3bmkY6x^!?PP{AWo9Ey9=`*kIyF{$IR2kF@)oKazX?1F$RT$QcOzI8agBu zHZE{mkkPCau!T|O)&VP4kA@Jlf`$@}h7uEo&Qu;A22Fj=c}a!_(}5|9=nx6d??h-~ggl zNkI?<1e73LMkPjr(ojP|Ak9#g8VI6YT*6WfL9}~mY9N>{F9p)fF6HIrK$@kbyu8Gb M0ji$~Zaf+T0P!LqW&i*H diff --git a/install/bitmaps/view_cubicclipping.png b/install/bitmaps/view_cubicclipping.png new file mode 100644 index 0000000000000000000000000000000000000000..0f846a847c235eabedc99fbbe38809c4d8d1d917 GIT binary patch literal 350 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q!VDzkV}oRX6k~CayA#8@b22Z19ED_$AYTTC zDm4a%h86~fUqGRT7Yq!g1`G_Z5*Qe)W-u^_7tGleXakgx2=EDU{m<|p$YfyXU|{%v z=FFLnj*kBzz%Z%J8fXRsV@Z%-FoVOh8)+a;lDE4HTVmRdS|G>A)5S4_<9c$!0cMti z4h@ZtCr;eB;o&-=(J|!YjT;uOEsc&Yo;PpsoHEdEHkfkhw37pmmdKI;Vst0OONu AhyVZp literal 0 HcmV?d00001 diff --git a/install/bitmaps/view_entity.bmp b/install/bitmaps/view_entity.bmp deleted file mode 100644 index 851ec2c3dbdeb7b938433d4bf21e5f671f257d61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?rea8R+Wk5;;hy{R{ABY(lSb!vM@Sov7LqkIY!~g&PaVx?hBPgT<0*ZoQ3P>v( f1A!3|&0xU5fXrtGnuN>;at-kDq2@u%7lZ)-DDfB( diff --git a/install/bitmaps/view_entity.png b/install/bitmaps/view_entity.png new file mode 100644 index 0000000000000000000000000000000000000000..c6a5184108d5cafe7b7d2ceefbb5a8802683393d GIT binary patch literal 303 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~q#0(@S$S&Ulq!^2X+?^QKos)S9K|>wkv-9UUG2|NjRH zeA$uw8%Qyh1o;IsI6S+N2I3@nySp%@ao3~(Ihvj>jv*Y^lM@&iuU)vnz_u-P^)iRf z|N9Li-~T@xvHzit0OJ+uLkBoi81{XYI+35SM-!+?wZt`|BqgyV)hf9t6-Y4{85kPr zf{v@qo+@uZYG+3l1DubB)>QJ>z_*Um{iw zIqn;RlKQM}$U02zxEOfT~sVR`MmotOK&}AR)gZI`-}_H{Gv~L zwO#-^M76{2OC7#SEE>4K1Dh=GBXsfCq+xvqh!m4QKI`}!gj4Y~O# fnQ4_M8Vs!rK?tHj?APMuKn)C@u6{1-oD!M<5?*@^ literal 0 HcmV?d00001 diff --git a/install/bitmaps/window2.bmp b/install/bitmaps/window2.bmp deleted file mode 100644 index b98b86910e112a9d996c028762d35f25f618c08a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 614 zcma)(K@!3s3`Mbc0GS*>1~)Fj+`zrp@CdGZbNmTV0jD!%CMhrf1ED-$@5Si&Am0(! z8Ygqs`hHr?3Nh!rZ#k$~9)_%9j^zUd6hWAknleXWjmC~frb(Z9_K*8L0!FvLvh-xj zd61Z;^8@TI(wTTAjG?HU8mAwV?pOU}Sy|Ro;?x-b1Avlsj_UbI3|&7!X1T+y>vv{Y aBAX=yy`8AI^B>ReQ+!+Y+e{~Z;^4nqP)U*i diff --git a/install/bitmaps/window2.png b/install/bitmaps/window2.png new file mode 100644 index 0000000000000000000000000000000000000000..8bd1092f1a6923aabd5eb8d5c568ba895985ea24 GIT binary patch literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^@*vE_3?!2d{96U27>k44ofy`glX(f`C?tCX`7$t6 zsWC7#v@kII0tz*}U|=XUU|@Kaz`$TNgMmT3V9u^U8=wSFfKP}k149Rp`TzfaAa#F7 z+$$i5G0EHAg(;1@CI!f8^K@|xk+__k;K0npk#m(3X&Ja$d;O@m4#0ZHv*h{Xo3dCH=tCgf8dswLHABhXkZpmpI6^{5BC`a$->V zU-Gg2nn^s+MXDvP5hW>!C8<`)MX5lF!N|bSNEd`GLktY8Of9So%ykV+tqcqz+t(MN kXvob^$xN$6(O_t02tp7IV!swI2WnvOboFyt=akR{0G5n-YXATM literal 0 HcmV?d00001 diff --git a/install/bitmaps/window3.bmp b/install/bitmaps/window3.bmp deleted file mode 100644 index 2ea6feb8a653fb04b77b0d405237959a670881f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 614 zcma)2K@P(p3}Y1^sKkBJPCHiW3qV}=jXhy!e$H%%iI%n6QW49IV-nKiIXT4(`HpjK z=iddT1+JFnF zR{{RQOeD;YMt5HwK#C`B} dZhLn>mJjCl>~8k~p;y39;nsaD10DFfyaA`?Y<2(u diff --git a/install/bitmaps/window3.png b/install/bitmaps/window3.png new file mode 100644 index 0000000000000000000000000000000000000000..a389dc1562caaa05ba2f0cb557e93219cfcbb16e GIT binary patch literal 364 zcmeAS@N?(olHy`uVBq!ia0vp^@*vE@3?$z+aDyZmi-X*q7}lMWc?skwBzpw;GB8xB zF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpag$_Plzi6LkE!g|Ns9EAYfql z^ZEr)83SXIx4R2d8h1?!kW=94;uxZFzPG=dw?To&rI7gw?*p@fX71nTRf6nu4O^O* zoIL&X7I)&Br|r2bHBWmKaxM74wB|kABZf(=XPsWJh;K~0urTyPwq^AU=HH1XQx%tn zSe88fF|D}I?(yv)uFXB^x;4?ewoN!}aN@%9^M==BH$)RibDxv@!%Chz7A= Si-*!nU$&P~KIL^y{a7^qZBTu(Z>iZh;@eg*<2WuVQ&^$OQH5_t>k6#MN zi24|FLG91?cvD%^cyr|03jc#VguvX+?=XnV|8eLSuzbiIP|F+SO|bpEusnT={m9I> atWbUCi@N!WugBNtyp3e-wG24$y1W4-FJ@f; diff --git a/install/bitmaps/window4.png b/install/bitmaps/window4.png new file mode 100644 index 0000000000000000000000000000000000000000..4001e0eebf19e11cc60d8e564f6aaa7c100f4b2b GIT binary patch literal 375 zcmeAS@N?(olHy`uVBq!ia0vp^@*vE@3?$z+aDyZmi-X*q7}lMWc?skwBzpw;GB8xB zF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpag$_Plzi6LkEy~=FFM@|NjG7 zmlmfj1@ak_yxm=x1$lfv0Xfy4E{-7@=ci7*E7+jG(_F}Wg|~(G67QjZ#*rWRtFBsq zW|22J>Bw<^mdv)C=A;Nll{sD?8br7sFf=I(Y?L$6{K}?y;wei`^efN%&0G@EUzuM- z$jw^y@oizF`cjcoqN`tD;dv*wM%iS+`g;t#E0(?2|0ciV|D}_RpI7W)Rygmg>$_rq zBhVSDC9V-ADTyViR>?)FK#IZ0z|cq+ge*f046IBotPIR`4NR>J3?kdt7olj#%}>cp gt3=UYXk}<m_pWidget, "cap_bevel.bmp" ); + pixmap = new_image_icon("cap_bevel.png"); gtk_widget_show( pixmap ); gtk_table_attach( GTK_TABLE( table ), pixmap, 0, 1, 0, 1, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); - pixmap = new_pixmap( g_pParentWnd->m_pWidget, "cap_endcap.bmp" ); + pixmap = new_image_icon("cap_endcap.png"); gtk_widget_show( pixmap ); gtk_table_attach( GTK_TABLE( table ), pixmap, 0, 1, 1, 2, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); - pixmap = new_pixmap( g_pParentWnd->m_pWidget, "cap_ibevel.bmp" ); + pixmap = new_image_icon("cap_ibevel.png"); gtk_widget_show( pixmap ); gtk_table_attach( GTK_TABLE( table ), pixmap, 0, 1, 2, 3, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); - pixmap = new_pixmap( g_pParentWnd->m_pWidget, "cap_iendcap.bmp" ); + pixmap = new_image_icon("cap_iendcap.png"); gtk_widget_show( pixmap ); gtk_table_attach( GTK_TABLE( table ), pixmap, 0, 1, 3, 4, (GtkAttachOptions) ( GTK_FILL ), diff --git a/radiant/gtkmisc.cpp b/radiant/gtkmisc.cpp index b80e7e3f..352acac9 100644 --- a/radiant/gtkmisc.cpp +++ b/radiant/gtkmisc.cpp @@ -727,18 +727,6 @@ GtkWidget* new_pixmap( GtkWidget* widget, const char* filename ){ } GtkWidget* new_image_icon(const char* filename) { - GError* gErr; - GdkPixbuf* iconPB; - - /* - iconPB = gdk_pixbuf_new_from_file(filename, &gErr); - - if(iconPB) - return gtk_image_new_from_pixbuf(iconPB); - else - return NULL; - */ - CString str = g_strBitmapsPath; str += filename; return gtk_image_new_from_file(str); diff --git a/radiant/preferences.cpp b/radiant/preferences.cpp index a8bc384d..c3021346 100644 --- a/radiant/preferences.cpp +++ b/radiant/preferences.cpp @@ -2083,28 +2083,28 @@ void PrefsDlg::BuildDialog(){ gtk_table_set_col_spacings( GTK_TABLE( table ), 5 ); // view type 1 - pixmap = new_pixmap( g_pParentWnd->m_pWidget, "window1.bmp" ); + pixmap = new_image_icon("window1.png"); gtk_widget_show( pixmap ); gtk_table_attach( GTK_TABLE( table ), pixmap, 0, 1, 0, 1, (GtkAttachOptions) ( 0 ), (GtkAttachOptions) ( 0 ), 0, 0 ); // view type 2 - pixmap = new_pixmap( g_pParentWnd->m_pWidget, "window2.bmp" ); + pixmap = new_image_icon("window2.png"); gtk_widget_show( pixmap ); gtk_table_attach( GTK_TABLE( table ), pixmap, 1, 2, 0, 1, (GtkAttachOptions) ( 0 ), (GtkAttachOptions) ( 0 ), 0, 0 ); // view type 3 - pixmap = new_pixmap( g_pParentWnd->m_pWidget, "window3.bmp" ); + pixmap = new_image_icon("window3.png"); gtk_widget_show( pixmap ); gtk_table_attach( GTK_TABLE( table ), pixmap, 2, 3, 0, 1, (GtkAttachOptions) ( 0 ), (GtkAttachOptions) ( 0 ), 0, 0 ); // view type 4 - pixmap = new_pixmap( g_pParentWnd->m_pWidget, "window4.bmp" ); + pixmap = new_image_icon("window4.png"); gtk_widget_show( pixmap ); gtk_table_attach( GTK_TABLE( table ), pixmap, 3, 4, 0, 1, (GtkAttachOptions) ( 0 ), From 3bb6ade3bdd55c8e069efecfcfc58ee1acae1f70 Mon Sep 17 00:00:00 2001 From: freemancw Date: Wed, 19 Dec 2012 01:41:17 -0500 Subject: [PATCH 9/9] updated splash image --- install/bitmaps/splash.png | Bin 272384 -> 285396 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/install/bitmaps/splash.png b/install/bitmaps/splash.png index 0c9bf224aa5eaa4c5f1901979a8cde374209b556..9ccfb6b650c949b464d27755a36dee5ae5d3228c 100644 GIT binary patch delta 284880 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

    FwB;Ye@4wA56mb`vk?~Cd zw=g_>1?8819Z_?AALpr$V09k)Oc;^wkydW;TGV2D-s?3}BxSJTVd$vI5Icj|+UO3< z?FVlKm0VXcqM>##D-#ve{twfR6E7)y56D0Ej=Sn)PI9)PO=-;nizkiW*MXnL-}I^* z_C&8f+i>`RuDz2Ye8-~e=W?pYFz-rt{&ml!N37L~S|ZeqAL6_FNiY1TmNJCokvE)a z358!v(mUC4V~V55lT;U`f{AhozeM47KXcKk*eWgq{>?0T^S+%m8E;I9H+HL#5>Hr4 z#8x&^7mzBc=>mIT{CijGFV3BmwPeBBq`W$-|Tz1~>W`|)%^Ea^0Q z`;yS8!|}3`fh6oMsLzSxN~TC!>$@BFxfVBJzo&HR4$%~$66I6SNAOGm_<)u9xsn6X z=E&a4oPQmXC1|_?CVdhKgBw?*ln7HG@BkTk*wcVy5$plyOC49Scm}rhdija#KmSGr zDIHw@gX+NM@U5c9{FkSsZqFt`L9}v6RCOn zv)n3F+|*PGegth5X3Y?Gi`dFoFsHmNjIDm%Mhm9lDsN7spmG{lDMVX5ol)tvkY%~p z7&_Lxb{EWP=*By=k`6NlCU{0dDdG#NXK2(9{-mZw1gNP?@=_(BBD5%E;TEhcgzHXM zH`b-P#a#?THEYL;Tx>=J6=i&#;7pD<&J#lK+_#f7K_jp*oryz10uhqahB zhO3e$+_9m=A$FC@T!$bLe@!@Cpv?34rsp)LVoyiiZ3ks3J+xa}&+l*RhTIfq2 zX3uPzcqshr(cZ*_u#US&2&yj1JJOi1(*1eXi|rv@D<6Y$G@6M$_OlN6Trv2!Mx-_G z?dphlaFFdY%YQtUdWIQ2@;Ji#KtQEK})_;o~EbbJqfP3OJQuPGiRB zt98c9RbHe#4OI*@xw{_#2}AZw3ESLa(O^dfUY#h2yzj!drI79C_Pi&s2Sz(1Tzw;ci7s;1htQ>fUhycZgTm2@}F+%p%I+M@gXiYrd4sLf_p7}kXvg8 zvF8X_%~W@69u@v?R3aT@PxLKyb^?|RtN{uFfGp~E9DmMnk-~vERDhgE1 zmMrkM>J|C$HOa7Y14GC17BEGNVG;HHjEKR;huD(%I}G9xCxhSOlucT<+bDkYG*b`l z61^H$shXhxr9~JE#CeP_oOW>{QlMYMf=}}Xzjhy<+m|7RXZ_dt&fsX)E;FP@d_0oD z0N7L8;N}@`!mSazKbES#%2=D1kV{js{sh5Z@ho46FCgX>sx!X|fVqEEqT7FM196Ko zRvaKM2rJ;wMDHz()mo#@K!j!?7p~(nN5H+tFFG!RSJSV~I&u(W5if>M=%%9C1SSHInNfPEn1J_oEW6zvgdHT5raE$;S7zJzoQ*-$lWG;mwRr1{`B9Jbq`I_dn zRd^~`0x54)cPrCxWo)6i$#bT0vqrm`n2=-vyZ0bXcuEr4;CfW>*A!oV-7-gdNxbyO z@}~zeZ5X9(Im7#?0-x(}xd&j&(v-c9ycdC1Kh~BUZ^p&w+kA^98r`+95`;O<)h8Rua;$bXE&<)n9^{82eEVsk^QU&&3an7c04DR>pvrX*UmTfoN zBcBg{m;O59UT<{MT@E2!Gpf7wNM}J+ba6;7$rzLAV$iLjvG8_34v7V9iNS9N<{UJD1^-j=&uz-ybYi zAtbGjWr%fSKdXpSOvEDz_f<87LPebu9d-3tbO9EV2%M(|^5jl@jBtOs2yz;$fK#!= z(3a;?!9A~5J{Pz318@D=ci*Oozc$#&VXh@--m5F(KM$gHmb{I14pza9zpPAY@adk? zg0LegPymASl!_IdW{ze#b35p}o-WvvGd`W%^7S+Yhropktk1OVsyemQGO4QaQN>E8 z;F*S*Ikr7ou2MctSlL+qGQemu{15}|*YJO#XOf)A+p=8A5(NR60U5pdl9jw>=lGSY zvnF}Oj24SuEJyzcW$t!YCB)?~V`6>$XJzVmi$Wi@#4JN10(uO!A>C^VmmKNnH|c|M@620W^e`-4I6>BJ=G z6NdQ3&jBXO^GXb)slY{hvJ%$63rBOKR6rZNWz*dxKtt{9i7W9F1OsXFbJ1WYk4kO} zBrdayW6pP|QGjZkxhGdzr?^TWjKB$_9i%o5>$Gi8EL48f^MG>@V$n_Mk}>h+E`%=* z-z6~0&?Kmb(_gcBz%j#!k>99P`q?+|SYXg-vSq8agh`{oQfL!bDR2?ZlTWIzy~t z*}CE6l96B9$x;^6?K+byH|=FC+EL-k+w@LckmmW1h)4GU zBo?)H^_l2*q2Qer55U)@$*I|y;q5!qo-@&cf*#Unr(MfY`NompGbLfgpKssYlLY`~ zl{VWpR|Pk^6p!9u)bSQg|w3?qL`|7^1JJ83*pyLmMhG zwlH#F+hV?KI$sOyHW)AwH*=2vAgG%OQ2=pSXtm$Smkm<0XEygKqnW-5F~R z>tY6K3>W0Zl^)s$dns7H_wrh2?9cAjk4;AgiNY!x)s@Q;gaul%zQzWpO@WK0k;rQ^ zqk&!($#smQQpX^5z2j0dBWh9M@;aczXUX{IdQ>Z)?swZs4gJK0mQHVO*s}lF(DE-Y z$|M=8_9s49N%wta;>N&05%IRBt|Fj5PM~kDz-zJ@-M7XNV?W6+@WPH#RQr3_`j8+e ziDm+&<(WVIr;>bVW33n7kvIrsy@jUsxNPGSJjYB-AqyXybBch{$gFng$%9aj{7z6Au8Xkpt zW8xAZBP9Z%Mw4wViXhq;eN;|&|KNTo0|@+mrn>x8@_cP*BHab@B@qg2)C|jgI$l?E2cv*0s?Zrz8XK5~Sl!SHl^8O2sT5 zu-*l0Ppl+gNcsmQ=@cre1}%JMFnl8V@RuP@-3|9Wb5Ud>0uzr9QX7m6zg)ru8jXBi z%J@=tOB%81@e@6I%Q=Xh$yhds;YM>up@__eyegKn)`{l(KPTmC%KB3Ns=P>*4d(|j zd0|#VYI8l5#Xwl3HT|~4T^0NCuGxm$54;MaH~MfZg;L6S=EuX^VHRT3u-1$mimRtzU!w(?b^9z z+9w`l8FSC;umEC{LC#?(J=;{7^Bbv)1&OTmE&4Nk#^NDW*+5(_6$YhXOFWs)r0hVkIc z%ov=r=)@dxbJ@o2JB5aYA8YMOG>cgU#FF-FVoC}YE|i)2A`M*BN6=u$0U^`@mk^{7 z1WEGFyyanaYbQe=+1pm2bi`T?5?X~x4sYDh4T)U%cmJ21ku+P$ts7GG7Q7Vzz(9pm zzvrkp9e3KpwyM3am@G!2SV2xfguGt=fQF9CJ0k1J4BinVm+)S&2P{*dND+He%USw~ zArr01GtHaq3hoIpHrytHoU9N*XsY+odK|)Oqc9H^2w`vDIeJMwGS#%lX=&N7Fz?{? zfm==+P$n@UQk=IFSWYS*@~)?i(G?LZMm6tZH2W6*Ji2o|Ut|e~zit-E z)XdnlY9m}BnfivY*(k&?h9%3 zh){@pl565w{_o_L&p64dZQYW+HtkzXM@UIhwH6g~6jXzkFw-LR?Sg&<<+k1yfH4aF z7IOEa?b}peRm|TfM9a%dw-}||cJak^m5<(zH$P1f|JbE)=Y9K+?fD~s~^U3=Zsfxu!hzO z=IcJz1OVLi3^{+h<*vFdyv_dDKY!de0(yFUf1aM$EJDTvah0VSw$4*cVs{wU4<-9U zY(%==-X(-sXn?e-L^iVr60-yz|7YiZtlYHa04eo)J$3gCD8j>Pem77rO~3#u(f9Xw zZSq_y>#aOKCn1BE(dem^BsUg>5>69vm;zX4iOb0?oWS$=wh+t-@J}M%wJQ}Q%%Mya?u@oh zIJT<IcrAA2I~zNl|!5W~!_)!DBW_;hlP{RNaGYf%dGgG-Czrso&d-yOE@e$(Qt zJ|Wfifoo?!X%BSkR}Qzfn*&kN?yt^{GC8~DXDz-sa0lLI&t+)ZUIg|#ucy&GQDp8i zFI4cE8qCuPw5#~LbzLj~xoKBBMmZ)?104&;jURQIb9^aO+2AnX4~WAvsyL1aLrnWSMx`G)KaY%+e*bir09 zcUU-tY&FXJZzp&G2LWJHK>-aA{Yf8Qry(`#GWuYMrGoRTcmg%sZ!^`0uZIE4{JZW3 zo)20ek+^wY{}!oE!Fqya!-CJU%x+vc3|WAH>2Hmoq#6UBmwBuSTIq%vQbze*fZ~yCe*WwmDLIA(YS6Z@7O>0vt+y zIU7G6;DySO-EDR#Oe3(`(3JZscApcVvL9%jY0?I4uRpN#n41~@d=_-q4CN@Qp`iEy zNtBzfkIM-Rk^ayj{Pi<3Wwr*xKNC+mzxkD-v9{+cfd9>lP{qW?^&pzb7Djq3PtnT9 z2+d3uK1%A1=fY?I&P&&01Yczw?+|IRU<0IETI#xfuczd-0%0m7rNq-TPyMIl70~5w z_5znz4Ep5&Df^`yC=od_Qq}E~@?)eFG5?Q0un(7f{vJulDyLIb5JF)fI>LG-BvnC8 zr85kbh^U4T#cbP}m_@m(tGn^Hw>rH|n6WPF;{v1MWJ~6_`+m(1prNgN&V6vb@C&m~sK|c{pNx%%7`S9W~d*TnP>%%gJ#{l#3`S&$;5?5JQh0 z7HA<8+LsY^XpYh0WW-E9s@KwSGqG*+q=R-hEjP}&@)(27SJ>u#caoYvK6y(z&XAY9 zUF{vIUy+{|X{<)N_sn6*<7yQ1?$K+vf%`te4W;=qA5R`j*!`~l)R`CQ-jVAuUJM2H z{>yyW9DOa5;hwdlyv-L@&iRM!VeQ`kJxG#T^L>Q>bR@LyoBL=DV@hh7IWlAD{&AAh z(4Bhk3jI5)PP@g^nKhcddi8R`*+)-hl-4loO$44}M8fjf^TyZ6wI*T(|L6w~r7X{n z-lER}Kf-YR*GEVqWyUw;4JT=(kKW!#@nst#-w-w~PS zP44;e+Ut~HQXYPMJl1{-4wZAVV9-&`tRm4>mc-}?1SU`C8aJz^g z8Yws_en9Xr393#3e{EiGH&ABGOi4)*_k6FfYZ@2qbfPj#%E&E*T7>R-iKQg_aWUWW z2d5>k6G(+_AhQ(n6<2KLuLQS$ST1qAyISLP@%%TT>pKb&^)CCs!=kJ7 z*3u$nUXu3#3LpOl=jS|_rx<#RM2F<&5z$`v($Pbv5jvbxs(@3jjEMO=>4%vdJAtXot+@OSl|iIoen*96ERd# zGh|BkF0p;%~1jRzQ)2CZ6NP4vI7<%`n+&N^CuEIBMVCOj>daEV0}Sa z=dsA^aiZhG-<8Og=9N(wO)l1(zt!C)(L-jp1Op`&F76c)7Y!{_HIBcflcp;^rhkf@ z1`drDI7|Z%jIGjZ@{p07~TOTPn;(C&(jr?j-aXBDGxA_mF z$%qztIqF(R5^M25doi_s3ZKUJ*1-THXwL@>DaUe%!Q$VWZT-%DckcQA-In0z6s17^ zZ5)1S^o3VnNe3mob1lo|tHl%g7r7Z010y(lmQ^v5`Faiopi#(U)H{d&f-AYkkfEIa zdhbMKnOnqzdg1AY8}f-hFxA6Pa=p2jqA@9C9c;$?y&6LhUHN^yv=G9QU2jjttV%E@ zjLzA;q~dOBdb@uWsnQvQZ_Z8A8+e%J4i)|t*EdJ0OuB&AYVHM@co626rMJFf~zDJhMhbR*p$ z(y??cAl}a1SC=^p!U*NK|c0e;Y)O}EvAyP z@k_la7m94zEyGj7kED>KcU0KlGaHG#Ab@rDTU+0XPYe#q%+wn4J+smETg7HXNyQ-C zL`InY))y5aqF0--ZpQPTSr_&EBnj$sufOSXzsV?SG|dkb`+#BLZ69H`@}5*m$Ve_) z-9+BwTil0%?_606j}eUnAy+GpsT%^DA@k`<#3UM4OV(7gqI=r~xeg0jLv)(TkuCs< z(d@v>4_+)B4P1P2l?Q#3rmc7wG=>F|+R#UG$Oy?l~RmL0~Hr9ZXfnlkji$jJ7 zXL4sJ#^ihdhJ#Isv)<1)*oRDP@!2MT`er&L#oe#8K;0%zP4eO3Rl(lHca*G7-uIVD z_b8l~N?Ej*$4W0wsrHjKWuq2s`dmKPePBcT^A}R?L+)ew+ATRef?@QUlHW*fEa>O} zU*}Qf#BxtYu=dsR@^m5DHkba?^0Lw8AxpMD0CJ>6kZjk?n%rJG_ zu<~tmWz)8mF@ak0*8RBIlFJvw*!M#R&2<*7Adb_sNiDwD z@zuKIWRX)%RJ(>OVG2N3Rq6YCuf$nzrU8gbN9z`?vQ`#7H0QylzM)+J9E0+K z*i%nUhM_DvI|1Ba9N^(v7kgj&kb%6H&e0~M3VUK~XWYBIW5nd^GAvcs69GUh{{^?EP?-u|Wn z|4#`}QrDlG9YE7!o+k5oZb-$rvpiG7_A^`r+ncW*$UF( z3nVIk^=n&32_3WEiC#Z}yY5?jxia>^PwkE_Hp!^OxH>ZSc~(B8|I_#BR=M0K#R+}^ zRz^8=m2sQ%`{F%{xZlgf2I6kkpPhD32jDcDP*seZE$4LW+~@4W%wQkxtfZv~4mRZl zj%ndOI#MEtI`V4M@8h4vWEw5|Xkd#jt=Ppqv~sB@ z#4l$1Y8IVEIOq2x)O)Q6+k1>8!QF^WjVu=w@zW?KPJ+ma;MtEb6z zSL#0lI$#EWf`iikmW(~Iw3_y~6pM6PXC0DDaTQ1Ma(d%66YqNV_B*n1|8kr(EPSQ& zlAc0jtd+rg5@6wB7ni}*5Uxv}9x*X4N$$mo#`)df;b8a-2e(4RKD+WYHZraLw9k(@79dcZ>($@ngDYoRnZ9^=66R%Vb@X5Bu#<%qKg-qdnIqS$Z!@NwI++2{(L$7lz*|Gc+zm8%7Ck%;O<1#2r`qqix*p zT7`l)%|~mpWG~zVCj(2j(4LhV+oQKBX)nzla+?9H% z-wfL_Pg?kWlbYikOGI&dDRdm!OYvl0ajTG?Gaf_NwY0;YEr|b{uG%hVgQl3YHh8Bb}zyRC`}|syrhiXi|RXmy4NAF8Z-xFdBT# z=N1tHIKJRCdQ<5S$=}6WMf&Wa*G+Fayu(Va*P8A`s+Y>@fDS^3ij98tGlF2Cvt{`;OsH4+ZN9H4yLaFh*kFm_ zG$ADF9VV6=jZsy^X1|jtA0T8>4Kry9Knk^iH-ht8Hzt{Mk-n+J0@)3FF*iB~ZVZ1N zl&4>bt5JC>&O|7De^;HP62)vBcu^#1kyyW?H8cA=+DV~`~^Dq@-oll%$oC2*L^4MSr zglS52u{*7})P@yBo=_cq%;Tqj{a(?@ZTl)>b0a<1hmP-3t}f6>VloN+SnK-SO(}lj zkd$T#Ok;US7l7X0EWpK&5?>B_SyNe=ewVXeH=~%5`Q;a_W&4~EiX_)mZG}popYXp! zZ3A-Ck24c(*MJNr^CAJR^)rqPpn{HXySQlag|nk$@vp9voghoHeSAITcdUOw zLveq&j5*mvw*{~ihrk#Ecb?<#^Fta8pJNrLRJ!M!ZHf8ed0FYi5G<+Rnj_S^k)<_l#xo@6k4053Jo5x>IYT7{CvZeJiew6Hg8lip?>CkA7=3 zxu+QpdQz7wzRNLjj|ikzeG!F1IQ*SWoiNvG8(bExlIdjYrj%8~CP<<)-0(CeH_`H* zx=iVTAyT`r3XSyMu+AjLzY$c?;$oSm<)i$okk5+Q2rJrH{m;oq-*cB2CfkOo;~BWb zUq-8LL)7)o?QsM za{*4uaB*T+mDMA8+3`vMm+m?8ZB>yTQ7fp}9fp8C_-MKMFj73EQ9gx(e|o2e$TJ#A;z% z(SjFfv&7los|s;yl%qu#ztSWX=<=TXdZfc5{dS0*f|*}66b*09nMcB<2+SMxH|JJg zd6aH~o#VKrKU#g`wUn~1Dd}qJexN4*tnFU{Sa46!nRnW}_8WseIeH?>7choK&pjn>HJc2eRpYSi*9|EowqQMpvo8Hj z8%Am|$O&5oN`;+Rzp14=0qLGG|F@_i;#o>9IYD`*pDo1OrOw}qgd#C_$V5Z}*d<*c zFG>b}Du3qE>YdIq2}*GR6UtnD-85%55Udn+<0JCW7&i=qsg89 zicu7QSS$8I;gwr%au!WR>GuntwCz(aksr*Uj4z!#b1oOMHL zI;!_hqs?;v8|chb+k9u4bftzZv z`g)Llvlv}Yul%KN@Xfp6B!!j-Q!ynK&k0Nnf*(;aAO#)x=iof&C&qpmE3YE<{_=f& z3WK`o`tc}C!7e9Em{#uhbxAuB0dfk|XT(*pvCf+Fo7@S2^$x>=PTYt_-Mr`mSI42( zEO0-I!^Sm8rB`o@M}CSgmZd4OnJmY0Dr(X(r>n!IiWmZCvSGa)>FZy5TjZk&`e-U( z&+o)BmaiAu*eoxvi~a$_!CJwV#@&fni9+?XX((x$W=%z*nw9 zfmu|=yYAEm6y+0Rw!-lXv#hLGAY8}$NWf^xvtR1Z5h68C95uW8+Y?R$&Ib5e$jbm$y2aW%a=kmumSPf$~Z+u zm#yV8X<}#1?^FFw_fvMsQZ@22k1ZLalr@^CGdb&*wQIc@#EZzP7-j<=dEH)r_%s;% zSL@DP-Q?4@_p^6;Taj2tOWy@W56Xd2SONYp&uBqv=Y900C@0Pk9ia$G>)p&G0$t7UB=aEuPJqELyRQ)n#`cYbPG8ZotEV0Vwrbp9_F^(}A`sPo1`jUWpEUQT_ zBKca_Qy~p%(wCY-7bJrBN?GI&iL@159Gv0?`sLpjG=yaoNMID1c>#Fvw`vw{J!n-( zHeKE8h=e=xW!PHQ$9++0mik72flZL)J)ddo!(sgXw_U%3VvzvKdx@**JG%u^oyHwX zb~zK=6eZP_6Hekm>)g8`78)Q!x5(+76$=m!4?K)#ad%Q_Pkci~T;?%b5nsD4rKx*g zcch(yi6@lYK8vAdZa!@yzca7>uOO>Wjf9>Zz(vb7OUIC6BIUK{6OZ^vjmE!B)1_+8 zshAZ@ZJ(ojE=h`+q_CUxwyh4&HEcXmBbDQVBSmM+7M`qx5v|N$69i;oq+1o1raco~ zc4FkG`1)#9i$?>fzg-dvKS~Pg*SYyvmKFO^VT<_rLGcu+QXXBN(2{JVU!9f%FOC8D~wKE2kGGy`?dcyfoiP#&OwTN zZ=pJIvVr49eUWKQX#$XU&U4XOdqKbsvNj{>8YQK3bmV{QHaSq@}MJgr_Lv78^T_s?c>XvTQ=`$Qk1Ox zFd*;2%2n{viCj~IPHs7Sh-d0UyTS3To9;#|D7mSBc=Xv(@*RM5;J+TZ#pj*P{T>8u z`1wz8#4SC#&1yV5gWxC3!bVev4-b|5a!qci?(yXi$mv@3LpFiU#X-N5rXFMpZG7|a zFsu@J(;^U~q+GvGCS~iZo_ZtkPS#>;;JAI!?ZV4qs?VmVE%h()@%WLtq#RX8hnZp= z!Rnan4??cK+I@gk>Z)KXoN>%s&&FKddQG28P5y`L?crOUi;MAB)suWMl!Kgj5f45UpzcH#@f(z?$?J*pK%@3Bl^EMYR!K;A}bk3 zsc<4`ZOJ=-ak=W$_K6j7sc`d5spLBA4Csr$ouRfhvW@^8GS+wGHdBV$CdN}{NCRb* zsH4SF8fKHToVCqNt_!Id$1VF0qz9?GJO3=5BMPl~w@|C``zLp(3DlE+`EUjJG_dA_ zK_n%T?g5WStGD+Hj~8&d;NwSn?6+MzyrI460E9mDK+SxU23o^J4{XLD4v(H5f`N^6 z4)-ppHQ?a@RRfcfmp5?2YVHR?-(2>Wl#~>UhklEPIN+T3X;$-5H>bhEJor3!w>$lJ z-BEwo09>HdJ8GoPDcCtsPt((V-XDVE^CDo$Jh4`)UB~bQKVLIkl@U{ae*4VSLcF~M z716mS=qH-LCe=Axsn_Q0bZK|>S#>OJsHm^_Gf+U{7B`{1h;|G~=!gk< zJP1K>q2lrN=p)${NWlq!7J52q?IWL8|@VhJLD~^VQ^2x)~eN`HC@~u1Um( zmsU0Svn}yleUK)ozICF4g=}&goO4dti?(1TOu&=)ND<}&_iELFJ86o^sX2}ZM=Z`lRA2laxpMWyd=2g6Pt=|doD z79mGBw1W?|34ke+SoJe(Hnzc2*KALbVJd0hHua*AuY}ra=tLqtNj~P23U2A$f2UoeZfFXOJjB2lw&{^jbRQ>-Bv|wYgO1IY09U^EBx%%5NID4$pt`i?6CKNHFymmm)!T_G0MA zYe#QxL4TL&D4MP*+W?L=vs z%9JQRI5nr)54Cf1@0iEdEa$CbOGN!1+g>?`oxvP)i2n1PzhAt()Hv>@(ie{HKa&G! z!{MWVkz_|4Jy0VH4i^soVqqIT*pS0ud8IU@KOh3+5;&M%j2KZ4H6mtn!u21Re6{!F z$QW($I8#P!etNXV7MB{HAze;06$@JE-OU1hbs4;yLO#SkC!cq%w<02($kD&a!>w;C zpP&pP<9)$4U{n;L4*#`%rK|G~zr}CUJh83#{MC4l6p(fSoyAPnbLoiq#+dX;TVybg z{{>r{kDo)dfe~5$!M*K3reVtghoX8mHrWh~7wGRxmE*p#)_Ogh&njv^l%MzKz6sXR zKFl69L|1!wo%%;pP+7|_L#a3Am!36>{b2Pm$WdgJwmP`pVaf%%0l4}f zk-2%j`Ju?YCFr^Ju4vbj@A(8T08v}J0PcBjfZe0Db79T3w$L^3OO)Rb7uc|lv3 zrNXs8m$zO@l)pWCA|GAyXNM%l_|}}eGwdlon3H35v|*74qEi^^h4i>vj4@jZqyxqL z!<=afJ{94eSvgHvB&a1$6T{>gH={+3ZS2%#Bq1}*m%Ip=J6~pOdG6s>^#*;P=Qk(NlcIkU6nPklzQ9f@lMEZ*xFLU05H7iizE|LLV+*I;4%qU-?1Q*}Ld7 zg^prM!Ai#-df`}A6?i`O8RhL;4Rb~#}{w6Qk|!?%_KZ=HBi`z*K6E9+gwYDiwCa3S1Br>-m=YY!J%)I$Baf4 zopm-JjV|iLd!UdO+`O3yiNT_jM}TnM>}wV;piyb}Yod*y3H@fRDScT&xY5Bs@AWI5 z5Z&sJRkc@6`W^KtsC1tAX_<_ZOyR6bW!rn-ygubT2(jkymC?EKy81Si46qCR)PDG5 zI~J*iLjbb4`p#=^e0l8uiHt5g?X)$%X0mf~Y|_JBUg)_*T@Vv`Wpo6-ATLnqMaTHE zgWPG^B{^QO-i28^f9a<Y@ z%BZmg%Bw2S@XV}eEWoJ~@2IDRL)?*E2=22TF>}3`oE-A_k}+89H~oSfW|ku+*9W2&lb1aOuo19fSuX3Nvz-L)<v}M(xaA8E9u}Up%tTGVOy2x_ZPNG$@)@gk{H2^il;AVky`3ag-=Brt#f}ap zLNnJ+ZX$VDsGm2!zjNC;%P;Ah*5w2pmDU&XmC+APXN{HNp?q{JzOOEH6ZFNOw+UR9 zC$yhHdWxk2zmKjCnB=kdNVjf#9P0@FF#3|=Aa6^N=qd4t6ZJW#QT_d3IBd9cyxSzR zEMLfN0?{zq&}B+nf%WVD#rMsnk~`1YTPVw=ueb>H{4((>-|B1EkY0GSIYetP>8MW+q{Q;`p_%P<&zr(909*G=-k|?0scAb zA6?56tRVC(&IjA8gAWf({7-Xa9y+kx|FwmlEU_0QVznT;vMnBNCfu{1aoDH!GmBjV znND`~@xv_JmZ7Nfo*&pJew%t#_@t?^0d>cL6`MWBcwOswtMFBr^yAeG_16|iML9-s zaZeu<))hT8Rx!sBF2sVhz^b%#=*t5!q8+Q@eq)eLWI!(7s8<>ez6|33^^S%pO-e!f z3uxH|%aQOpBC|y13_5w;xGhu>b!%Q*?npGWrCa3lo+{fb$u%qV zy^3bB^_+Yz19MYlcwPg6ag2qR}q;8GRpX=m7DhSz-Pd=7Vo?*<7g-i~y$V_S|F1w^$) z=L)+prz;+!I#T>swZ2?jBbQ<#4iX{-$+M&xA!^o61*iGd-R?=ZwU>H)AcBlQsbguv@f&UmDJCL8neAe$$W3n|_l4W_7_AIZkrF^O$nE zkYZ>&>vG0eooMv~v%1yLr|qJZO^753UfJQ~$Gmc(s$qh1Y4__?C1yTIIvbCzm!|Z2 zAL70qu`&9zNWtxI0$M&fomqYO(&X4y4PSSE*o8iwLJ>Dr%Y%bTyg<-ZB(j8vOVF>l z1|J<&cLr`PK_LB?^nt4sA**{~+f4Mn8kd_CQq&gPx3~KnQFJTU=mxKah(hP>!v>!I zn!~!z`E*(2*+q&1^zTknD?YGVfj>{&{1I%oWu@+Z+K_pOTBtS)-r=+egx|PgihT$N z>xL}&cHhQzx0H6F0A~}3I~fEd?-8>2Ug>?vx0fa=w}qNwD1$syZkoE>KLY&=Ey&O% ze}jIFV-PgX&;@beGD0BU@;U_8h|+UV;6!0Hr9@ERmgV|L51|_R1l5 zobSI^a6}go`xVpsSC`GKY@)dask|UF3svOoKF9=&-}tl(M6qaUk&%a^nvT4&dcRtZ zVdSm4^4-EQiVExUY7`d~lrSUN%opXkOK+KB#!+tC zlI7emh^d%84Dgnhz5dIhB2uqqyknhVO;@q!@+^-0;%)7_;ol3#z8v%otqTxakB?Yl zVNvNg?abF?X@z;=yO+_*S8I>mPv^@rh^n$c^cHWwo1_Hkr+qwgkE5FtCAzRhadINo zumd%Vhh5Rmu=anEgsV4!dGMqJ$tO7d)0wly<>8*p1JDsg7j(A68$5h4JbYvU%d(;j zL-YdcIr4ooTU*7BBb@Yo(-cmcD=*J^SbV(cvp4xqP96KJH6g14p75KK^p0(o1gV=v zXAAg}g#YPlO3(JvZj>`$3z#o2a5oC?;$8C}e}u@u?SV5afX}^hr@WUCfRK{#+Kowv z<5U2Sd{j_B;xvT9iuE%m!gF!%jETz9@k(Y7x^JEtP$9)))@HfVy7!=oQYi}K-$m`% zo4*dC!7=J;ds8FzzPq1+#8zR!)nTo3z1F&6qYbar8KI?maW$NDNv^(oi}H{ahjDSW z@@%*;+N`yY$#E)2>=c?>2JlYuY|MBVzYE9_aY&bBZQK~RdZE9ZJ}({LsF_FG@t6c+ ziWw%RhFK4h%d*%hj;$*JzvP9*Z#{t)0ZGnim zs(F_ZEn0&4m$@9w9s-S#iXq5tRx`YJo6S*tsb7y4s;d@`23k{6wlI?TyAM-&Epnz1 z_lO63>8tkcX7d{JvmvHuyBY1(BuRv}3$XJ0a)XYJVhh^J7Wj05yh{RRqn*sr^AbtR z>~@B6_Z1Fs0k!jdMG*GJuPD0vM!*8zxX^j(+w8ukPe~z6iL%u6!=&93dqWAhQd(|$ z6h-W=RW3Lup<{|gAWNGYZyA_3Gz`{`Bq73VnwN`$!(T_WWuS4bof_Fbl#A^>^>^P= z5J;ZMiYISvZFOJzKaCS0&Io{$g|HLR&>2xzk?h;Y>Z4Uy7p!xO7Rm=aFG8^hmCDuY zFaIiW99s?={gH2`b>}%!p0g#RUwG%Mly#6hp>l1;<%MOagB7(GC$qb8y%SOq=|;Am zSkelHe>rIO`ePruXVdg*5m4(Oei&juDUnVraTE@X_8ZZk732di;w|s}#{mKj&KJgcCmSCNJiCmq03(?;{6PX7E?lP;@%eL|5CzSZ3qogG20 z?dKVBe>nwS9iOy;Fx>@y_Mj&Ru25jV7u}|#u&v(Bb#Sw^t8h^RWX>a7EI$`dOp@i_QAWxD!8L=ujy9(KV`gL%r2JAKDIi&8D&`F0A-{**YP5)rNt$zvyd;sOm9Dofd< z_(i)Z#fehbk3E^C94k+*h?&ZEIQ% z^bCc|BO0HXuw2oZcS+u*`>)0$f?C{Co0CJ zImd@fb^9$vT7R_NvsPa4n$u+4&9=qq*~nghzqQs+Xo$a`8LZr(3)xKOKE>oc9Pr+D zG)v#EqgPaqN@YJ(K|}Unh;@QMDC#;rA_ce^?7oUyJ7cd2zGmw@^p=L#=OTciUe7Z6 zFIM%CRdES;9wI%jGw^Z%9kvEs?q0Lyg&fLv!{Kk`hzQebF5}|F48j)L5j)*?nE>o& z0>R`S(t8v?e*}ki6L1)lqX=n)!=>GJ!g5PmTI^es>Fzm%Z|7>luB%L%RiBH5o0qkBiYc8ny>FEuDNSuvvler&272+C9Zk0aHaFa&* zCx*xKmH#9t(d%D~F_AjepC_8WYRaYAa~V&;@}@p0&(v_wHrY2nk0G2<9?TL!EraI1 zG4Oy+>b)aw6~~V#utc?dBZbH8hX6BkA2-p9jiS71j`lr(XbYmJs%n5DvaFgFdB0gJ9jJ zRSPorG7nptp))Ua8==8_Sx9~36A1Wn;t}nQo%Y^nGSqvU`ZO5%WZR=?M z6PM`r-Wvy=CZV0%iV0y$*=x6tGPm+j#CnZ|sp$hR;#3CZPr}n}a4u{e9k{GPr1HX( zc>!~u;eX|$Zn=hL_dC_xxdHwV+Sv5w6pz-_0@xGs;KSFkWNuaRu2lZZP%nq8P*7_5 z6T4xsr&WtPPN3+ht$lte+@c(5XlJr^KaZ?75og^97~)AsdI$N|q5uiXP;0w-M6M0> zEDPs@*CZ&o`Kq&HoG~z1PW^{B6aWx#O-)TQl!(&#{%=0H!dm!dh7CemO<1+AZh!{BdiZ8g(&16!ur)a zsSKra7_!URfm^Bg*~#K!K({gb0pt0z^FMa-m0wuzstYBF1h{MLvrldm6S~&d9-|N^ zNa})O^;{S@)G>!2F8V*MBHPu$)$3fbf(F~rHxQMlPT*_}v4wbokB^T7aDQo`_^kro zkRF1k6vT$b;}IZ%A6}YyP$PNs1btjyd)ksYPeIHeaRk)<4{-Cx4nRD@9xb}=vj3}S zU?hk`3;FX-w1-6!P4jlYHpKaIvAE?cZetLsSNN;&Y*F|3hCYk^ z?CgxL&CAfcTl_`Kej5vEF^dm) zH~qIk%cylz2_Gw>SE|J2m-?pxtqpl5tjl|03cb>i_k_|0J4cIX{f$B~LZ&E_PI`|G z`|q-6eIXfa-qADGhu$0Iigpf%M{r^L-d7)0D0iU%P0dzIvp;sUg8=ECcw^32&@-`e zWsU{~NdHIDfo-DFeCp4ZP5v#%yNPPC*<&iyi6=Q81Ll2&$6v*=vB!(cqXWuhLKfj+ z^N`-;E+-F5>{4+r6e6`D_q*2ut_3Vhxw;f@zUSQKtJbuyWy!?99?b+TS;lgU_#XIT zfW}dP0LTz`rg2YK+VBlwy3t{aYKy%g9;`ZKe*3#%!^hC;Nk;QpcM6)s@R6UipJtqQ!K>Fg0xwg{?_z7EDbBK(4a zh-Wl(S-dIN4@{cZ2`QFz+4gGHX0y-#N_^~%pTzR@O}sR!@69pO4|nFGCQW(?*w*od z?D>c7{a2jQ($botx)4u6AdvL1gLLAl=8)0mj>i-CnmeQN`yr+TzWQi^=4Zs{?A}zl z`YsXsb*j;GG%VsY>>11q)B~@xD{giE3$lmMHCU(}=bthE(-nR}=BVvgkFDz!%EsxK}l=Ch$<_b(hgB3lDt^AAUEFp2f6&;}~DfmhiKTo5J<7JJbIH`JKu z#iGCAf=q+o<6Iz>ZeF?8f|)T`)?Fm15`y%h#nP>NLRVFfa8mDtbTE>RbXZau?nU;hjCfmtz{$c3o6U1a1i&vLF z!`ox88SR~T3eVnWJX%Sf1bQN>4|1>Vb%hB%rK+lN!{;5`f(q_Cz~*^6-)gC@4DvKH zz}NO`HBESG&(?&7G6w-+?N!a$FJuPQc~NMtPZY+kZoppw6I&^8_Sl04B|+D;=I&cL|a!LSW=X zZ0)WZNvd7fgvB|p1aCCNHQ_7lS^3$T2ys(_=emd%YT3YD3iDAn{LEU~f{Ji&U+`sG6{R!M1s7UfP zx|i=^W#W+rKzSIJxnrXT9?mg+D_l(&wZX-Ln+2ep=xU2@&&yp6MqCC-B=u%*Zy!jz zVaU$F!hnk+c{M37>b@FsG0c`1bXaEC%Og>k5PEDw4__R7Aq_$)PBu@Bu3l>~f3aRx zlKVzRMicaY<&`*}7S#=FRDJbbk`kS`lvL=$`QQXFK8`OJ=oKFye*-m!hDxHKry_Q_yuZc!v4 z7c?cm6F0(fq4#}U^ZeQPbHUWooutM|UkjcM5aI0SN1Uw^CHb(10k+)Yt*ef%LfN6~eLsZfYqu{3`)ce3m+ zhb`*9a(jY!ka*-H)SeL;Glk0`5y-|918GfY)mOgb46>QYc%%4+AUeB<0n8L!NA0G z?nFa+MFZ%)PzOfc-Fokmo$67WXj#cvmZ>GwKepkakC5YAc6)^Wf#QgrF*M zLPSO4Y{#siWW?gVL#jPJj&Cc@o{dQbkuB)v5miua5k=@vW(gS-;hwsi-s~-0YQ&s; zGk}%s#~FRpbHf*IUrd8LFK_j1>K#z0k0Xw~ro;Pv6#9Z~Zm3qaN*{TUN`K8jbIFss zo=S zr=6&@r7WANNVjt59tD;QPskpOo`)fmgY?-af{tC`n!wE%Hd)l9D63l&;K;r6@&~jd zA7vqZ3pY?RCo~Mn%xobOn!kXzy2w|%mqgjcfpZn+*W7Ct+-qmg*Up-d9hS_~AOiak ze6o_0mV^^tVVxW#0aIuZNdcos4VXpv3C=oWp@LFi z18h|swqxTj=u*i4cWg?{uO>+xp>G@$XC{yRTQcz9PpC>-VTlklaU)KpV?3_ zc0GE}xG(Ps3f; z9ChxI`9_fV`^|F&>*rm_lU(z-R3nAV93WyWEyOzgLwG{SHWPpaJY`nOI2GsJm*w4? ztzDZXy)xslP@vQmj^M+WU+|SdQahnr1J89(>uc`IYucb)@Gn-cABqkAfeYl2=%_6` z7)cD^5m8+lU|Y3oY$UYg6G zfEqd;cREg|iXH#h7tTcBG`HYvxUNxrbM1vA2cWOUNN1As+~(Z7^r_w`v`ek347~or zZz6z~cD_TtZ*vj@WQf#|Go&YdRFm>%34Yr$CQxJTzaePqRGCvUS~>%pH`@)+k(Bi8 z<$N`DNVe;L;2kB2fL^IJb-31uX^8=<1VQ!O&DK!{xer3RMiE8X0)`*>Nz^^p8Dz!w z0f16rtkN_72Qqa_KQYPNoFZu);3ad%eTw<@+LP!SB>44%_c>6fR;7*Yv$TjvFOR5CJ!N+f~j@s$8<@+26VYkQz7T( z&0qY`tdRV5)h6jPU3USfN0-ZsoavvI4(Z!_ZNbJvylD%9uo*^YenfE^g6k1@l*kI} zyFm#42s784Q{R*+Q@yKwFgK>Py{6UE(IV5)!r^#3V;jANFP$sq@K0~ z5a_5mH9KTiVf*Q|b}Cf6!i?|F&7E?&dHTF}2Et+bVkK3lEdLOCRes`oflxfZh0d3B zEh;yi1{^7)omD9M70=U>CO*+yd^kH??g<8t=3RAYzidb$4od*UAaHIYgNW<)@vJ>x zMcg6JJuGCfxpO~~7X^i0j<6NF3zoUovv^ty*~;TBSPKlPA$yt)YYprlH5YK4#Mjk5 zS}~URQ2xa#0%;g?OCjN*{XdSb0w|8I2_CK=?gV%D;BLW#I|K_F2oPKr=Wuro5*&gC zcM0wgJi(pd4*%xgqKcyK*rV9@W_r4N=B}*QC$7>(*e*za;&# z>-4hA-*rDe%^@XYZWXx<6q!C0n?6M3-9>Ob^>Va>MLf-d_hKt)<9t+o*UHJ}Xthp- zZr8*7j+;4?_sYupO%y2IiC=4qUkm143R;C1l$(No-8C3%a3P?hu%~0n)**9n>SH4cra2>%&`&oQjWu60`hTK6x z&5lbm%U1kM3%FO8bGPk%dm)&gu);64vm<2U^{L1h@3=mRtXiQ94hpt^T)p%7ma1|P zF32>P>o#2H6ZHJqM8EpG;MxzuDh0XE2qw>!ALr2epQjvXLZQpXP+YXZII>^PzSKA` zwi7{oOMlq&eqpSsSgF+dte~sVM0Ygup9ZL$^dOxAJ-}t2IQrJ>C58WM-cz0U%?L=& zkG|fe`A)Bhz3%zH5WZZV`R)&Jcwb%D_>9wFir3Ij%`FfsasPR$0k>;hBxz|h0|Q!} zH0c0ITxH1s5(OJlK-!V%K_XJjVFZRCHyogo{q&l>

    # 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

    5XP9Z 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 delta 271766 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 z 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