diff options
author | Holger Zickner <[email protected]> | 2004-07-07 19:59:59 +0000 |
---|---|---|
committer | Holger Zickner <[email protected]> | 2004-07-07 19:59:59 +0000 |
commit | 6e23fc1074d1f0c2c2812f4c2e663f5a21a43c20 (patch) | |
tree | 46ecc6d0255c874ba4cd26dc3d0733f785019896 /src/jake2/qcommon |
import of Jake2 version sunrisesunrise
Diffstat (limited to 'src/jake2/qcommon')
-rw-r--r-- | src/jake2/qcommon/CM.java | 1927 | ||||
-rw-r--r-- | src/jake2/qcommon/Cbuf.java | 250 | ||||
-rw-r--r-- | src/jake2/qcommon/Com.java | 468 | ||||
-rw-r--r-- | src/jake2/qcommon/Cvar.java | 452 | ||||
-rw-r--r-- | src/jake2/qcommon/FS.java | 849 | ||||
-rw-r--r-- | src/jake2/qcommon/MD4.java | 312 | ||||
-rw-r--r-- | src/jake2/qcommon/MSG.java | 588 | ||||
-rw-r--r-- | src/jake2/qcommon/Netchan.java | 389 | ||||
-rw-r--r-- | src/jake2/qcommon/PMove.java | 1261 | ||||
-rw-r--r-- | src/jake2/qcommon/Qcommon.java | 315 | ||||
-rw-r--r-- | src/jake2/qcommon/SZ.java | 110 | ||||
-rw-r--r-- | src/jake2/qcommon/Z.java | 63 | ||||
-rw-r--r-- | src/jake2/qcommon/cmd_function_t.java | 35 | ||||
-rw-r--r-- | src/jake2/qcommon/longjmpException.java | 33 | ||||
-rw-r--r-- | src/jake2/qcommon/lump_t.java | 35 | ||||
-rw-r--r-- | src/jake2/qcommon/miptex_t.java | 49 | ||||
-rw-r--r-- | src/jake2/qcommon/netadr_t.java | 65 | ||||
-rw-r--r-- | src/jake2/qcommon/netchan_t.java | 79 | ||||
-rw-r--r-- | src/jake2/qcommon/qfiles.java | 655 | ||||
-rw-r--r-- | src/jake2/qcommon/sizebuf_t.java | 48 | ||||
-rw-r--r-- | src/jake2/qcommon/texinfo_t.java | 67 | ||||
-rw-r--r-- | src/jake2/qcommon/xcommand_t.java | 34 | ||||
-rw-r--r-- | src/jake2/qcommon/zhead_t.java | 38 |
23 files changed, 8122 insertions, 0 deletions
diff --git a/src/jake2/qcommon/CM.java b/src/jake2/qcommon/CM.java new file mode 100644 index 0000000..6cb141f --- /dev/null +++ b/src/jake2/qcommon/CM.java @@ -0,0 +1,1927 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 02.01.2004 by RST. +// $Id: CM.java,v 1.1 2004-07-07 19:59:29 hzi Exp $ + +package jake2.qcommon; + +import jake2.Defines; +import jake2.game.*; +import jake2.util.Lib; +import jake2.util.Math3D; +import jake2.util.Vargs; + +import java.io.FileNotFoundException; +import java.io.RandomAccessFile; +import java.nio.*; +import java.util.Arrays; + +public class CM extends Game { + + public static class cnode_t { + cplane_t plane; // ptr + int children[] = { 0, 0 }; // negative numbers are leafs + } + + public static class cbrushside_t { + cplane_t plane; // ptr + mapsurface_t surface; // ptr + } + + public static class cleaf_t { + int contents; + int cluster; + int area; + + // was unsigned short, but is ok (rst) + short firstleafbrush; + // was unsigned short, but is ok (rst) + short numleafbrushes; + } + + public static class cbrush_t { + int contents; + int numsides; + int firstbrushside; + int checkcount; // to avoid repeated testings + } + + public static class carea_t { + int numareaportals; + int firstareaportal; + int floodnum; // if two areas have equal floodnums, they are connected + int floodvalid; + } + + static int checkcount; + + static String map_name = ""; + + static int numbrushsides; + static cbrushside_t map_brushsides[] = new cbrushside_t[MAX_MAP_BRUSHSIDES]; + static { + for (int n = 0; n < MAX_MAP_BRUSHSIDES; n++) + map_brushsides[n] = new cbrushside_t(); + } + + public static int numtexinfo; + public static mapsurface_t map_surfaces[] = new mapsurface_t[MAX_MAP_TEXINFO]; + static { + for (int n = 0; n < MAX_MAP_TEXINFO; n++) + map_surfaces[n] = new mapsurface_t(); + } + + static int numplanes; + // extra for box hull ( +6) + static cplane_t map_planes[] = new cplane_t[MAX_MAP_PLANES + 6]; + + static { + for (int n = 0; n < MAX_MAP_PLANES + 6; n++) + map_planes[n] = new cplane_t(); + } + + static int numnodes; + // extra for box hull ( +6) + static cnode_t map_nodes[] = new cnode_t[MAX_MAP_NODES + 6]; + + static { + for (int n = 0; n < MAX_MAP_NODES + 6; n++) + map_nodes[n] = new cnode_t(); + } + + static int numleafs = 1; // allow leaf funcs to be called without a map + static cleaf_t map_leafs[] = new cleaf_t[MAX_MAP_LEAFS]; + static { + for (int n = 0; n < MAX_MAP_LEAFS; n++) + map_leafs[n] = new cleaf_t(); + } + + static int emptyleaf, solidleaf; + + static int numleafbrushes; + //static unsigned short map_leafbrushes[MAX_MAP_LEAFBRUSHES]; + public static int map_leafbrushes[] = new int[MAX_MAP_LEAFBRUSHES]; + public static int numcmodels; + public static cmodel_t map_cmodels[] = new cmodel_t[MAX_MAP_MODELS]; + static { + for (int n = 0; n < MAX_MAP_MODELS; n++) + map_cmodels[n] = new cmodel_t(); + + } + + public static int numbrushes; + public static cbrush_t map_brushes[] = new cbrush_t[MAX_MAP_BRUSHES]; + static { + for (int n = 0; n < MAX_MAP_BRUSHES; n++) + map_brushes[n] = new cbrush_t(); + + } + + public static int numvisibility; + public static byte map_visibility[] = new byte[MAX_MAP_VISIBILITY]; + + // main visibility data. rst + // was: static dvis_t *map_vis = (dvis_t *)map_visibility; + public static qfiles.dvis_t map_vis = new qfiles.dvis_t(ByteBuffer.wrap(map_visibility)); + + public static int numentitychars; + public static String map_entitystring; + + public static int numareas = 1; + public static carea_t map_areas[] = new carea_t[MAX_MAP_AREAS]; + static { + for (int n = 0; n < MAX_MAP_AREAS; n++) + map_areas[n] = new carea_t(); + + } + public static int numareaportals; + public static qfiles.dareaportal_t map_areaportals[] = new qfiles.dareaportal_t[MAX_MAP_AREAPORTALS]; + + static { + for (int n = 0; n < MAX_MAP_AREAPORTALS; n++) + map_areaportals[n] = new qfiles.dareaportal_t(); + + } + + public static int numclusters = 1; + + public static mapsurface_t nullsurface = new mapsurface_t(); + + public static int floodvalid; + + public static boolean portalopen[] = new boolean[MAX_MAP_AREAPORTALS]; + + public static cvar_t map_noareas; + + public static int c_pointcontents; + public static int c_traces, c_brush_traces; + + /* + =============================================================================== + + MAP LOADING + + =============================================================================== + */ + + public static byte cmod_base[]; + + // is that right (rst) ? + public static int checksum; + public static int last_checksum; + + /* + ================== + CM_LoadMap + + Loads in the map and all submodels + ================== + */ + public static cmodel_t CM_LoadMap(String name, boolean clientload, intwrap checksum) { + Com.DPrintf("CM_LoadMap...\n"); + byte buf[]; + int i; + qfiles.dheader_t header; + int length; + + map_noareas = Cvar.Get("map_noareas", "0", 0); + + if (0 == strcmp(map_name, name) && (clientload || 0 == Cvar.VariableValue("flushmap"))) { + + checksum.i = last_checksum; + + if (!clientload) { + Arrays.fill(portalopen, false); + FloodAreaConnections(); + } + return map_cmodels[0]; // still have the right version + } + + // free old stuff + numnodes = 0; + numleafs = 0; + numcmodels = 0; + numvisibility = 0; + numentitychars = 0; + map_entitystring = ""; + map_name = ""; + + if (name == null || name.length() == 0) { + numleafs = 1; + numclusters = 1; + numareas = 1; + checksum.i = 0; + return map_cmodels[0]; + // cinematic servers won't have anything at all + } + + // + // load the file + // + buf = FS.LoadFile(name); + + if (buf == null) + Com.Error(ERR_DROP, "Couldn't load " + name); + + length = buf.length; + + ByteBuffer bbuf = ByteBuffer.wrap(buf); + + last_checksum = MD4.Com_BlockChecksum(buf, length); + checksum.i = last_checksum; + + header = new qfiles.dheader_t(bbuf.slice()); + + if (header.version != BSPVERSION) + Com.Error( + ERR_DROP, + "CMod_LoadBrushModel: " + name + " has wrong version number (" + header.version + " should be " + BSPVERSION + ")"); + + cmod_base = buf; + + // load into heap + CMod_LoadSurfaces(header.lumps[LUMP_TEXINFO]); // ok. + CMod_LoadLeafs(header.lumps[LUMP_LEAFS]); + CMod_LoadLeafBrushes(header.lumps[LUMP_LEAFBRUSHES]); + CMod_LoadPlanes(header.lumps[LUMP_PLANES]); + CMod_LoadBrushes(header.lumps[LUMP_BRUSHES]); + CMod_LoadBrushSides(header.lumps[LUMP_BRUSHSIDES]); + CMod_LoadSubmodels(header.lumps[LUMP_MODELS]); + + CMod_LoadNodes(header.lumps[LUMP_NODES]); + CMod_LoadAreas(header.lumps[LUMP_AREAS]); + CMod_LoadAreaPortals(header.lumps[LUMP_AREAPORTALS]); + CMod_LoadVisibility(header.lumps[LUMP_VISIBILITY]); + CMod_LoadEntityString(header.lumps[LUMP_ENTITIES]); + + FS.FreeFile(buf); + + CM_InitBoxHull(); + + Arrays.fill(portalopen, false); + + FloodAreaConnections(); + + map_name = name; + + // debug (rst) + Com.p("Testing pointleafes:"); + for (int n = 0; n < 20; n++) { + float pos[] = new float[] {(float) (Math.random() * 1000), (float) (Math.random() * 1000), 0 }; + int x = CM_PointLeafnum(pos); + + Com.p(Lib.vtofsbeaty(pos) + " ---> leaf=" + x + " area = " +map_leafs[x].area); + } + + return map_cmodels[0]; + } + + /* + ================= + CMod_LoadSubmodels + ================= + */ + public static void CMod_LoadSubmodels(lump_t l) { + Com.DPrintf("CMod_LoadSubmodels...\n"); + qfiles.dmodel_t in; + cmodel_t out; + int i, j, count; + + if ((l.filelen % qfiles.dmodel_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / qfiles.dmodel_t.SIZE; + + if (count < 1) + Com.Error(ERR_DROP, "Map with no models"); + if (count > MAX_MAP_MODELS) + Com.Error(ERR_DROP, "Map has too many models"); + + Com.DPrintf(" numcmodels=" + count + "\n"); + numcmodels = count; + + if (debugloadmap) { + Com.DPrintf("submodles(headnode, <origin>, <mins>, <maxs>)\n"); + } + for (i = 0; i < count; i++) { + in = new qfiles.dmodel_t(ByteBuffer.wrap(cmod_base, i * qfiles.dmodel_t.SIZE + l.fileofs, qfiles.dmodel_t.SIZE)); + out = map_cmodels[i]; + + for (j = 0; j < 3; j++) { // spread the mins / maxs by a pixel + out.mins[j] = in.mins[j] - 1; + out.maxs[j] = in.maxs[j] + 1; + out.origin[j] = in.origin[j]; + } + out.headnode = in.headnode; + if (debugloadmap) { + Com.DPrintf( + "|%6i|%8.2f|%8.2f|%8.2f| %8.2f|%8.2f|%8.2f| %8.2f|%8.2f|%8.2f|\n", + new Vargs() + .add(out.headnode) + .add(out.origin[0]) + .add(out.origin[1]) + .add(out.origin[2]) + .add(out.mins[0]) + .add(out.mins[1]) + .add(out.mins[2]) + .add(out.maxs[0]) + .add(out.maxs[1]) + .add(out.maxs[2])); + } + } + } + static boolean debugloadmap = false; + + /* + ================= + CMod_LoadSurfaces + ================= + */ + public static void CMod_LoadSurfaces(lump_t l) { + Com.DPrintf("CMod_LoadSurfaces...\n"); + texinfo_t in; + mapsurface_t out; + int i, count; + + if ((l.filelen % texinfo_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / texinfo_t.SIZE; + if (count < 1) + Com.Error(ERR_DROP, "Map with no surfaces"); + if (count > MAX_MAP_TEXINFO) + Com.Error(ERR_DROP, "Map has too many surfaces"); + + numtexinfo = count; + Com.DPrintf("numtexinfo=" + count + "\n"); + if (debugloadmap) + Com.DPrintf("surfaces:\n"); + + for (i = 0; i < count; i++) { + out = map_surfaces[i] = new mapsurface_t(); + in = new texinfo_t(cmod_base, l.fileofs + i * texinfo_t.SIZE, texinfo_t.SIZE); + + out.c.name = in.texture; + out.rname = in.texture; + out.c.flags = in.flags; + out.c.value = in.value; + + if (debugloadmap) { + Com.DPrintf("|%20s|%20s|%6i|%6i|\n", new Vargs().add(out.c.name).add(out.rname).add(out.c.value).add(out.c.flags)); + } + + } + } + + /* + ================= + CMod_LoadNodes + + ================= + */ + public static void CMod_LoadNodes(lump_t l) { + Com.DPrintf("CMod_LoadNodes...\n"); + qfiles.dnode_t in; + int child; + cnode_t out; + int i, j, count; + + if ((l.filelen % qfiles.dnode_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size:" + l.fileofs + "," + qfiles.dnode_t.SIZE); + count = l.filelen / qfiles.dnode_t.SIZE; + + if (count < 1) + Com.Error(ERR_DROP, "Map has no nodes"); + if (count > MAX_MAP_NODES) + Com.Error(ERR_DROP, "Map has too many nodes"); + + numnodes = count; + Com.DPrintf(" numnodes=" + count + "\n"); + + if (debugloadmap) { + Com.DPrintf("nodes(planenum, child[0], child[1])\n"); + } + + for (i = 0; i < count; i++) { + in = new qfiles.dnode_t(ByteBuffer.wrap(cmod_base, qfiles.dnode_t.SIZE * i + l.fileofs, qfiles.dnode_t.SIZE)); + out = map_nodes[i]; + + out.plane = map_planes[in.planenum]; + for (j = 0; j < 2; j++) { + child = in.children[j]; + out.children[j] = child; + } + if (debugloadmap) { + Com.DPrintf("|%6i| %6i| %6i|\n", new Vargs().add(in.planenum).add(out.children[0]).add(out.children[1])); + } + } + } + + /* + ================= + CMod_LoadBrushes + + ================= + */ + public static void CMod_LoadBrushes(lump_t l) { + Com.DPrintf("CMod_LoadBrushes...\n"); + qfiles.dbrush_t in; + cbrush_t out; + int i, count; + + if ((l.filelen % qfiles.dbrush_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / qfiles.dbrush_t.SIZE; + + if (count > MAX_MAP_BRUSHES) + Com.Error(ERR_DROP, "Map has too many brushes"); + + numbrushes = count; + Com.DPrintf(" numbrushes=" + count + "\n"); + if (debugloadmap) { + Com.DPrintf("brushes:(firstbrushside, numsides, contents)\n"); + } + for (i = 0; i < count; i++) { + in = new qfiles.dbrush_t(ByteBuffer.wrap(cmod_base, i * qfiles.dbrush_t.SIZE + l.fileofs, qfiles.dbrush_t.SIZE)); + out = map_brushes[i]; + out.firstbrushside = in.firstside; + out.numsides = in.numsides; + out.contents = in.contents; + + if (debugloadmap) { + Com.DPrintf("| %6i| %6i| %8X|\n", new Vargs().add(out.firstbrushside).add(out.numsides).add(out.contents)); + } + } + } + + /* + ================= + CMod_LoadLeafs + ================= + */ + public static void CMod_LoadLeafs(lump_t l) { + Com.DPrintf("CMod_LoadLeafs...\n"); + int i; + cleaf_t out; + qfiles.dleaf_t in; + int count; + + if ((l.filelen % qfiles.dleaf_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / qfiles.dleaf_t.SIZE; + + if (count < 1) + Com.Error(ERR_DROP, "Map with no leafs"); + + // need to save space for box planes + if (count > MAX_MAP_PLANES) + Com.Error(ERR_DROP, "Map has too many planes"); + + Com.DPrintf(" numleafes=" + count + "\n"); + + numleafs = count; + numclusters = 0; + if (debugloadmap) + Com.DPrintf("cleaf-list:(contents, cluster, area, firstleafbrush, numleafbrushes)\n"); + for (i = 0; i < count; i++) { + in = new qfiles.dleaf_t(cmod_base, i * qfiles.dleaf_t.SIZE + l.fileofs, qfiles.dleaf_t.SIZE); + + out = map_leafs[i]; + + out.contents = in.contents; + out.cluster = in.cluster; + out.area = in.area; + out.firstleafbrush = (short) in.firstleafbrush; + out.numleafbrushes = (short) in.numleafbrushes; + + if (out.cluster >= numclusters) + numclusters = out.cluster + 1; + + if (debugloadmap) { + Com.DPrintf( + "|%8x|%6i|%6i|%6i|\n", + new Vargs().add(out.contents).add(out.cluster).add(out.area).add(out.firstleafbrush).add(out.numleafbrushes)); + } + + } + + Com.DPrintf(" numclusters=" + numclusters + "\n"); + + if (map_leafs[0].contents != CONTENTS_SOLID) + Com.Error(ERR_DROP, "Map leaf 0 is not CONTENTS_SOLID"); + + solidleaf = 0; + emptyleaf = -1; + + for (i = 1; i < numleafs; i++) { + if (map_leafs[i].contents == 0) { + emptyleaf = i; + break; + } + } + + if (emptyleaf == -1) + Com.Error(ERR_DROP, "Map does not have an empty leaf"); + } + + /* + ================= + CMod_LoadPlanes + ================= + */ + public static void CMod_LoadPlanes(lump_t l) { + Com.DPrintf("CMod_LoadPlanes...\n"); + int i, j; + cplane_t out; + qfiles.dplane_t in; + int count; + int bits; + + if ((l.filelen % qfiles.dplane_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / qfiles.dplane_t.SIZE; + + if (count < 1) + Com.Error(ERR_DROP, "Map with no planes"); + + // need to save space for box planes + if (count > MAX_MAP_PLANES) + Com.Error(ERR_DROP, "Map has too many planes"); + + Com.DPrintf(" numplanes=" + count + "\n"); + + numplanes = count; + if (debugloadmap) { + Com.DPrintf("cplanes(normal[0],normal[1],normal[2], dist, type, signbits)\n"); + } + + for (i = 0; i < count; i++) { + in = new qfiles.dplane_t(ByteBuffer.wrap(cmod_base, i * qfiles.dplane_t.SIZE + l.fileofs, qfiles.dplane_t.SIZE)); + + out = map_planes[i]; + + bits = 0; + for (j = 0; j < 3; j++) { + out.normal[j] = in.normal[j]; + + if (out.normal[j] < 0) + bits |= 1 << j; + } + + out.dist = in.dist; + out.type = (byte) in.type; + out.signbits = (byte) bits; + + if (debugloadmap) { + Com.DPrintf( + "|%6.2f|%6.2f|%6.2f| %10.2f|%3i| %1i|\n", + new Vargs().add(out.normal[0]).add(out.normal[1]).add(out.normal[2]).add(out.dist).add(out.type).add( + out.signbits)); + } + } + } + + /* + ================= + CMod_LoadLeafBrushes + ================= + */ + public static void CMod_LoadLeafBrushes(lump_t l) { + Com.DPrintf("CMod_LoadLeafBrushes...\n"); + int i; + int out[]; + short in[]; + int count; + + if ((l.filelen % 2) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / 2; + + Com.DPrintf(" numbrushes=" + count + "\n"); + + if (count < 1) + Com.Error(ERR_DROP, "Map with no planes"); + + // need to save space for box planes + if (count > MAX_MAP_LEAFBRUSHES) + Com.Error(ERR_DROP, "Map has too many leafbrushes"); + + out = map_leafbrushes; + numleafbrushes = count; + + ByteBuffer bb = ByteBuffer.wrap(cmod_base, l.fileofs, count * 2).order(ByteOrder.LITTLE_ENDIAN); + + if (debugloadmap) { + Com.DPrintf("map_brushes:\n"); + } + + for (i = 0; i < count; i++) { + out[i] = bb.getShort(); + if (debugloadmap) { + Com.DPrintf("|%6i|%6i|\n", new Vargs().add(i).add(out[i])); + } + } + } + + /* + ================= + CMod_LoadBrushSides + ================= + */ + public static void CMod_LoadBrushSides(lump_t l) { + Com.DPrintf("CMod_LoadBrushSides...\n"); + int i, j; + cbrushside_t out; + qfiles.dbrushside_t in; + int count; + int num; + + if ((l.filelen % qfiles.dbrushside_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + count = l.filelen / qfiles.dbrushside_t.SIZE; + + // need to save space for box planes + if (count > MAX_MAP_BRUSHSIDES) + Com.Error(ERR_DROP, "Map has too many planes"); + + numbrushsides = count; + + Com.DPrintf(" numbrushsides=" + count + "\n"); + + if (debugloadmap) { + Com.DPrintf("brushside(planenum, surfacenum):\n"); + } + for (i = 0; i < count; i++) { + + in = + new qfiles.dbrushside_t( + ByteBuffer.wrap(cmod_base, i * qfiles.dbrushside_t.SIZE + l.fileofs, qfiles.dbrushside_t.SIZE)); + + out = map_brushsides[i]; + + num = in.planenum; + + out.plane = map_planes[num]; // pointer + + j = in.texinfo; + + if (j >= numtexinfo) + Com.Error(ERR_DROP, "Bad brushside texinfo"); + + // rst: some mysterious happens here, even in the original code ???, texinfo is -1!!! + // hoz: checked against c version: ok. + if (j == -1) + out.surface = new mapsurface_t(); // just for safety + else + out.surface = map_surfaces[j]; + + if (debugloadmap) { + Com.DPrintf("| %6i| %6i|\n", new Vargs().add(num).add(j)); + } + } + } + + /* + ================= + CMod_LoadAreas + ================= + */ + public static void CMod_LoadAreas(lump_t l) { + Com.DPrintf("CMod_LoadAreas...\n"); + int i; + carea_t out; + qfiles.darea_t in; + int count; + + if ((l.filelen % qfiles.darea_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / qfiles.darea_t.SIZE; + + if (count > MAX_MAP_AREAS) + Com.Error(ERR_DROP, "Map has too many areas"); + + Com.DPrintf(" numareas=" + count + "\n"); + numareas = count; + + if (debugloadmap) { + Com.DPrintf("areas(numportals, firstportal)\n"); + } + + for (i = 0; i < count; i++) { + + in = new qfiles.darea_t(ByteBuffer.wrap(cmod_base, i * qfiles.darea_t.SIZE + l.fileofs, qfiles.darea_t.SIZE)); + out = map_areas[i]; + + out.numareaportals = in.numareaportals; + out.firstareaportal = in.firstareaportal; + out.floodvalid = 0; + out.floodnum = 0; + if (debugloadmap) { + Com.DPrintf("| %6i| %6i|\n", new Vargs().add(out.numareaportals).add(out.firstareaportal)); + } + } + } + + /* + ================= + CMod_LoadAreaPortals + ================= + */ + public static void CMod_LoadAreaPortals(lump_t l) { + Com.DPrintf("CMod_LoadAreaPortals...\n"); + int i; + qfiles.dareaportal_t out; + qfiles.dareaportal_t in; + int count; + + if ((l.filelen % qfiles.dareaportal_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + count = l.filelen / qfiles.dareaportal_t.SIZE; + + if (count > MAX_MAP_AREAS) + Com.Error(ERR_DROP, "Map has too many areas"); + + numareaportals = count; + Com.DPrintf(" numareaportals=" + count + "\n"); + if (debugloadmap) { + Com.DPrintf("areaportals(portalnum, otherarea)\n"); + } + for (i = 0; i < count; i++) { + in = + new qfiles.dareaportal_t( + ByteBuffer.wrap(cmod_base, i * qfiles.dareaportal_t.SIZE + l.fileofs, qfiles.dareaportal_t.SIZE)); + + out = map_areaportals[i]; + + out.portalnum = in.portalnum; + out.otherarea = in.otherarea; + + if (debugloadmap) { + Com.DPrintf("|%6i|%6i|\n", new Vargs().add(out.portalnum).add(out.otherarea)); + } + } + } + + /* + ================= + CMod_LoadVisibility + ================= + */ + public static void CMod_LoadVisibility(lump_t l) { + Com.DPrintf("CMod_LoadVisibility...\n"); + int i; + + numvisibility = l.filelen; + + Com.DPrintf(" numvisibility=" + numvisibility + "\n"); + + if (l.filelen > MAX_MAP_VISIBILITY) + Com.Error(ERR_DROP, "Map has too large visibility lump"); + + //was: memcpy(map_visibility, cmod_base + l.fileofs, l.filelen); + System.arraycopy(cmod_base, l.fileofs, map_visibility, 0, l.filelen); + + ByteBuffer bb = ByteBuffer.wrap(map_visibility, 0, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + map_vis = new qfiles.dvis_t(bb); + + } + + /* + ================= + CMod_LoadEntityString + ================= + */ + public static void CMod_LoadEntityString(lump_t l) { + Com.DPrintf("CMod_LoadEntityString...\n"); + + numentitychars = l.filelen; + + if (l.filelen > MAX_MAP_ENTSTRING) + Com.Error(ERR_DROP, "Map has too large entity lump"); + + int x = 0; + for (; x < l.filelen && cmod_base[x + l.fileofs] != 0; x++); + + map_entitystring = new String(cmod_base, l.fileofs, x).trim(); + } + + /* + ================== + CM_InlineModel + ================== + */ + + // works fine + public static cmodel_t InlineModel(String name) { + int num; + + if (name == null || name.charAt(0) != '*') + Com.Error(ERR_DROP, "CM_InlineModel: bad name"); + + num = atoi(name.substring(1)); + + if (num < 1 || num >= numcmodels) + Com.Error(ERR_DROP, "CM_InlineModel: bad number"); + + return map_cmodels[num]; + } + + public static int CM_NumClusters() { + return numclusters; + } + + public static int CM_NumInlineModels() { + return numcmodels; + } + + public static String CM_EntityString() { + return map_entitystring; + } + + public static int CM_LeafContents(int leafnum) { + if (leafnum < 0 || leafnum >= numleafs) + Com.Error(ERR_DROP, "CM_LeafContents: bad number"); + return map_leafs[leafnum].contents; + } + + public static int CM_LeafCluster(int leafnum) { + if (leafnum < 0 || leafnum >= numleafs) + Com.Error(ERR_DROP, "CM_LeafCluster: bad number"); + return map_leafs[leafnum].cluster; + } + + public static int CM_LeafArea(int leafnum) { + if (leafnum < 0 || leafnum >= numleafs) + Com.Error(ERR_DROP, "CM_LeafArea: bad number"); + return map_leafs[leafnum].area; + } + + //======================================================================= + + static cplane_t box_planes[]; + static int box_headnode; + static cbrush_t box_brush; + static cleaf_t box_leaf; + + /* + =================== + 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. + =================== + */ + public static void CM_InitBoxHull() { + int i; + int side; + cnode_t c; + cplane_t p; + cbrushside_t s; + + box_headnode = numnodes; // noch platz f?r 6 brushes + + box_planes = + new cplane_t[] { + map_planes[numplanes], + map_planes[numplanes + 1], + map_planes[numplanes + 2], + map_planes[numplanes + 3], + map_planes[numplanes + 4], + map_planes[numplanes + 5], + map_planes[numplanes + 6], + map_planes[numplanes + 7], + map_planes[numplanes + 8], + map_planes[numplanes + 9], + map_planes[numplanes + 10], + map_planes[numplanes + 11], + map_planes[numplanes + 12] }; + + if (numnodes + 6 > MAX_MAP_NODES + || numbrushes + 1 > MAX_MAP_BRUSHES + || numleafbrushes + 1 > MAX_MAP_LEAFBRUSHES + || numbrushsides + 6 > MAX_MAP_BRUSHSIDES + || numplanes + 12 > MAX_MAP_PLANES) + Com.Error(ERR_DROP, "Not enough room for box tree"); + + box_brush = map_brushes[numbrushes]; + box_brush.numsides = 6; + box_brush.firstbrushside = numbrushsides; + box_brush.contents = CONTENTS_MONSTER; + + box_leaf = map_leafs[numleafs]; + box_leaf.contents = CONTENTS_MONSTER; + box_leaf.firstleafbrush = (short) numleafbrushes; + box_leaf.numleafbrushes = 1; + + map_leafbrushes[numleafbrushes] = numbrushes; + + for (i = 0; i < 6; i++) { + side = i & 1; + + // brush sides + s = map_brushsides[numbrushsides + i]; + s.plane = map_planes[(numplanes + i * 2 + side)]; + s.surface = nullsurface; + + // nodes + c = map_nodes[box_headnode + i]; + c.plane = map_planes[(numplanes + i * 2)]; + c.children[side] = -1 - emptyleaf; + if (i != 5) + c.children[side ^ 1] = box_headnode + i + 1; + else + c.children[side ^ 1] = -1 - numleafs; + + // planes + p = box_planes[i * 2]; + p.type = (byte) (i >> 1); + p.signbits = 0; + VectorClear(p.normal); + p.normal[i >> 1] = 1; + + p = box_planes[i * 2 + 1]; + p.type = (byte) (3 + (i >> 1)); + p.signbits = 0; + VectorClear(p.normal); + p.normal[i >> 1] = -1; + } + } + + /* + =================== + CM_HeadnodeForBox + + To keep everything totally uniform, bounding boxes are turned into small + BSP trees instead of being compared directly. + =================== + */ + public static int HeadnodeForBox(float[] mins, float[] maxs) { + 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]; + + return box_headnode; + } + + /* + ================== + CM_PointLeafnum_r + ================== + */ + public static int CM_PointLeafnum_r(float[] p, int num) { + float d; + cnode_t node; + cplane_t plane; + + while (num >= 0) { + node = map_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; + } + + public static int CM_PointLeafnum(float[] p) { + if (numplanes == 0) + return 0; // sound may call this without map loaded + return CM_PointLeafnum_r(p, 0); + } + + /* + ============= + CM_BoxLeafnums + + Fills in a list of all the leafs touched + ============= + */ + static int leaf_count, leaf_maxcount; + static int leaf_list[]; + static float leaf_mins[], leaf_maxs[]; + static int leaf_topnode; + + public static void CM_BoxLeafnums_r(int nodenum) { + cplane_t plane; + cnode_t node; + int s; + + while (true) { + if (nodenum < 0) { + if (leaf_count >= leaf_maxcount) { + Com.DPrintf("CM_BoxLeafnums_r: overflow\n"); + return; + } + leaf_list[leaf_count++] = -1 - nodenum; + return; + } + + node = map_nodes[nodenum]; + plane = node.plane; + + s = Math3D.BoxOnPlaneSide(leaf_mins, leaf_maxs, plane); + + if (s == 1) + nodenum = node.children[0]; + else if (s == 2) + nodenum = node.children[1]; + else { + // go down both + if (leaf_topnode == -1) + leaf_topnode = nodenum; + CM_BoxLeafnums_r(node.children[0]); + nodenum = node.children[1]; + } + } + } + + public static int CM_BoxLeafnums_headnode(float[] mins, float[] maxs, int list[], int listsize, int headnode, intwrap topnode) { + leaf_list = list; + leaf_count = 0; + leaf_maxcount = listsize; + leaf_mins = mins; + leaf_maxs = maxs; + + leaf_topnode = -1; + + CM_BoxLeafnums_r(headnode); + + if (topnode != null) + topnode.i = leaf_topnode; + + return leaf_count; + } + + public static int CM_BoxLeafnums(float[] mins, float[] maxs, int list[], int listsize, intwrap topnode) { + return CM_BoxLeafnums_headnode(mins, maxs, list, listsize, map_cmodels[0].headnode, topnode); + } + + public static class intwrap { + public intwrap(int i) { + this.i = i; + } + public int i; + } + + /* + ================== + CM_PointContents + ================== + */ + public static int PointContents(float[] p, int headnode) { + int l; + + if (numnodes == 0) // map not loaded + return 0; + + l = CM_PointLeafnum_r(p, headnode); + + return map_leafs[l].contents; + } + + /* + ================== + CM_TransformedPointContents + + Handles offseting and rotation of the end points for moving and + rotating entities + ================== + */ + public static int TransformedPointContents(float[] p, int headnode, float[] origin, float[] angles) { + float[] p_l = { 0, 0, 0 }; + float[] temp = { 0, 0, 0 }; + float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 }; + int l; + + // subtract origin offset + VectorSubtract(p, origin, p_l); + + // rotate start and end into the models frame of reference + if (headnode != box_headnode && (angles[0] != 0 || angles[1] != 0 || angles[2] != 0)) { + 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); + } + + l = CM_PointLeafnum_r(p_l, headnode); + + return map_leafs[l].contents; + } + + /* + =============================================================================== + + BOX TRACING + + =============================================================================== + */ + + // 1/32 epsilon to keep floating point happy + private static final float DIST_EPSILON = 0.03125f; + + private static float[] trace_start = { 0, 0, 0 }, trace_end = { 0, 0, 0 }; + private static float[] trace_mins = { 0, 0, 0 }, trace_maxs = { 0, 0, 0 }; + private static float[] trace_extents = { 0, 0, 0 }; + + private static trace_t trace_trace = new trace_t(); + private static int trace_contents; + private static boolean trace_ispoint; // optimized case + + /* + ================ + CM_ClipBoxToBrush + ================ + */ + public static void CM_ClipBoxToBrush(float[] mins, float[] maxs, float[] p1, float[] p2, trace_t trace, cbrush_t brush) { + int i, j; + cplane_t plane, clipplane; + float dist; + float enterfrac, leavefrac; + float[] ofs = { 0, 0, 0 }; + float d1, d2; + boolean getout, startout; + float f; + cbrushside_t side, leadside; + + enterfrac = -1; + leavefrac = 1; + clipplane = null; + + if (brush.numsides == 0) + return; + + c_brush_traces++; + + getout = false; + startout = false; + leadside = null; + + for (i = 0; i < brush.numsides; i++) { + side = map_brushsides[brush.firstbrushside + i]; + plane = side.plane; + + // FIXME: special case for axial + + if (!trace_ispoint) { // general box case + + // push the plane out apropriately for mins/maxs + + // FIXME: use signbits into 8 way lookup for each mins/maxs + for (j = 0; j < 3; j++) { + if (plane.normal[j] < 0) + ofs[j] = maxs[j]; + else + ofs[j] = mins[j]; + } + dist = DotProduct(ofs, plane.normal); + dist = plane.dist - dist; + } + else { // special point case + dist = plane.dist; + } + + d1 = DotProduct(p1, plane.normal) - dist; + d2 = DotProduct(p2, plane.normal) - dist; + + if (d2 > 0) + getout = true; // endpoint is not in solid + if (d1 > 0) + startout = true; + + // if completely in front of face, no intersection + if (d1 > 0 && d2 >= d1) + return; + + if (d1 <= 0 && d2 <= 0) + continue; + + // crosses face + if (d1 > d2) { // enter + f = (d1 - DIST_EPSILON) / (d1 - d2); + if (f > enterfrac) { + enterfrac = f; + clipplane = plane; + leadside = side; + } + } + else { // leave + f = (d1 + DIST_EPSILON) / (d1 - d2); + if (f < leavefrac) + leavefrac = f; + } + } + + if (!startout) { // original point was inside brush + trace.startsolid = true; + if (!getout) + trace.allsolid = true; + return; + } + if (enterfrac < leavefrac) { + if (enterfrac > -1 && enterfrac < trace.fraction) { + if (enterfrac < 0) + enterfrac = 0; + trace.fraction = enterfrac; + // copy + trace.plane.set(clipplane); + trace.surface = leadside.surface.c; + trace.contents = brush.contents; + } + } + } + + /* + ================ + CM_TestBoxInBrush + ================ + */ + public static void CM_TestBoxInBrush(float[] mins, float[] maxs, float[] p1, trace_t trace, cbrush_t brush) { + int i, j; + cplane_t plane; + float dist; + float[] ofs = { 0, 0, 0 }; + float d1; + cbrushside_t side; + + if (brush.numsides == 0) + return; + + for (i = 0; i < brush.numsides; i++) { + side = map_brushsides[brush.firstbrushside + i]; + plane = side.plane; + + // FIXME: special case for axial + // general box case + // push the plane out apropriately for mins/maxs + // FIXME: use signbits into 8 way lookup for each mins/maxs + + for (j = 0; j < 3; j++) { + if (plane.normal[j] < 0) + ofs[j] = maxs[j]; + else + ofs[j] = mins[j]; + } + dist = DotProduct(ofs, plane.normal); + dist = plane.dist - dist; + + d1 = DotProduct(p1, plane.normal) - dist; + + // if completely in front of face, no intersection + if (d1 > 0) + return; + + } + + // inside this brush + trace.startsolid = trace.allsolid = true; + trace.fraction = 0; + trace.contents = brush.contents; + } + + /* + ================ + CM_TraceToLeaf + ================ + */ + public static void CM_TraceToLeaf(int leafnum) { + int k; + int brushnum; + cleaf_t leaf; + cbrush_t b; + + leaf = map_leafs[leafnum]; + if (0 == (leaf.contents & trace_contents)) + return; + + // trace line against all brushes in the leaf + for (k = 0; k < leaf.numleafbrushes; k++) { + + brushnum = map_leafbrushes[leaf.firstleafbrush + k]; + b = map_brushes[brushnum]; + if (b.checkcount == checkcount) + continue; // already checked this brush in another leaf + b.checkcount = checkcount; + + if (0 == (b.contents & trace_contents)) + continue; + CM_ClipBoxToBrush(trace_mins, trace_maxs, trace_start, trace_end, trace_trace, b); + if (0 == trace_trace.fraction) + return; + } + + } + + /* + ================ + CM_TestInLeaf + ================ + */ + public static void CM_TestInLeaf(int leafnum) { + int k; + int brushnum; + cleaf_t leaf; + cbrush_t b; + + leaf = map_leafs[leafnum]; + if (0 == (leaf.contents & trace_contents)) + return; + // trace line against all brushes in the leaf + for (k = 0; k < leaf.numleafbrushes; k++) { + brushnum = map_leafbrushes[leaf.firstleafbrush + k]; + b = map_brushes[brushnum]; + if (b.checkcount == checkcount) + continue; // already checked this brush in another leaf + b.checkcount = checkcount; + + if (0 == (b.contents & trace_contents)) + continue; + CM_TestBoxInBrush(trace_mins, trace_maxs, trace_start, trace_trace, b); + if (0 == trace_trace.fraction) + return; + } + + } + + /* + ================== + CM_RecursiveHullCheck + ================== + */ + public static void CM_RecursiveHullCheck(int num, float p1f, float p2f, float[] p1, float[] p2) { + cnode_t node; + cplane_t plane; + float t1, t2, offset; + float frac, frac2; + float idist; + int i; + float[] mid = { 0, 0, 0 }; + int side; + float midf; + + if (trace_trace.fraction <= p1f) + return; // already hit something nearer + + // if < 0, we are in a leaf node + if (num < 0) { + CM_TraceToLeaf(-1 - num); + return; + } + + // + // find the point distances to the seperating plane + // and the offset for the size of the box + // + node = map_nodes[num]; + plane = node.plane; + + if (plane.type < 3) { + t1 = p1[plane.type] - plane.dist; + t2 = p2[plane.type] - plane.dist; + offset = trace_extents[plane.type]; + } + else { + t1 = DotProduct(plane.normal, p1) - plane.dist; + t2 = DotProduct(plane.normal, p2) - plane.dist; + if (trace_ispoint) + offset = 0; + else + offset = + Math.abs(trace_extents[0] * plane.normal[0]) + + Math.abs(trace_extents[1] * plane.normal[1]) + + Math.abs(trace_extents[2] * plane.normal[2]); + } + + // see which sides we need to consider + if (t1 >= offset && t2 >= offset) { + CM_RecursiveHullCheck(node.children[0], p1f, p2f, p1, p2); + return; + } + if (t1 < -offset && t2 < -offset) { + CM_RecursiveHullCheck(node.children[1], p1f, p2f, p1, p2); + return; + } + + // put the crosspoint DIST_EPSILON pixels on the near side + if (t1 < t2) { + idist = 1.0f / (t1 - t2); + side = 1; + frac2 = (t1 + offset + DIST_EPSILON) * idist; + frac = (t1 - offset + DIST_EPSILON) * idist; + } + else if (t1 > t2) { + idist = 1.0f / (t1 - t2); + side = 0; + frac2 = (t1 - offset - DIST_EPSILON) * idist; + frac = (t1 + offset + DIST_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; + for (i = 0; i < 3; i++) + mid[i] = p1[i] + frac * (p2[i] - p1[i]); + + CM_RecursiveHullCheck(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; + for (i = 0; i < 3; i++) + mid[i] = p1[i] + frac2 * (p2[i] - p1[i]); + + CM_RecursiveHullCheck(node.children[side ^ 1], midf, p2f, mid, p2); + } + + //====================================================================== + + /* + ================== + CM_BoxTrace + ================== + */ + public static trace_t BoxTrace(float[] start, float[] end, float[] mins, float[] maxs, int headnode, int brushmask) { + + // for multi-check avoidance + checkcount++; + + // for statistics, may be zeroed + c_traces++; + + // fill in a default trace + //was: memset(& trace_trace, 0, sizeof(trace_trace)); + trace_trace = new trace_t(); + + trace_trace.fraction = 1; + trace_trace.surface = nullsurface.c; + + if (numnodes == 0) // map not loaded + { + Com.DPrintf("dummy trace zurueck, da map not loaded!\n"); + return trace_trace; + } + + trace_contents = brushmask; + VectorCopy(start, trace_start); + VectorCopy(end, trace_end); + VectorCopy(mins, trace_mins); + VectorCopy(maxs, trace_maxs); + + // + // check for position test special case + // + if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) { + + int leafs[] = new int[1024]; + int i, numleafs; + float[] c1 = { 0, 0, 0 }, c2 = { 0, 0, 0 }; + int topnode = 0; + + VectorAdd(start, mins, c1); + VectorAdd(start, maxs, c2); + + for (i = 0; i < 3; i++) { + c1[i] -= 1; + c2[i] += 1; + } + + intwrap tn = new intwrap(topnode); + + numleafs = CM_BoxLeafnums_headnode(c1, c2, leafs, 1024, headnode, tn); + topnode = tn.i; + for (i = 0; i < numleafs; i++) { + CM_TestInLeaf(leafs[i]); + if (trace_trace.allsolid) + break; + } + VectorCopy(start, trace_trace.endpos); + return trace_trace; + } + + // + // check for point special case + // + if (mins[0] == 0 && mins[1] == 0 && mins[2] == 0 && maxs[0] == 0 && maxs[1] == 0 && maxs[2] == 0) { + trace_ispoint = true; + VectorClear(trace_extents); + } + else { + trace_ispoint = false; + trace_extents[0] = -mins[0] > maxs[0] ? -mins[0] : maxs[0]; + trace_extents[1] = -mins[1] > maxs[1] ? -mins[1] : maxs[1]; + trace_extents[2] = -mins[2] > maxs[2] ? -mins[2] : maxs[2]; + } + + // + // general sweeping through world + // + CM_RecursiveHullCheck(headnode, 0, 1, start, end); + + if (trace_trace.fraction == 1) { + VectorCopy(end, trace_trace.endpos); + } + else { + for (int i = 0; i < 3; i++) + trace_trace.endpos[i] = start[i] + trace_trace.fraction * (end[i] - start[i]); + } + return trace_trace; + } + + /* + ================== + CM_TransformedBoxTrace + + Handles offseting and rotation of the end points for moving and + rotating entities + ================== + */ + public static trace_t TransformedBoxTrace( + float[] start, + float[] end, + float[] mins, + float[] maxs, + int headnode, + int brushmask, + float[] origin, + float[] angles) { + trace_t trace; + float[] start_l = { 0, 0, 0 }, end_l = { 0, 0, 0 }; + float[] a = { 0, 0, 0 }; + float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 }; + float[] temp = { 0, 0, 0 }; + boolean rotated; + + // subtract origin offset + VectorSubtract(start, origin, start_l); + VectorSubtract(end, origin, end_l); + + // rotate start and end into the models frame of reference + if (headnode != box_headnode && (angles[0] != 0 || angles[1] != 0 || angles[2] != 0)) + rotated = true; + else + rotated = false; + + if (rotated) { + AngleVectors(angles, forward, right, up); + + VectorCopy(start_l, temp); + start_l[0] = DotProduct(temp, forward); + start_l[1] = -DotProduct(temp, right); + start_l[2] = DotProduct(temp, up); + + VectorCopy(end_l, temp); + end_l[0] = DotProduct(temp, forward); + end_l[1] = -DotProduct(temp, right); + end_l[2] = DotProduct(temp, up); + } + + // sweep the box through the model + trace = BoxTrace(start_l, end_l, mins, maxs, headnode, brushmask); + + if (rotated && trace.fraction != 1.0) { + // FIXME: figure out how to do this with existing angles + VectorNegate(angles, a); + AngleVectors(a, forward, right, up); + + VectorCopy(trace.plane.normal, temp); + trace.plane.normal[0] = DotProduct(temp, forward); + trace.plane.normal[1] = -DotProduct(temp, right); + trace.plane.normal[2] = DotProduct(temp, up); + } + + 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]); + + return trace; + } + + /* + =============================================================================== + PVS / PHS + =============================================================================== + */ + + /* + =================== + CM_DecompressVis + =================== + */ + public static void CM_DecompressVis(byte in[], int offset, byte out[]) { + int c; + + int row; + + row = (numclusters + 7) >> 3; + int outp = 0; + int inp = offset; + + if (in == null || numvisibility == 0) { // no vis info, so make all visible + while (row != 0) { + out[outp++] = (byte) 0xFF; + row--; + } + return; + } + + do { + if (in[inp] != 0) { + out[outp++] = in[inp++]; + continue; + } + + c = in[inp + 1] & 0xFF; + inp += 2; + if (outp + c > row) { + c = row - (outp); + Com.DPrintf("warning: Vis decompression overrun\n"); + } + while (c != 0) { + out[outp++] = 0; + c--; + } + } + while (outp < row); + } + + public static byte pvsrow[] = new byte[MAX_MAP_LEAFS / 8]; + public static byte phsrow[] = new byte[MAX_MAP_LEAFS / 8]; + + public static byte[] CM_ClusterPVS(int cluster) { + if (cluster == -1) + Lib.memset(pvsrow, (byte) 0, (numclusters + 7) >> 3); + else + CM_DecompressVis(map_visibility, map_vis.bitofs[cluster][DVIS_PVS], pvsrow); + return pvsrow; + } + + public static byte[] CM_ClusterPHS(int cluster) { + if (cluster == -1) + Lib.memset(phsrow, (byte) 0, (numclusters + 7) >> 3); + else + CM_DecompressVis(map_visibility, map_vis.bitofs[cluster][Defines.DVIS_PHS], phsrow); + return phsrow; + } + + /* + =============================================================================== + AREAPORTALS + =============================================================================== + */ + + public static void FloodArea_r(carea_t area, int floodnum) { + Com.DPrintf("FloodArea_r(" + floodnum + ")...\n"); + int i; + qfiles.dareaportal_t p; + + if (area.floodvalid == floodvalid) { + if (area.floodnum == floodnum) + return; + Com.Error(ERR_DROP, "FloodArea_r: reflooded"); + } + + area.floodnum = floodnum; + area.floodvalid = floodvalid; + + for (i = 0; i < area.numareaportals; i++) { + p = map_areaportals[area.firstareaportal + i]; + if (portalopen[p.portalnum]) + FloodArea_r(map_areas[p.otherarea], floodnum); + } + } + + /* + ==================== + FloodAreaConnections + ==================== + */ + public static void FloodAreaConnections() { + Com.DPrintf("FloodAreaConnections...\n"); + + int i; + carea_t area; + int floodnum; + + // all current floods are now invalid + floodvalid++; + floodnum = 0; + + // area 0 is not used + for (i = 1; i < numareas; i++) { + + area = map_areas[i]; + + if (area.floodvalid == floodvalid) + continue; // already flooded into + floodnum++; + FloodArea_r(area, floodnum); + } + } + + /* + ================= + CM_SetAreaPortalState + ================= + */ + public static void CM_SetAreaPortalState(int portalnum, boolean open) { + if (portalnum > numareaportals) + Com.Error(ERR_DROP, "areaportal > numareaportals"); + + portalopen[portalnum] = open; + FloodAreaConnections(); + } + + /* + ================= + CM_AreasConnected + ================= + */ + + public static boolean CM_AreasConnected(int area1, int area2) { + if (map_noareas.value != 0) + return true; + + if (area1 > numareas || area2 > numareas) + Com.Error(ERR_DROP, "area > numareas"); + + if (map_areas[area1].floodnum == map_areas[area2].floodnum) + return true; + + return false; + } + + /* + ================= + CM_WriteAreaBits + + Writes a length byte followed by a bit vector of all the areas + that area in the same flood as the area parameter + + This is used by the client refreshes to cull visibility + ================= + */ + public static int CM_WriteAreaBits(byte buffer[], int area) { + int i; + int floodnum; + int bytes; + + bytes = (numareas + 7) >> 3; + + if (map_noareas.value != 0) { // for debugging, send everything + Lib.memset(buffer, 255, bytes); + } + else { + Lib.memset(buffer, 0, bytes); + floodnum = map_areas[area].floodnum; + for (i = 0; i < numareas; i++) { + if (map_areas[i].floodnum == floodnum || area == 0) + buffer[i >> 3] |= 1 << (i & 7); + } + } + + return bytes; + } + + /* + =================== + CM_WritePortalState + + Writes the portal state to a savegame file + =================== + */ + + public static void CM_WritePortalState(RandomAccessFile os) { + + //was: fwrite(portalopen, sizeof(portalopen), 1, f); + try { + + for (int n = 0; n < portalopen.length; n++) + if (portalopen[n]) + os.writeInt(1); + else + os.writeInt(0); + } + catch (Exception e) { + Com.Printf("ERROR:" + e); + e.printStackTrace(); + } + } + + /* + =================== + CM_ReadPortalState + + Reads the portal state from a savegame file + and recalculates the area connections + =================== + */ + public static void CM_ReadPortalState(RandomAccessFile f) { + + //was: FS_Read(portalopen, sizeof(portalopen), f); + int len = portalopen.length * 4; + + byte buf[] = new byte[len]; + + try { + FS.Read(buf, len, f); + } + catch (FileNotFoundException e) { + Com.Printf("ERROR:" + e); + } + ByteBuffer bb = ByteBuffer.wrap(buf); + IntBuffer ib = bb.asIntBuffer(); + + //TODO: i assume upward counting (rst) + for (int n = 0; n < portalopen.length; n++) + portalopen[n] = ib.get() != 0; + + FloodAreaConnections(); + } + + /* + ============= + CM_HeadnodeVisible + + Returns true if any leaf under headnode has a cluster that + is potentially visible + ============= + */ + public static boolean CM_HeadnodeVisible(int nodenum, byte visbits[]) { + int leafnum; + int cluster; + cnode_t node; + + if (nodenum < 0) { + leafnum = -1 - nodenum; + cluster = map_leafs[leafnum].cluster; + if (cluster == -1) + return false; + if (0 != (visbits[cluster >>> 3] & (1 << (cluster & 7)))) + return true; + return false; + } + + node = map_nodes[nodenum]; + if (CM_HeadnodeVisible(node.children[0], visbits)) + return true; + return CM_HeadnodeVisible(node.children[1], visbits); + } +} diff --git a/src/jake2/qcommon/Cbuf.java b/src/jake2/qcommon/Cbuf.java new file mode 100644 index 0000000..7a28a6c --- /dev/null +++ b/src/jake2/qcommon/Cbuf.java @@ -0,0 +1,250 @@ +/* + * Cbuf.java + * Copyright (C) 2003 + * + * $Id: Cbuf.java,v 1.1 2004-07-07 19:59:27 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +import jake2.Defines; +import jake2.Globals; +import jake2.game.Cmd; +import jake2.util.Lib; + +/** + * Cbuf + */ +public final class Cbuf { + + /** + * + */ + public static void Init() { + SZ.Init(Globals.cmd_text, Globals.cmd_text_buf, Globals.cmd_text_buf.length); + } + + public static void InsertText(String text) { + + byte[] temp = null; + int templen = 0; + + // copy off any commands still remaining in the exec buffer + templen = Globals.cmd_text.cursize; + if (templen != 0) { + temp = new byte[templen]; + System.arraycopy(Globals.cmd_text.data, 0, temp, 0, templen); + SZ.Clear(Globals.cmd_text); + } + + // add the entire text of the file + Cbuf.AddText(text); + + // add the copied off data + if (templen != 0) { + SZ.Write(Globals.cmd_text, temp, templen); + temp = null; + } + } + + /** + * @param clear + */ + static void AddEarlyCommands(boolean clear) { + + for (int i = 0; i < Com.Argc(); i++) { + String s = Com.Argv(i); + if (!s.equals("+set")) + continue; + Cbuf.AddText("set " + Com.Argv(i + 1) + " " + Com.Argv(i + 2) + "n"); + if (clear) { + Com.ClearArgv(i); + Com.ClearArgv(i + 1); + Com.ClearArgv(i + 2); + } + i += 2; + } + } + + /** + * @return + */ + static boolean AddLateCommands() { + int i; + int j; + boolean ret = false; + + // build the combined string to parse from + int s = 0; + int argc = Com.Argc(); + for (i = 1; i < argc; i++) { + s += Com.Argv(i).length(); + } + if (s == 0) + return false; + + String text = ""; + for (i = 1; i < argc; i++) { + text += Com.Argv(i); + if (i != argc - 1) + text += " "; + } + + // pull out the commands + String build = ""; + for (i = 0; i < text.length(); i++) { + if (text.charAt(i) == '+') { + i++; + + for (j = i;(text.charAt(j) != '+') && (text.charAt(j) != '-') && j < text.length(); j++); + + build += text.substring(i, j - 1); + build += "\n"; + + i = j - 1; + } + } + + ret = (build.length() != 0); + if (ret) + Cbuf.AddText(build); + + text = null; + build = null; + + return ret; + } + + /** + * @param text + */ + public static void AddText(String text) { + int l = text.length(); + + if (Globals.cmd_text.cursize + l >= Globals.cmd_text.maxsize) { + Com.Printf("Cbuf_AddText: overflow\n"); + return; + } + SZ.Write(Globals.cmd_text, text.getBytes(), l); + } + + /** + * + */ + public static void Execute() { + + byte[] text = null; + byte[] line = new byte[1024]; + + Globals.alias_count = 0; // don't allow infinite alias loops + + while (Globals.cmd_text.cursize != 0) { + // find a \n or ; line break + text = Globals.cmd_text.data; + + int quotes = 0; + int i; + + for (i = 0; i < Globals.cmd_text.cursize; i++) { + if (text[i] == '"') + quotes++; + if (!(quotes % 2 != 0) && text[i] == ';') + break; // don't break if inside a quoted string + if (text[i] == '\n') + break; + } + + System.arraycopy(text, 0, line, 0, i); + line[i] = 0; + + // delete the text from the command buffer and move remaining commands down + // this is necessary because commands (exec, alias) can insert data at the + // beginning of the text buffer + + if (i == Globals.cmd_text.cursize) + Globals.cmd_text.cursize = 0; + else { + i++; + Globals.cmd_text.cursize -= i; + byte[] tmp = new byte[Globals.cmd_text.cursize]; + + System.arraycopy(text, i, tmp, 0, Globals.cmd_text.cursize); + System.arraycopy(tmp, 0, text, 0, Globals.cmd_text.cursize); + text[Globals.cmd_text.cursize] = '\0'; + + } + + // execute the command line + int len = jake2.util.Lib.strlen(line); + //System.out.println("Cbuf.executelen:" + Globals.cmd_text.cursize + "/" + len); + + String cmd = new String(line, 0, len); + //System.out.println("Cbuf.data :" + new String(text,0,16).replace('\n','.').replace('\r','.')); + //System.out.println("Cbuf.execute:" + xxx.replace('\n','.').replace('\r','.') ); + Cmd.ExecuteString(cmd); + + if (Globals.cmd_wait) { + // skip out while text still remains in buffer, leaving it + // for next frame + Globals.cmd_wait = false; + break; + } + } + } + + public static void ExecuteText(int exec_when, String text) { + switch(exec_when) { + case Defines.EXEC_NOW: + Cmd.ExecuteString(text); + break; + case Defines.EXEC_INSERT: + Cbuf.InsertText(text); + break; + case Defines.EXEC_APPEND: + Cbuf.AddText(text); + break; + default: + Com.Error(Defines.ERR_FATAL, "Cbuf_ExecuteText: bad exec_when"); + } + } + + /* + ============ + Cbuf_CopyToDefer + ============ + */ + public static void CopyToDefer() { + Lib.memcpy(Globals.defer_text_buf, Globals.cmd_text_buf, Globals.cmd_text.cursize); + Globals.defer_text_buf[Globals.cmd_text.cursize] = 0; + Globals.cmd_text.cursize = 0; + } + + /* + ============ + Cbuf_InsertFromDefer + ============ + */ + public static void InsertFromDefer() { + InsertText(new String(Globals.defer_text_buf).trim()); + Globals.defer_text_buf[0] = 0; + } + +} diff --git a/src/jake2/qcommon/Com.java b/src/jake2/qcommon/Com.java new file mode 100644 index 0000000..63b7d0e --- /dev/null +++ b/src/jake2/qcommon/Com.java @@ -0,0 +1,468 @@ +/* + * Com.java + * Copyright (C) 2003 + * + * $Id: Com.java,v 1.1 2004-07-07 19:59:30 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +import jake2.Defines; +import jake2.Globals; +import jake2.client.CL; +import jake2.client.Console; +import jake2.game.Cmd; +import jake2.server.SV; +import jake2.server.SV_MAIN; +import jake2.sys.Sys; +import jake2.util.Lib; +import jake2.util.PrintfFormat; +import jake2.util.Vargs; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.logging.Logger; + +/** + * Com + * TODO complete Com interface + */ +public final class Com { + + public static class RD_Flusher { + public void rd_flush(int target, byte [] buffer) { + } + } + + static int rd_target; + static byte[] rd_buffer; + static int rd_buffersize; + static RD_Flusher rd_flusher; + + public static void BeginRedirect(int target, byte [] buffer, int buffersize, RD_Flusher flush) { + if (0 == target || null == buffer || 0 == buffersize || null == flush) + return; + + rd_target = target; + rd_buffer = buffer; + rd_buffersize = buffersize; + rd_flusher = flush; + + rd_buffer = null; + } + + public static void EndRedirect() { + rd_flusher.rd_flush(rd_target, rd_buffer); + + rd_target = 0; + rd_buffer = null; + rd_buffersize = 0; + rd_flusher = null; + } + + static boolean recursive = false; + + static String msg = ""; + + private static Logger logger = Logger.getLogger(Com.class.getName()); + + // helper class to replace the pointer-pointer + public static class ParseHelp { + + public ParseHelp(String in, int offset) { + this(in.toCharArray(), offset); + } + + public ParseHelp(String in) { + if (in == null) { + data = null; + } + else { + data = in.toCharArray(); + } + index = 0; + } + + public ParseHelp(char in[]) { + this(in, 0); + } + + public ParseHelp(char in[], int offset) { + if (in == null || in.length == 0) + data = null; + else + data = in; + index = offset; + } + + public char getchar() { + // faster than if + try { + return data[index]; + } + catch (Exception e) { + data = null; + // last char + return 0; + } + } + + public char nextchar() { + // faster than if + try { + index++; + return data[index]; + } + catch (Exception e) { + data = null; + // avoid int wraps; + index--; + // last char + return 0; + } + } + + public boolean isEof() { + return data == null; + } + + public int index; + public char data[]; + + public char skipwhites() { + char c; + while (((c = getchar()) <= ' ') && c != 0) + index++; + return c; + } + + public char skipwhitestoeol() { + char c; + while (((c = getchar()) <= ' ') && c != '\n' && c != 0) + index++; + return c; + } + + public char skiptoeol() { + char c; + while ((c = getchar()) != '\n' && c != 0) + index++; + return c; + } + } + + public static char com_token[] = new char[Defines.MAX_TOKEN_CHARS]; + + // See GameSpanw.ED_ParseEdict() to see how to use it now. + // works perfect ! + public static String Parse(ParseHelp hlp) { + + int c; + int len = 0; + len = 0; + + com_token[0] = 0; + + if (hlp.data == null) { + return ""; + } + + // skip whitespace + hlp.skipwhites(); + + if (hlp.isEof()) { + return ""; + } + + // skip // comments + if (hlp.getchar() == '/') { + if (hlp.nextchar() == '/') { + if ((hlp.skiptoeol() == 0) || (hlp.skipwhites() == 0)) { + return ""; + } + } + else { + com_token[len] = '/'; + len++; + } + } + // handle quoted strings specially + if (hlp.getchar() == '\"') { + while (true) { + c = hlp.nextchar(); + if (c == '\"' || c == 0) { + + char xxx = hlp.nextchar(); + com_token[len] = '?'; + return new String(com_token, 0, len); + } + if (len < Defines.MAX_TOKEN_CHARS) { + com_token[len] = hlp.getchar(); + len++; + } + } + } + + // parse a regular word + do { + if (len < Defines.MAX_TOKEN_CHARS) { + com_token[len] = hlp.getchar(); + len++; + } + + c = hlp.nextchar(); + } + while (c > 32); + + if (len == Defines.MAX_TOKEN_CHARS) { + Printf("Token exceeded " + Defines.MAX_TOKEN_CHARS + " chars, discarded.\n"); + len = 0; + } + + // trigger the eof + hlp.skipwhites(); + + com_token[len] = 0; + return new String(com_token, 0, len); + } + + public static xcommand_t Error_f = new xcommand_t() { + public void execute() throws longjmpException { + Error(Globals.ERR_FATAL, Cmd.Argv(1)); + } + }; + + public static void Error(int code, String fmt) throws longjmpException { + Error(code, fmt, null); + } + + public static void Error(int code, String fmt, Vargs vargs) throws longjmpException { + // va_list argptr; + // static char msg[MAXPRINTMSG]; + + if (recursive) { + Sys.Error("recursive error after: " + msg); + } + recursive = true; + + msg = sprintf(fmt, vargs); + + if (code == Defines.ERR_DISCONNECT) { + CL.Drop(); + recursive = false; + throw new longjmpException(); + } + else if (code == Defines.ERR_DROP) { + Com.Printf("********************\nERROR: " + msg + "\n********************\n"); + SV_MAIN.SV_Shutdown("Server crashed: " + msg + "\n", false); + CL.Drop(); + recursive = false; + //throw new longjmpException(); + } + else { + SV_MAIN.SV_Shutdown("Server fatal crashed: %s" + msg + "\n", false); + CL.Shutdown(); + } + + Sys.Error(msg); + } + + /** + * Com_InitArgv checks the number of command line arguments + * and copies all arguments with valid length into com_argv. + */ + static void InitArgv(String[] args) throws longjmpException { + + if (args.length > Globals.MAX_NUM_ARGVS) { + Com.Error(Globals.ERR_FATAL, "argc > MAX_NUM_ARGVS"); + } + + Globals.com_argc = args.length; + for (int i = 0; i < Globals.com_argc; i++) { + if (args[i].length() >= Globals.MAX_TOKEN_CHARS) + Globals.com_argv[i] = ""; + else + Globals.com_argv[i] = args[i]; + } + } + + public static void DPrintf(String fmt) { + DPrintf(fmt, null); + } + + public static void d(String fmt) { + DPrintf(fmt + "\n", null); + } + + public static void Printf(String fmt) { + Printf(fmt, null); + } + + public static void DPrintf(String fmt, Vargs vargs) { + if (Globals.developer == null || Globals.developer.value == 0) + return; // don't confuse non-developers with techie stuff... + + Printf(fmt, vargs); + } + + public static void Printf(String fmt, Vargs vargs) { + // TODO Com.Printf ist nur zum testen + String msg = sprintf(fmt, vargs); + + if (rd_target != 0) + { + if ((msg.length() + Lib.strlen(rd_buffer)) > (rd_buffersize - 1)) + { + rd_flusher.rd_flush(rd_target, rd_buffer); + // *rd_buffer = 0; + rd_buffer[rd_buffersize] = '\0'; + } + // TODO handle rd_buffer + // strcat(rd_buffer, msg); + return; + } + + Console.Print(msg); + + // also echo to debugging console + Sys.ConsoleOutput(msg); + + // logfile + if (Globals.logfile_active != null && Globals.logfile_active.value != 0) + { + String name; + + if (Globals.logfile == null) + { + name = FS.Gamedir() + "/qconsole.log"; + if (Globals.logfile_active.value > 2) + try + { + Globals.logfile = new RandomAccessFile(name, "a"); + } + catch (FileNotFoundException e) + { + // TODO: do quake2 error handling! + e.printStackTrace(); + } + else + try + { + Globals.logfile = new RandomAccessFile(name, "rw"); + } + catch (FileNotFoundException e1) + { + // TODO: do quake2 error handling! + e1.printStackTrace(); + } + } + if (Globals.logfile != null) + try + { + Globals.logfile.writeChars(msg); + } + catch (IOException e) + { + // TODO: do quake2 error handling! + e.printStackTrace(); + } + if (Globals.logfile_active.value > 1) + ; // do nothing + // fflush (logfile); // force it to save every time + } + } + + public static void Println(String fmt) { + Printf(fmt); + Printf("\n"); + } + + public static void p(String fmt) { + Printf(fmt); + Printf("\n"); + } + + public static String sprintf(String fmt, Vargs vargs) { + String msg = ""; + if (vargs == null || vargs.size() == 0) { + msg = fmt; + } + else { + msg = new PrintfFormat(fmt).sprintf(vargs.toArray()); + } + return msg; + } + + public static int ServerState() { + return Globals.server_state; + } + + public static int Argc() { + return Globals.com_argc; + } + + public static String Argv(int arg) { + if (arg < 0 || arg >= Globals.com_argc || Globals.com_argv[arg].length() < 1) + return ""; + return Globals.com_argv[arg]; + } + + public static void ClearArgv(int arg) { + if (arg < 0 || arg >= Globals.com_argc || Globals.com_argv[arg].length() < 1) + return; + Globals.com_argv[arg] = ""; + } + + public static void Quit() { + SV_MAIN.SV_Shutdown("Server quit\n", false); + CL.Shutdown(); + + if (Globals.logfile != null) { + try + { + Globals.logfile.close(); + } + catch (IOException e) + { + } + Globals.logfile = null; + } + + Sys.Quit(); + } + + + public static void SetServerState(int i) + { + Globals.server_state = i; + } + + + public static int BlockChecksum(byte[] buf, int length) + { + return MD4.Com_BlockChecksum(buf, length); + } + + public static void StripExtension(String string, String string2) { + // TODO implement! + } +}
\ No newline at end of file diff --git a/src/jake2/qcommon/Cvar.java b/src/jake2/qcommon/Cvar.java new file mode 100644 index 0000000..b84a40c --- /dev/null +++ b/src/jake2/qcommon/Cvar.java @@ -0,0 +1,452 @@ +/* + * Cvar.java + * Copyright (C) 2003 + * + * $Id: Cvar.java,v 1.1 2004-07-07 19:59:30 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +package jake2.qcommon; + +import java.io.IOException; +import java.io.RandomAccessFile; + +import jake2.Defines; +import jake2.Globals; +import jake2.game.*; +import jake2.util.Lib; + +/** + * Cvar implements console variables. The original code is + * located in cvar.c + */ +public class Cvar extends Globals { + + /** + * @param var_name + * @param var_value + * @param flags + * @return + */ + public static cvar_t Get(String var_name, String var_value, int flags) { + cvar_t var; + + if ((flags & (CVAR_USERINFO | CVAR_SERVERINFO)) != 0) { + if (!Info.Info_Validate(var_name)) { + Com.Printf("invalid info cvar name\n"); + return null; + } + } + + var = Cvar.FindVar(var_name); + if (var != null) { + var.flags |= flags; + return var; + } + + if (var_value == null) + return null; + + if ((flags & (CVAR_USERINFO | CVAR_SERVERINFO)) != 0) { + if (!InfoValidate(var_value)) { + Com.Printf("invalid info cvar value\n"); + return null; + } + } + var = new cvar_t(); + var.name = new String(var_name); + var.string = new String(var_value); + var.modified = true; + // handles atof(var.string) + try { + var.value = Float.parseFloat(var.string); + } + catch (NumberFormatException e) { + var.value = 0.0f; + } + // link the variable in + var.next = Globals.cvar_vars; + Globals.cvar_vars = var; + + var.flags = flags; + + return var; + } + + static void Init() { + Cmd.AddCommand("set", Set_f); + Cmd.AddCommand("cvarlist", List_f); + } + + public static String VariableString(String var_name) { + cvar_t var; + var = FindVar(var_name); + return (var == null) ? "" : var.string; + } + + static cvar_t FindVar(String var_name) { + cvar_t var; + + for (var = Globals.cvar_vars; var != null; var = var.next) { + if (var_name.equals(var.name)) + return var; + } + + return null; + } + + /* + ============ + Cvar_FullSet + ============ + */ + public static cvar_t FullSet(String var_name, String value, int flags) { + cvar_t var; + + var = Cvar.FindVar(var_name); + if (null == var) { // create it + return Cvar.Get(var_name, value, flags); + } + + var.modified = true; + + if ((var.flags & CVAR_USERINFO) != 0) + Globals.userinfo_modified = true; // transmit at next oportunity + + var.string = value; + var.value = Lib.atof(var.string); + var.flags = flags; + + return var; + } + + /* + ============ + Cvar_Set + ============ + */ + public static cvar_t Set(String var_name, String value) { + return Set2(var_name, value, false); + } + + /* + ============ + Cvar_Set2 + ============ + */ + static cvar_t Set2(String var_name, String value, boolean force) { + + cvar_t var = Cvar.FindVar(var_name); + if (var == null) { // create it + return Cvar.Get(var_name, value, 0); + } + + if ((var.flags & (CVAR_USERINFO | CVAR_SERVERINFO)) != 0) { + if (!InfoValidate(value)) { + Com.Printf("invalid info cvar value\n"); + return var; + } + } + + if (!force) { + if ((var.flags & CVAR_NOSET) != 0) { + Com.Printf(var_name + " is write protected.\n"); + return var; + } + + if ((var.flags & CVAR_LATCH) != 0) { + if (var.latched_string != null) { + if (value.equals(var.latched_string)) + return var; + //Z_Free (var.latched_string); + var.latched_string = null; + } + else { + if (value.equals(var.string)) + return var; + } + + if (Com.ServerState() != 0) { + Com.Printf(var_name + " will be changed for next game.\n"); + //var.latched_string = CopyString(value); + var.latched_string = value; + } + else { + //var.string = CopyString(value); + var.string = value; + var.value = Lib.atof(var.string); + if (var.name.equals("game")) { + FS.SetGamedir(var.string); + FS.ExecAutoexec(); + } + } + return var; + } + } + else { + if (var.latched_string != null) { + //Z_Free(var.latched_string); + var.latched_string = null; + } + } + + if (value.equals(var.string)) + return var; // not changed + + var.modified = true; + + if ((var.flags & CVAR_USERINFO) != 0) + Globals.userinfo_modified = true; // transmit at next oportunity + + //Z_Free(var.string); // free the old value string + + //var.string = CopyString(value); + var.string = value; + var.value = Lib.atof(var.string); + + return var; + } + + static xcommand_t Set_f = new xcommand_t() { + public void execute() { + int c; + int flags; + + c = Cmd.Argc(); + if (c != 3 && c != 4) { + Com.Printf("usage: set <variable> <value> [u / s]\n"); + return; + } + + if (c == 4) { + if (Cmd.Argv(3).equals("u")) + flags = CVAR_USERINFO; + else if (Cmd.Argv(3).equals("s")) + flags = CVAR_SERVERINFO; + else { + Com.Printf("flags can only be 'u' or 's'\n"); + return; + } + Cvar.FullSet(Cmd.Argv(1), Cmd.Argv(2), flags); + } + else + Cvar.Set(Cmd.Argv(1), Cmd.Argv(2)); + + } + + }; + + static xcommand_t List_f = new xcommand_t() { + public void execute() { + cvar_t var; + int i; + + i = 0; + for (var = Globals.cvar_vars; var != null; var = var.next, i++) { + if ((var.flags & CVAR_ARCHIVE) != 0) + Com.Printf("*"); + else + Com.Printf(" "); + if ((var.flags & CVAR_USERINFO) != 0) + Com.Printf("U"); + else + Com.Printf(" "); + if ((var.flags & CVAR_SERVERINFO) != 0) + Com.Printf("S"); + else + Com.Printf(" "); + if ((var.flags & CVAR_NOSET) != 0) + Com.Printf("-"); + else if ((var.flags & CVAR_LATCH) != 0) + Com.Printf("L"); + else + Com.Printf(" "); + Com.Printf(" " + var.name + " \"" + var.string + "\"\n"); + } + Com.Printf(i + " cvars\n"); + } + }; + + /* + ============ + Cvar_ForceSet + ============ + */ + public static cvar_t ForceSet(String var_name, String value) { + return Cvar.Set2(var_name, value, true); + } + /* + ============ + Cvar_SetValue + ============ + */ + public static void SetValue(String var_name, float value) { + Cvar.Set(var_name, "" + value); + } + + /* + ============ + Cvar_VariableValue + ============ + */ + public static float VariableValue(String var_name) { + cvar_t var = Cvar.FindVar(var_name); + if (var == null) + return 0; + return Lib.atof(var.string); + } + + /* + ============ + Cvar_Command + + Handles variable inspection and changing from the console + ============ + */ + public static boolean Command() { + cvar_t v; + + // check variables + v = Cvar.FindVar(Cmd.Argv(0)); + if (v == null) + return false; + + // perform a variable print or set + if (Cmd.Argc() == 1) { + Com.Printf("\"" + v.name + "\" is \"" + v.string + "\"\n"); + return true; + } + + Cvar.Set(v.name, Cmd.Argv(1)); + return true; + } + + public static String BitInfo(int bit) { + String info; + cvar_t var; + + info = ""; + + for (var = Globals.cvar_vars; var != null; var = var.next) { + if ((var.flags & bit) != 0) + info = Info.Info_SetValueForKey1(info, var.name, var.string); + } + return info; + } + + // returns an info string containing all the CVAR_SERVERINFO cvars + public static String Serverinfo() { + return BitInfo(Defines.CVAR_SERVERINFO); + } + + public static void GetLatchedVars() { + cvar_t var; + + for (var = Globals.cvar_vars; var != null; var = var.next) { + if (var.latched_string == null || var.latched_string == "") + continue; + var.string = var.latched_string; + var.latched_string = null; + try { + var.value = Float.parseFloat(var.string); + } + catch (NumberFormatException e) { + var.value = 0.0f; + } + if (var.name.equals("game")) { + FS.SetGamedir(var.string); + FS.ExecAutoexec(); + } + } + } + + /** + * returns an info string containing all the CVAR_USERINFO cvars. + */ + public static String Userinfo() { + return BitInfo(CVAR_USERINFO); + } + + public static void WriteVariables(String path) { + cvar_t var; + RandomAccessFile f; + String buffer; + + f = Lib.fopen(path, "rw"); + if (f == null) + return; + + try { + f.seek(f.length()); + } catch (IOException e1) { + fclose(f); + return; + } + for (var = cvar_vars; var != null; var = var.next) { + if ((var.flags & CVAR_ARCHIVE) != 0) { + buffer = "set " + var.name + " \"" + var.string + "\"\n"; + try { + f.writeBytes(buffer); + } + catch (IOException e) { + } + } + } + fclose(f); + } + + /* + ============ + Cvar_CompleteVariable + ============ + */ + static String CompleteVariable(String partial) { + cvar_t cvar; + int len; + + len = partial.length(); + + if (len == 0) + return null; + + // check match + for (cvar = Globals.cvar_vars; cvar != null; cvar = cvar.next) + if (cvar.name.startsWith(partial)) + return cvar.name; + + return null; + } + + /* + ============ + Cvar_InfoValidate + ============ + */ + static boolean InfoValidate(String s) { + if (s.indexOf("\\") != -1) + return false; + if (s.indexOf("\"") != -1) + return false; + if (s.indexOf(";") != -1) + return false; + return true; + } +} diff --git a/src/jake2/qcommon/FS.java b/src/jake2/qcommon/FS.java new file mode 100644 index 0000000..47d760c --- /dev/null +++ b/src/jake2/qcommon/FS.java @@ -0,0 +1,849 @@ +/* + * FS.java + * Copyright (C) 2003 + * + * $Id: FS.java,v 1.1 2004-07-07 19:59:31 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +import jake2.Globals; +import jake2.game.Cmd; +import jake2.game.cvar_t; +import jake2.sys.Sys; + +import java.io.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.imageio.stream.FileImageInputStream; + +/** + * FS + * + * @author cwei + */ +public final class FS extends Globals { + + private static Logger logger = Logger.getLogger(FS.class.getName()); + + /* + ============================================================================= + + QUAKE FILESYSTEM + + ============================================================================= + */ + + public static class packfile_t { + static final int SIZE = 64; + static final int NAME_SIZE = 56; + + String name; // char name[56] + int filepos, filelen; + + public String toString() { + return name + " [ length: " + filelen + " pos: " + filepos + " ]"; + } + } + + public static class pack_t { + String filename; + RandomAccessFile handle; + int numfiles; + Hashtable files; // with packfile_t entries + } + + public static String fs_gamedir; + public static cvar_t fs_basedir; + public static cvar_t fs_cddir; + public static cvar_t fs_gamedirvar; + + public static class filelink_t { + String from; + int fromlength; + String to; + } + + public static List fs_links = new LinkedList(); // with filelink_t entries + + public static class searchpath_t { + String filename; + pack_t pack; // only one of filename / pack will be used + searchpath_t next; + } + + public static searchpath_t fs_searchpaths; + public static searchpath_t fs_base_searchpaths; // without gamedirs + + /* + + All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources. + + The "base directory" is the path to the directory holding the quake.exe and all game directories. The sys_* files pass this to host_init in quakeparms_t->basedir. This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory. The base directory is + only used during filesystem initialization. + + The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to. This can be overridden with the "-game" command line parameter. The game directory can never be changed while quake is executing. This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't. + + */ + + /* + ============ + FS_CreatePath + + Creates any directories needed to store the given filename + ============ + */ + public static void CreatePath(String path) { + int index = path.lastIndexOf('/'); + // -1 if not found and 0 means write to root + if (index > 0) { + File f = new File(path.substring(0, index)); + if (!f.mkdirs()) { + logger.log(Level.WARNING, "can't create path \"" + path + '"'); + } + } + } + + /* + ============== + FS_FCloseFile + + For some reason, other dll's can't just call fclose() + on files returned by FS_FOpenFile... + ============== + */ + public static void FCloseFile(RandomAccessFile file) throws IOException { + file.close(); + } + + public static void FCloseFile(InputStream stream) throws IOException { + stream.close(); + } + + public static int FileLength(String filename) { + searchpath_t search; + String netpath; + pack_t pak; + int i; + filelink_t link; + + file_from_pak = 0; + + // check for links first + for (Iterator it = fs_links.iterator(); it.hasNext();) { + link = (filelink_t) it.next(); + + if (filename.regionMatches(0, link.from, 0, link.fromlength)) { + netpath = link.to + filename.substring(link.fromlength); + File file = new File(netpath); + if (file.canRead()) { + Com.DPrintf("link file: " + netpath + '\n'); + return (int) file.length(); + } + return -1; + } + } + + // search through the path, one element at a time + + for (search = fs_searchpaths; search != null; search = search.next) { + // is the element a pak file? + if (search.pack != null) { + // look through all the pak file elements + pak = search.pack; + filename = filename.toLowerCase(); + packfile_t entry = (packfile_t) pak.files.get(filename); + + if (entry != null) { + // found it! + file_from_pak = 1; + Com.DPrintf("PackFile: " + pak.filename + " : " + filename + '\n'); + // open a new file on the pakfile + File file = new File(pak.filename); + if (!file.canRead()) { + Com.Error(Globals.ERR_FATAL, "Couldn't reopen " + pak.filename); + } + return entry.filelen; + } + } + else { + // check a file in the directory tree + netpath = search.filename + '/' + filename; + + File file = new File(netpath); + if (!file.canRead()) + continue; + + Com.DPrintf("FindFile: " + netpath + '\n'); + + return (int) file.length(); + } + } + Com.DPrintf("FindFile: can't find " + filename + '\n'); + return -1; + } + + /* + =========== + FS_FOpenFile + + Finds the file in the search path. + returns filesize and an open FILE * + Used for streaming data out of either a pak file or + a seperate file. + =========== + */ + + public static int file_from_pak = 0; + + public static RandomAccessFile FOpenFile(String filename) throws IOException { + searchpath_t search; + String netpath; + pack_t pak; + int i; + filelink_t link; + File file = null; + + file_from_pak = 0; + + // check for links first + for (Iterator it = fs_links.iterator(); it.hasNext();) { + link = (filelink_t) it.next(); + + // if (!strncmp (filename, link->from, link->fromlength)) + if (filename.regionMatches(0, link.from, 0, link.fromlength)) { + netpath = link.to + filename.substring(link.fromlength); + file = new File(netpath); + if (file.canRead()) { + //Com.DPrintf ("link file: " + netpath +'\n'); + return new RandomAccessFile(file, "r"); + } + return null; + } + } + + // + // search through the path, one element at a time + // + for (search = fs_searchpaths; search != null; search = search.next) { + // is the element a pak file? + if (search.pack != null) { + // look through all the pak file elements + pak = search.pack; + filename = filename.toLowerCase(); + packfile_t entry = (packfile_t) pak.files.get(filename); + + if (entry != null) { + // found it! + file_from_pak = 1; + //Com.DPrintf ("PackFile: " + pak.filename + " : " + filename + '\n'); + file = new File(pak.filename); + if (!file.canRead()) + Com.Error(Globals.ERR_FATAL, "Couldn't reopen " + pak.filename); + if (pak.handle == null || !pak.handle.getFD().valid()) { + // hold the pakfile handle open + pak.handle = new RandomAccessFile(pak.filename, "r"); + } + // open a new file on the pakfile + + RandomAccessFile raf = new RandomAccessFile(file, "r"); + raf.seek(entry.filepos); + + return raf; + } + } + else { + // check a file in the directory tree + netpath = search.filename + '/' + filename; + + file = new File(netpath); + if (!file.canRead()) + continue; + + //Com.DPrintf("FindFile: " + netpath +'\n'); + + return new RandomAccessFile(file, "r"); + } + } + //Com.DPrintf ("FindFile: can't find " + filename + '\n'); + return null; + } + + /* + ================= + FS_ReadFile + + Properly handles partial reads + ================= + */ + public static final int MAX_READ = 0x10000; // read in blocks of 64k + + public static void Read(byte[] buffer, int len, RandomAccessFile f) { + + int block, remaining; + int offset = 0; + int read = 0; + boolean tries = true; + + // read in chunks for progress bar + remaining = len; + + while (remaining != 0) { + block = Math.min(remaining, MAX_READ); + try { + read = f.read(buffer, offset, block); + } + catch (IOException e) { + Com.Error(Globals.ERR_FATAL, e.toString()); + } + + if (read == 0) { + Com.Error(Globals.ERR_FATAL, "FS_Read: 0 bytes read"); + } + else if (read == -1) { + Com.Error(Globals.ERR_FATAL, "FS_Read: -1 bytes read"); + } + // + // do some progress bar thing here... + // + remaining -= read; + offset += read; + } + } + + /* + ============ + FS_LoadFile + + Filename are reletive to the quake search path + a null buffer will just return the file length without loading + ============ + */ + public static byte[] LoadFile(String path) { + RandomAccessFile file; + + byte[] buf = null; + int len = 0; + + // TODO bughack for bad strings (fuck \0) + int index = path.indexOf('\0'); + if (index != -1) + path = path.substring(0, index); + + // look for it in the filesystem or pack files + len = FileLength(path); + + if (len < 1) + return null; + + try { + file = FOpenFile(path); + //Read(buf = new byte[len], len, h); + buf = new byte[len]; + file.readFully(buf); + file.close(); + } + catch (IOException e) { + Com.Error(Globals.ERR_FATAL, e.toString()); + } + return buf; + } + + /* + ============= + FS_FreeFile + ============= + */ + public static void FreeFile(byte[] buffer) { + Z.Free(buffer); + } + + static final int IDPAKHEADER = (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P'); + + static class dpackheader_t { + int ident; // == IDPAKHEADER + int dirofs; + int dirlen; + } + + static final int MAX_FILES_IN_PACK = 4096; + + /* + ================= + FS_LoadPackFile + + Takes an explicit (not game tree related) path to a pak file. + + Loads the header and directory, adding the files at the beginning + of the list so they override previous pack files. + ================= + */ + static pack_t LoadPackFile(String packfile) { + + dpackheader_t header; + Hashtable newfiles; + int numpackfiles = 0; + pack_t pack = null; + RandomAccessFile file; + FileImageInputStream packhandle; + // unsigned checksum; + // + try { + packhandle = new FileImageInputStream(file = new RandomAccessFile(packfile, "r")); + packhandle.setByteOrder(ByteOrder.LITTLE_ENDIAN); + + if (packhandle.length() < 1) + return null; + // + header = new dpackheader_t(); + header.ident = packhandle.readInt(); + header.dirofs = packhandle.readInt(); + header.dirlen = packhandle.readInt(); + + if (header.ident != IDPAKHEADER) + Com.Error(Globals.ERR_FATAL, packfile + " is not a packfile"); + + numpackfiles = header.dirlen / packfile_t.SIZE; + + if (numpackfiles > MAX_FILES_IN_PACK) + Com.Error(Globals.ERR_FATAL, packfile + " has " + numpackfiles + " files"); + + newfiles = new Hashtable(numpackfiles); + + packhandle.seek(header.dirofs); + + // buffer for C-Strings char[56] + byte[] text = new byte[packfile_t.NAME_SIZE]; + // parse the directory + packfile_t entry = null; + + for (int i = 0; i < numpackfiles; i++) { + packhandle.readFully(text); + + entry = new packfile_t(); + entry.name = new String(text).trim(); + entry.filepos = packhandle.readInt(); + entry.filelen = packhandle.readInt(); + + newfiles.put(entry.name.toLowerCase(), entry); + + logger.log(Level.FINEST, i + ".\t" + entry); + } + + } + catch (IOException e) { + logger.log(Level.WARNING, e.toString()); + return null; + } + + pack = new pack_t(); + pack.filename = new String(packfile); + pack.handle = file; + pack.numfiles = numpackfiles; + pack.files = newfiles; + + Com.Printf("Added packfile " + packfile + " (" + numpackfiles + " files)\n"); + + return pack; + } + + /* + ================ + FS_AddGameDirectory + + Sets fs_gamedir, adds the directory to the head of the path, + then loads and adds pak1.pak pak2.pak ... + ================ + */ + static void AddGameDirectory(String dir) { + int i; + searchpath_t search; + pack_t pak; + String pakfile; + + fs_gamedir = new String(dir); + + // + // add the directory to the search path + // + search = new searchpath_t(); + search.filename = new String(dir); + search.next = fs_searchpaths; + fs_searchpaths = search; + + // + // add any pak files in the format pak0.pak pak1.pak, ... + // + for (i = 0; i < 10; i++) { + pakfile = dir + "/pak" + i + ".pak"; + if (!(new File(pakfile).canRead())) + continue; + + pak = LoadPackFile(pakfile); + if (pak == null) + continue; + + search = new searchpath_t(); + search.pack = pak; + search.filename = ""; + search.next = fs_searchpaths; + fs_searchpaths = search; + } + } + + /* + ============ + FS_Gamedir + + Called to find where to write a file (demos, savegames, etc) + ============ + */ + public static String Gamedir() { + return (fs_gamedir != null) ? fs_gamedir : Globals.BASEDIRNAME; + } + + /* + ============= + FS_ExecAutoexec + ============= + */ + + public static void ExecAutoexec() { + + String dir; + String name; + + dir = Cvar.VariableString("gamedir"); + + if (dir != null && dir.length() > 0) { + name = fs_basedir.string + '/' + dir + "/autoexec.cfg"; + } + else { + name = fs_basedir.string + '/' + Globals.BASEDIRNAME + "/autoexec.cfg"; + } + + int canthave = Globals.SFF_SUBDIR | Globals.SFF_HIDDEN | Globals.SFF_SYSTEM; + + if (Sys.FindAll(name, 0, canthave) != null) { + Cbuf.AddText("exec autoexec.cfg\n"); + } + } + + /* + ================ + FS_SetGamedir + + Sets the gamedir and path to a different directory. + ================ + */ + public static void SetGamedir(String dir) { + searchpath_t next; + + // if (strstr(dir, "..") || strstr(dir, "/") + // || strstr(dir, "\\") || strstr(dir, ":") ) + + if (dir.indexOf("..") != -1 || dir.indexOf("/") != -1 || dir.indexOf("\\") != -1 || dir.indexOf(":") != -1) { + Com.Printf("Gamedir should be a single filename, not a path\n"); + return; + } + + // + // free up any current game dir info + // + while (fs_searchpaths != fs_base_searchpaths) { + if (fs_searchpaths.pack != null) { + try { + fs_searchpaths.pack.handle.close(); + } + catch (IOException e) { + logger.log(Level.WARNING, e.toString()); + } + // clear the hashtable + fs_searchpaths.pack.files.clear(); + fs_searchpaths.pack.files = null; + fs_searchpaths.pack = null; + } + next = fs_searchpaths.next; + fs_searchpaths = null; + fs_searchpaths = next; + } + + // + // flush all data, so it will be forced to reload + // + if ((Globals.dedicated != null) && (Globals.dedicated.value == 0.0f)) + Cbuf.AddText("vid_restart\nsnd_restart\n"); + + fs_gamedir = fs_basedir.string + '/' + dir; + + if (!dir.equals(Globals.BASEDIRNAME) || (dir.length() == 0)) { + Cvar.FullSet("gamedir", "", CVAR_SERVERINFO | CVAR_NOSET); + Cvar.FullSet("game", "", CVAR_LATCH | CVAR_SERVERINFO); + } + else { + Cvar.FullSet("gamedir", dir, CVAR_SERVERINFO | CVAR_NOSET); + if (fs_cddir.string != null && fs_cddir.string.length() > 0) + AddGameDirectory(fs_cddir.string + '/' + dir); + + AddGameDirectory(fs_basedir.string + '/' + dir); + } + } + + /* + ================ + FS_Link_f + + Creates a filelink_t + ================ + */ + public static void Link_f() { + filelink_t entry = null; + + if (Cmd.Argc() != 3) { + Com.Printf("USAGE: link <from> <to>\n"); + return; + } + + // see if the link already exists + for (Iterator it = fs_links.iterator(); it.hasNext();) { + entry = (filelink_t) it.next(); + + if (entry.from.equals(Cmd.Argv(1))) { + if (Cmd.Argv(2).length() < 1) { + // delete it + it.remove(); + return; + } + entry.to = new String(Cmd.Argv(2)); + return; + } + } + + // create a new link if the <to> is not empty + if (Cmd.Argv(2).length() > 0) { + entry = new filelink_t(); + entry.from = new String(Cmd.Argv(1)); + entry.fromlength = entry.from.length(); + entry.to = new String(Cmd.Argv(2)); + fs_links.add(entry); + } + } + + /* + ** FS_ListFiles + */ + public static String[] ListFiles(String findname, int musthave, int canthave) { + String[] list = null; + + File[] files = Sys.FindAll(findname, musthave, canthave); + + if (files != null) { + list = new String[files.length]; + for (int i = 0; i < files.length; i++) { + list[i] = files[i].getPath(); + } + } + + return list; + } + + /* + ** FS_Dir_f + */ + public static void Dir_f() { + String path = null; + String findname = null; + String wildcard = "*.*"; + String[] dirnames; + + if (Cmd.Argc() != 1) { + wildcard = Cmd.Argv(1); + } + + while ((path = NextPath(path)) != null) { + String tmp = findname; + + findname = path + '/' + wildcard; + + if (tmp != null) + tmp.replaceAll("\\\\", "/"); + + Com.Printf("Directory of " + findname + '\n'); + Com.Printf("----\n"); + + dirnames = ListFiles(findname, 0, 0); + + if (dirnames != null) { + int index = 0; + for (int i = 0; i < dirnames.length; i++) { + if ((index = dirnames[i].lastIndexOf('/')) > 0) { + Com.Printf(dirnames[i].substring(index + 1, dirnames[i].length()) + '\n'); + } + else { + Com.Printf(dirnames[i] + '\n'); + } + } + } + + Com.Printf("\n"); + } + } + + /* + ============ + FS_Path_f + + ============ + */ + public static void Path_f() { + + searchpath_t s; + filelink_t link; + + Com.Printf("Current search path:\n"); + for (s = fs_searchpaths; s != null; s = s.next) { + if (s == fs_base_searchpaths) + Com.Printf("----------\n"); + if (s.pack != null) + Com.Printf(s.pack.filename + " (" + s.pack.numfiles + " files)\n"); + else + Com.Printf(s.filename + '\n'); + } + + Com.Printf("\nLinks:\n"); + for (Iterator it = fs_links.iterator(); it.hasNext();) { + link = (filelink_t) it.next(); + Com.Printf(link.from + " : " + link.to + '\n'); + } + } + + /* + ================ + FS_NextPath + + Allows enumerating all of the directories in the search path + ================ + */ + public static String NextPath(String prevpath) { + searchpath_t s; + String prev; + + if (prevpath == null || prevpath.length() == 0) + return fs_gamedir; + + prev = fs_gamedir; + for (s = fs_searchpaths; s != null; s = s.next) { + if (s.pack != null) + continue; + + if (prevpath == prev) + return s.filename; + + prev = s.filename; + } + + return null; + } + + /* + ================ + FS_InitFilesystem + ================ + */ + public static void InitFilesystem() { + Cmd.AddCommand("path", new xcommand_t() { + public void execute() { + Path_f(); + } + }); + Cmd.AddCommand("link", new xcommand_t() { + public void execute( ) { + Link_f(); + } + }); + Cmd.AddCommand("dir", new xcommand_t() { + public void execute() { + Dir_f(); + } + }); + + // + // basedir <path> + // allows the game to run from outside the data tree + // + fs_basedir = Cvar.Get("basedir", ".", CVAR_NOSET); + + // + // cddir <path> + // Logically concatenates the cddir after the basedir for + // allows the game to run from outside the data tree + // + + // TODO zur zeit wird auf baseq2 mit ../../ zugegriffen, sonst "" + fs_cddir = Cvar.Get("cddir", "../..", CVAR_NOSET); + if (fs_cddir.string.length() > 0) + AddGameDirectory(fs_cddir.string + '/' + Globals.BASEDIRNAME); + + // + // start up with baseq2 by default + // + AddGameDirectory(fs_basedir.string + '/' + Globals.BASEDIRNAME); + + // any set gamedirs will be freed up to here + fs_base_searchpaths = fs_searchpaths; + + // check for game override + fs_gamedirvar = Cvar.Get("game", "", CVAR_LATCH | CVAR_SERVERINFO); + + if (fs_gamedirvar.string.length() > 0) + SetGamedir(fs_gamedirvar.string); + } + + // RAFAEL + /* + Developer_searchpath + */ + public static int Developer_searchpath(int who) { + + int ch; + // PMM - warning removal + // char *start; + searchpath_t s; + + if (who == 1) // xatrix + ch = 'x'; + else if (who == 2) + ch = 'r'; + + for (s = fs_searchpaths; s != null; s = s.next) { + if (s.filename.indexOf("xatrix") != -1) + return 1; + + if (s.filename.indexOf("rogue") != -1) + return 2; + } + + return 0; + } +} diff --git a/src/jake2/qcommon/MD4.java b/src/jake2/qcommon/MD4.java new file mode 100644 index 0000000..45fdf7c --- /dev/null +++ b/src/jake2/qcommon/MD4.java @@ -0,0 +1,312 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 02.02.2004 by RST. +// $Id: MD4.java,v 1.1 2004-07-07 19:59:31 hzi Exp $ + +package jake2.qcommon; + +import java.nio.ByteBuffer; +import java.security.MessageDigest; + +import jake2.*; +import jake2.client.*; +import jake2.game.*; +import jake2.render.*; +import jake2.server.*; +import jake2.util.Lib; + + +public class MD4 extends MessageDigest implements Cloneable { + // MD4 specific object variables + //........................................................................... + + /** + * The size in bytes of the input block to the tranformation algorithm. + */ + private static final int BLOCK_LENGTH = 64; // = 512 / 8; + + /** + * 4 32-bit words (interim result) + */ + private int[] context = new int[4]; + + /** + * Number of bytes processed so far mod. 2 power of 64. + */ + private long count; + + /** + * 512 bits input buffer = 16 x 32-bit words holds until reaches 512 bits. + */ + private byte[] buffer = new byte[BLOCK_LENGTH]; + + /** + * 512 bits work buffer = 16 x 32-bit words + */ + private int[] X = new int[16]; + + // Constructors + //........................................................................... + + public MD4() { + super("MD4"); + engineReset(); + } + + /** + * This constructor is here to implement cloneability of this class. + */ + private MD4(MD4 md) { + this(); + context = (int[]) md.context.clone(); + buffer = (byte[]) md.buffer.clone(); + count = md.count; + } + + // Cloneable method implementation + //........................................................................... + + /** + * Returns a copy of this MD object. + */ + public Object clone() { + return new MD4(this); + } + + // JCE methods + //........................................................................... + + /** + * Resets this object disregarding any temporary data present at the + * time of the invocation of this call. + */ + public void engineReset() { + // initial values of MD4 i.e. A, B, C, D + // as per rfc-1320; they are low-order byte first + context[0] = 0x67452301; + context[1] = 0xEFCDAB89; + context[2] = 0x98BADCFE; + context[3] = 0x10325476; + count = 0L; + for (int i = 0; i < BLOCK_LENGTH; i++) + buffer[i] = 0; + } + + /** + * Continues an MD4 message digest using the input byte. + */ + public void engineUpdate(byte b) { + // compute number of bytes still unhashed; ie. present in buffer + int i = (int) (count % BLOCK_LENGTH); + count++; // update number of bytes + buffer[i] = b; + if (i == BLOCK_LENGTH - 1) + transform(buffer, 0); + } + + /** + * MD4 block update operation. + * <p> + * Continues an MD4 message digest operation, by filling the buffer, + * transform(ing) data in 512-bit message block(s), updating the variables + * context and count, and leaving (buffering) the remaining bytes in buffer + * for the next update or finish. + * + * @param input input block + * @param offset start of meaningful bytes in input + * @param len count of bytes in input block to consider + */ + public void engineUpdate(byte[] input, int offset, int len) { + // make sure we don't exceed input's allocated size/length + if (offset < 0 || len < 0 || (long) offset + len > input.length) + throw new ArrayIndexOutOfBoundsException(); + + // compute number of bytes still unhashed; ie. present in buffer + int bufferNdx = (int) (count % BLOCK_LENGTH); + count += len; // update number of bytes + int partLen = BLOCK_LENGTH - bufferNdx; + int i = 0; + if (len >= partLen) { + System.arraycopy(input, offset, buffer, bufferNdx, partLen); + + transform(buffer, 0); + + for (i = partLen; i + BLOCK_LENGTH - 1 < len; i += BLOCK_LENGTH) + transform(input, offset + i); + bufferNdx = 0; + } + // buffer remaining input + if (i < len) + System.arraycopy(input, offset + i, buffer, bufferNdx, len - i); + } + + /** + * Completes the hash computation by performing final operations such + * as padding. At the return of this engineDigest, the MD engine is + * reset. + * + * @return the array of bytes for the resulting hash value. + */ + public byte[] engineDigest() { + // pad output to 56 mod 64; as RFC1320 puts it: congruent to 448 mod 512 + int bufferNdx = (int) (count % BLOCK_LENGTH); + int padLen = (bufferNdx < 56) ? (56 - bufferNdx) : (120 - bufferNdx); + + // padding is alwas binary 1 followed by binary 0s + byte[] tail = new byte[padLen + 8]; + tail[0] = (byte) 0x80; + + // append length before final transform: + // save number of bits, casting the long to an array of 8 bytes + // save low-order byte first. + for (int i = 0; i < 8; i++) + tail[padLen + i] = (byte) ((count * 8) >>> (8 * i)); + + engineUpdate(tail, 0, tail.length); + + byte[] result = new byte[16]; + // cast this MD4's context (array of 4 ints) into an array of 16 bytes. + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + result[i * 4 + j] = (byte) (context[i] >>> (8 * j)); + + // reset the engine + engineReset(); + return result; + } + + // own methods + //........................................................................... + + /** + * MD4 basic transformation. + * <p> + * Transforms context based on 512 bits from input block starting + * from the offset'th byte. + * + * @param block input sub-array. + * @param offset starting position of sub-array. + */ + private void transform(byte[] block, int offset) { + + // encodes 64 bytes from input block into an array of 16 32-bit + // entities. Use A as a temp var. + for (int i = 0; i < 16; i++) + X[i] = + (block[offset++] & 0xFF) | (block[offset++] & 0xFF) + << 8 | (block[offset++] & 0xFF) + << 16 | (block[offset++] & 0xFF) + << 24; + + int A = context[0]; + int B = context[1]; + int C = context[2]; + int D = context[3]; + + A = FF(A, B, C, D, X[0], 3); + D = FF(D, A, B, C, X[1], 7); + C = FF(C, D, A, B, X[2], 11); + B = FF(B, C, D, A, X[3], 19); + A = FF(A, B, C, D, X[4], 3); + D = FF(D, A, B, C, X[5], 7); + C = FF(C, D, A, B, X[6], 11); + B = FF(B, C, D, A, X[7], 19); + A = FF(A, B, C, D, X[8], 3); + D = FF(D, A, B, C, X[9], 7); + C = FF(C, D, A, B, X[10], 11); + B = FF(B, C, D, A, X[11], 19); + A = FF(A, B, C, D, X[12], 3); + D = FF(D, A, B, C, X[13], 7); + C = FF(C, D, A, B, X[14], 11); + B = FF(B, C, D, A, X[15], 19); + + A = GG(A, B, C, D, X[0], 3); + D = GG(D, A, B, C, X[4], 5); + C = GG(C, D, A, B, X[8], 9); + B = GG(B, C, D, A, X[12], 13); + A = GG(A, B, C, D, X[1], 3); + D = GG(D, A, B, C, X[5], 5); + C = GG(C, D, A, B, X[9], 9); + B = GG(B, C, D, A, X[13], 13); + A = GG(A, B, C, D, X[2], 3); + D = GG(D, A, B, C, X[6], 5); + C = GG(C, D, A, B, X[10], 9); + B = GG(B, C, D, A, X[14], 13); + A = GG(A, B, C, D, X[3], 3); + D = GG(D, A, B, C, X[7], 5); + C = GG(C, D, A, B, X[11], 9); + B = GG(B, C, D, A, X[15], 13); + + A = HH(A, B, C, D, X[0], 3); + D = HH(D, A, B, C, X[8], 9); + C = HH(C, D, A, B, X[4], 11); + B = HH(B, C, D, A, X[12], 15); + A = HH(A, B, C, D, X[2], 3); + D = HH(D, A, B, C, X[10], 9); + C = HH(C, D, A, B, X[6], 11); + B = HH(B, C, D, A, X[14], 15); + A = HH(A, B, C, D, X[1], 3); + D = HH(D, A, B, C, X[9], 9); + C = HH(C, D, A, B, X[5], 11); + B = HH(B, C, D, A, X[13], 15); + A = HH(A, B, C, D, X[3], 3); + D = HH(D, A, B, C, X[11], 9); + C = HH(C, D, A, B, X[7], 11); + B = HH(B, C, D, A, X[15], 15); + + context[0] += A; + context[1] += B; + context[2] += C; + context[3] += D; + } + + // The basic MD4 atomic functions. + + private int FF(int a, int b, int c, int d, int x, int s) { + int t = a + ((b & c) | (~b & d)) + x; + return t << s | t >>> (32 - s); + } + + private int GG(int a, int b, int c, int d, int x, int s) { + int t = a + ((b & (c | d)) | (c & d)) + x + 0x5A827999; + return t << s | t >>> (32 - s); + } + + private int HH(int a, int b, int c, int d, int x, int s) { + int t = a + (b ^ c ^ d) + x + 0x6ED9EBA1; + return t << s | t >>> (32 - s); + } + + public static int Com_BlockChecksum(byte[] buffer, int length) { + byte digest[] = new byte[16]; + int val; + MD4 md4 = new MD4(); + + md4.engineUpdate(buffer, 0, length); + byte data[] = md4.engineDigest(); + Com.Printf("md4: " + Lib.hexDump(data, 16, false)); + + ByteBuffer bb = ByteBuffer.wrap(data); + //val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; + val = bb.getInt() ^ bb.getInt() ^ bb.getInt() ^ bb.getInt(); + return val; + } +} diff --git a/src/jake2/qcommon/MSG.java b/src/jake2/qcommon/MSG.java new file mode 100644 index 0000000..cdad707 --- /dev/null +++ b/src/jake2/qcommon/MSG.java @@ -0,0 +1,588 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 29.11.2003 by RST. +// $Id: MSG.java,v 1.1 2004-07-07 19:59:31 hzi Exp $ + +package jake2.qcommon; + +import jake2.game.*; +import jake2.util.*; + +public class MSG extends GameBase { + + // + // writing functions + // + + //ok. + public static void WriteChar(sizebuf_t sb, int c) { +// if (c < -128 || c > 127) +// Com.Error(ERR_FATAL, "WriteChar: range error"); + + sb.data[SZ.GetSpace(sb, 1)] = (byte) (c & 0xFF); + } + + //ok. + public static void WriteChar(sizebuf_t sb, float c) { + + WriteChar(sb, (int) c); + } + + //ok. + public static void WriteByte(sizebuf_t sb, int c) { + byte buf; + + //if (c < 0 || c > 255) + //Com.Error(ERR_FATAL, "WriteByte: range error"); + + sb.data[SZ.GetSpace(sb, 1)] = (byte) (c & 0xFF); + } + + //ok. + public static void WriteByte(sizebuf_t sb, float c) { + WriteByte(sb, (int) c); + } + + + public static void WriteShort(sizebuf_t sb, int c) { + + // TODO brauchen wir nicht (cwei) + // if (c < -32768 || c > 32767) + // Com.Error(ERR_FATAL, "WriteShort: range error"); + + int i = SZ.GetSpace(sb, 2); + sb.data[i++] = (byte) (c & 0xff); + sb.data[i] = (byte) ((c >>> 8) & 0xFF); + } + + //ok. + public static void WriteInt(sizebuf_t sb, int c) { + + int i = SZ.GetSpace(sb, 4); + sb.data[i++] = (byte) ((c & 0xff)); + sb.data[i++] = (byte) ((c >>> 8) & 0xff); + sb.data[i++] = (byte) ((c >>> 16) & 0xff); + sb.data[i++] = (byte) ((c >>> 24) & 0xff); + } + + //ok. + public static void WriteLong(sizebuf_t sb, int c) { + WriteInt(sb, c); + } + + //ok. + public static void WriteFloat(sizebuf_t sb, float f) { + WriteInt(sb, Float.floatToIntBits(f)); + } + + // had a bug, now its ok. + public static void WriteString(sizebuf_t sb, String s) { + String x = s; + + if (s == null) + x = ""; + + SZ.Write(sb, x.getBytes()); + WriteByte(sb, 0); + } + + //ok. + public static void WriteString(sizebuf_t sb, byte s[]) { + WriteString(sb, new String(s).trim()); + } + + public static void WriteCoord(sizebuf_t sb, float f) { + WriteShort(sb, (int) (f * 8)); + } + + public static void WritePos(sizebuf_t sb, float[] pos) { + assert(pos.length == 3) : "vec3_t bug"; + WriteShort(sb, (int) (pos[0] * 8)); + WriteShort(sb, (int) (pos[1] * 8)); + WriteShort(sb, (int) (pos[2] * 8)); + } + + public static void WriteAngle(sizebuf_t sb, float f) { + WriteByte(sb, (int) (f * 256 / 360) & 255); + } + + public static void WriteAngle16(sizebuf_t sb, float f) { + WriteShort(sb, Math3D.ANGLE2SHORT(f)); + } + + public static void WriteDeltaUsercmd(sizebuf_t buf, usercmd_t from, usercmd_t cmd) { + int bits; + + // + // send the movement message + // + bits = 0; + if (cmd.angles[0] != from.angles[0]) + bits |= CM_ANGLE1; + if (cmd.angles[1] != from.angles[1]) + bits |= CM_ANGLE2; + if (cmd.angles[2] != from.angles[2]) + bits |= CM_ANGLE3; + if (cmd.forwardmove != from.forwardmove) + bits |= CM_FORWARD; + if (cmd.sidemove != from.sidemove) + bits |= CM_SIDE; + if (cmd.upmove != from.upmove) + bits |= CM_UP; + if (cmd.buttons != from.buttons) + bits |= CM_BUTTONS; + if (cmd.impulse != from.impulse) + bits |= CM_IMPULSE; + + WriteByte(buf, bits); + + if ((bits & CM_ANGLE1) != 0) + WriteShort(buf, cmd.angles[0]); + if ((bits & CM_ANGLE2) != 0) + WriteShort(buf, cmd.angles[1]); + if ((bits & CM_ANGLE3) != 0) + WriteShort(buf, cmd.angles[2]); + + if ((bits & CM_FORWARD) != 0) + WriteShort(buf, cmd.forwardmove); + if ((bits & CM_SIDE) != 0) + WriteShort(buf, cmd.sidemove); + if ((bits & CM_UP) != 0) + WriteShort(buf, cmd.upmove); + + if ((bits & CM_BUTTONS) != 0) + WriteByte(buf, cmd.buttons); + if ((bits & CM_IMPULSE) != 0) + WriteByte(buf, cmd.impulse); + + WriteByte(buf, cmd.msec); + WriteByte(buf, cmd.lightlevel); + } + + //should be ok. + public static void WriteDir(sizebuf_t sb, float[] dir) { + int i, best; + float d, bestd; + + if (dir == null) { + WriteByte(sb, 0); + return; + } + + bestd = 0; + best = 0; + for (i = 0; i < NUMVERTEXNORMALS; i++) { + d = Math3D.DotProduct(dir, bytedirs[i]); + if (d > bestd) { + bestd = d; + best = i; + } + } + WriteByte(sb, best); + } + + //should be ok. + public static void ReadDir(sizebuf_t sb, float[] dir) { + int b; + + b = ReadByte(sb); + if (b >= NUMVERTEXNORMALS) + Com.Error(ERR_DROP, "MSF_ReadDir: out of range"); + Math3D.VectorCopy(bytedirs[b], dir); + } + + /* + ================== + WriteDeltaEntity + + Writes part of a packetentities message. + Can delta from either a baseline or a previous packet_entity + ================== + */ + public static void WriteDeltaEntity(entity_state_t from, entity_state_t to, sizebuf_t msg, boolean force, boolean newentity) { + int bits; + + if (0 == to.number) + Com.Error(ERR_FATAL, "Unset entity number"); + if (to.number >= MAX_EDICTS) + Com.Error(ERR_FATAL, "Entity number >= MAX_EDICTS"); + + // send an update + bits = 0; + + if (to.number >= 256) + bits |= U_NUMBER16; // number8 is implicit otherwise + + if (to.origin[0] != from.origin[0]) + bits |= U_ORIGIN1; + if (to.origin[1] != from.origin[1]) + bits |= U_ORIGIN2; + if (to.origin[2] != from.origin[2]) + bits |= U_ORIGIN3; + + if (to.angles[0] != from.angles[0]) + bits |= U_ANGLE1; + if (to.angles[1] != from.angles[1]) + bits |= U_ANGLE2; + if (to.angles[2] != from.angles[2]) + bits |= U_ANGLE3; + + if (to.skinnum != from.skinnum) { + if (to.skinnum < 256) + bits |= U_SKIN8; + else if (to.skinnum < 0x10000) + bits |= U_SKIN16; + else + bits |= (U_SKIN8 | U_SKIN16); + } + + if (to.frame != from.frame) { + if (to.frame < 256) + bits |= U_FRAME8; + else + bits |= U_FRAME16; + } + + if (to.effects != from.effects) { + if (to.effects < 256) + bits |= U_EFFECTS8; + else if (to.effects < 0x8000) + bits |= U_EFFECTS16; + else + bits |= U_EFFECTS8 | U_EFFECTS16; + } + + if (to.renderfx != from.renderfx) { + if (to.renderfx < 256) + bits |= U_RENDERFX8; + else if (to.renderfx < 0x8000) + bits |= U_RENDERFX16; + else + bits |= U_RENDERFX8 | U_RENDERFX16; + } + + if (to.solid != from.solid) + bits |= U_SOLID; + + // event is not delta compressed, just 0 compressed + if (to.event != 0) + bits |= U_EVENT; + + if (to.modelindex != from.modelindex) + bits |= U_MODEL; + if (to.modelindex2 != from.modelindex2) + bits |= U_MODEL2; + if (to.modelindex3 != from.modelindex3) + bits |= U_MODEL3; + if (to.modelindex4 != from.modelindex4) + bits |= U_MODEL4; + + if (to.sound != from.sound) + bits |= U_SOUND; + + if (newentity || (to.renderfx & RF_BEAM) != 0) + bits |= U_OLDORIGIN; + + // + // write the message + // + if (bits == 0 && !force) + return; // nothing to send! + + //---------- + + if ((bits & 0xff000000) != 0) + bits |= U_MOREBITS3 | U_MOREBITS2 | U_MOREBITS1; + else if ((bits & 0x00ff0000) != 0) + bits |= U_MOREBITS2 | U_MOREBITS1; + else if ((bits & 0x0000ff00) != 0) + bits |= U_MOREBITS1; + + WriteByte(msg, bits & 255); + + if ((bits & 0xff000000) != 0) { + WriteByte(msg, (bits >>> 8) & 255); + WriteByte(msg, (bits >>> 16) & 255); + WriteByte(msg, (bits >>> 24) & 255); + } + else if ((bits & 0x00ff0000) != 0) { + WriteByte(msg, (bits >>> 8) & 255); + WriteByte(msg, (bits >>> 16) & 255); + } + else if ((bits & 0x0000ff00) != 0) { + WriteByte(msg, (bits >>> 8) & 255); + } + + //---------- + + if ((bits & U_NUMBER16) != 0) + WriteShort(msg, to.number); + else + WriteByte(msg, to.number); + + if ((bits & U_MODEL) != 0) + WriteByte(msg, to.modelindex); + if ((bits & U_MODEL2) != 0) + WriteByte(msg, to.modelindex2); + if ((bits & U_MODEL3) != 0) + WriteByte(msg, to.modelindex3); + if ((bits & U_MODEL4) != 0) + WriteByte(msg, to.modelindex4); + + if ((bits & U_FRAME8) != 0) + WriteByte(msg, to.frame); + if ((bits & U_FRAME16) != 0) + WriteShort(msg, to.frame); + + if ((bits & U_SKIN8) != 0 && (bits & U_SKIN16) != 0) //used for laser colors + WriteInt(msg, to.skinnum); + else if ((bits & U_SKIN8) != 0) + WriteByte(msg, to.skinnum); + else if ((bits & U_SKIN16) != 0) + WriteShort(msg, to.skinnum); + + if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16)) + WriteInt(msg, to.effects); + else if ((bits & U_EFFECTS8) != 0) + WriteByte(msg, to.effects); + else if ((bits & U_EFFECTS16) != 0) + WriteShort(msg, to.effects); + + if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16)) + WriteInt(msg, to.renderfx); + else if ((bits & U_RENDERFX8) != 0) + WriteByte(msg, to.renderfx); + else if ((bits & U_RENDERFX16) != 0) + WriteShort(msg, to.renderfx); + + if ((bits & U_ORIGIN1) != 0) + WriteCoord(msg, to.origin[0]); + if ((bits & U_ORIGIN2) != 0) + WriteCoord(msg, to.origin[1]); + if ((bits & U_ORIGIN3) != 0) + WriteCoord(msg, to.origin[2]); + + if ((bits & U_ANGLE1) != 0) + WriteAngle(msg, to.angles[0]); + if ((bits & U_ANGLE2) != 0) + WriteAngle(msg, to.angles[1]); + if ((bits & U_ANGLE3) != 0) + WriteAngle(msg, to.angles[2]); + + if ((bits & U_OLDORIGIN) != 0) { + WriteCoord(msg, to.old_origin[0]); + WriteCoord(msg, to.old_origin[1]); + WriteCoord(msg, to.old_origin[2]); + } + + if ((bits & U_SOUND) != 0) + WriteByte(msg, to.sound); + if ((bits & U_EVENT) != 0) + WriteByte(msg, to.event); + if ((bits & U_SOLID) != 0) + WriteShort(msg, to.solid); + } + + //============================================================ + + // + // reading functions + // + + public static void BeginReading(sizebuf_t msg) { + msg.readcount = 0; + } + + // returns -1 if no more characters are available, but also [-128 , 127] + public static int ReadChar(sizebuf_t msg_read) { + int c; + + if (msg_read.readcount + 1 > msg_read.cursize) + c = -1; + else + c = msg_read.data[msg_read.readcount]; + msg_read.readcount++; + // kickangles bugfix (rst) + return c; + } + + public static int ReadByte(sizebuf_t msg_read) { + int c; + + if (msg_read.readcount + 1 > msg_read.cursize) + c = -1; + else + c = msg_read.data[msg_read.readcount] & 0xff; + msg_read.readcount++; + + return c; + } + + public static short ReadShort(sizebuf_t msg_read) { + int c; + + if (msg_read.readcount + 2 > msg_read.cursize) + c = -1; + else + c = (short) ((msg_read.data[msg_read.readcount] & 0xff) + (msg_read.data[msg_read.readcount + 1] << 8)); + + msg_read.readcount += 2; + + return (short) c; + } + + public static int ReadLong(sizebuf_t msg_read) { + int c; + + if (msg_read.readcount + 4 > msg_read.cursize) + { + Com.Printf("buffer underrun in ReadLong!"); + c = -1; + } + + else + c = + (msg_read.data[msg_read.readcount] & 0xff) + | ((msg_read.data[msg_read.readcount + 1] & 0xff) << 8) + | ((msg_read.data[msg_read.readcount + 2] & 0xff) << 16) + | ((msg_read.data[msg_read.readcount + 3] & 0xff) << 24); + + msg_read.readcount += 4; + + return c; + } + + public static float ReadFloat(sizebuf_t msg_read) { + int n = ReadLong(msg_read); + return Float.intBitsToFloat(n); + } + + // 2k read buffer. + public static byte readbuf[] = new byte[2048]; + + public static String ReadString(sizebuf_t msg_read) { + String string = ""; + + byte c; + int l = 0; + do { + c = (byte) ReadByte(msg_read); + if (c == -1 || c == 0) + break; + + readbuf[l] = c; + l++; + } + while (l < 2047); + + //readbuf[l] = 0; + return new String(readbuf, 0, l); + } + + // 2k read buffer. + public static byte readbuf1[] = new byte[2048]; + public static String ReadStringLine(sizebuf_t msg_read) { + + int l; + byte c; + + l = 0; + do { + c = (byte) ReadChar(msg_read); + if (c == -1 || c == 0 || c == 0x0a) + break; + readbuf1[l] = c; + l++; + } + while (l < 2047); + + readbuf1[l] = 0; + + return new String(readbuf1, 0, l).trim(); + } + + public static float ReadCoord(sizebuf_t msg_read) { + return ReadShort(msg_read) * (1.0f / 8); + } + + public static void ReadPos(sizebuf_t msg_read, float pos[]) { + assert(pos.length == 3) : "vec3_t bug"; + pos[0] = ReadShort(msg_read) * (1.0f / 8); + pos[1] = ReadShort(msg_read) * (1.0f / 8); + pos[2] = ReadShort(msg_read) * (1.0f / 8); + } + + public static float ReadAngle(sizebuf_t msg_read) { + return ReadChar(msg_read) * (360.0f / 256); + } + + public static float ReadAngle16(sizebuf_t msg_read) { + return Math3D.SHORT2ANGLE(ReadShort(msg_read)); + } + + public static void ReadDeltaUsercmd(sizebuf_t msg_read, usercmd_t from, usercmd_t move) { + int bits; + + //memcpy(move, from, sizeof(* move)); + // IMPORTANT!! copy without new + move.set(from); + bits = ReadByte(msg_read); + + // read current angles + if ((bits & CM_ANGLE1) != 0) + move.angles[0] = ReadShort(msg_read); + if ((bits & CM_ANGLE2) != 0) + move.angles[1] = ReadShort(msg_read); + if ((bits & CM_ANGLE3) != 0) + move.angles[2] = ReadShort(msg_read); + + // read movement + if ((bits & CM_FORWARD) != 0) + move.forwardmove = ReadShort(msg_read); + if ((bits & CM_SIDE) != 0) + move.sidemove = ReadShort(msg_read); + if ((bits & CM_UP) != 0) + move.upmove = ReadShort(msg_read); + + // read buttons + if ((bits & CM_BUTTONS) != 0) + move.buttons = (byte) ReadByte(msg_read); + + if ((bits & CM_IMPULSE) != 0) + move.impulse = (byte) ReadByte(msg_read); + + // read time to run command + move.msec = (byte) ReadByte(msg_read); + + // read the light level + move.lightlevel = (byte) ReadByte(msg_read); + + } + + public static void ReadData(sizebuf_t msg_read, byte data[], int len) { + int i; + + for (i = 0; i < len; i++) + data[i] = (byte) ReadByte(msg_read); + } + + //============================================================================ +} diff --git a/src/jake2/qcommon/Netchan.java b/src/jake2/qcommon/Netchan.java new file mode 100644 index 0000000..d949588 --- /dev/null +++ b/src/jake2/qcommon/Netchan.java @@ -0,0 +1,389 @@ +/* + * NetChannel.java + * Copyright (C) 2003 + * + * $Id: Netchan.java,v 1.1 2004-07-07 19:59:32 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +import sun.applet.resources.MsgAppletViewer; +import jake2.*; +import jake2.client.*; +import jake2.game.*; +import jake2.render.*; +import jake2.server.*; +import jake2.sys.NET; +import jake2.sys.Sys; +import jake2.util.Lib; + +/** + * Netchan + */ +public final class Netchan extends SV_MAIN { + + /* + + packet header + ------------- + 31 sequence + 1 does this message contains a reliable payload + 31 acknowledge sequence + 1 acknowledge receipt of even/odd message + 16 qport + + The remote connection never knows if it missed a reliable message, the + local side detects that it has been dropped by seeing a sequence acknowledge + higher thatn the last reliable sequence, but without the correct evon/odd + bit for the reliable set. + + If the sender notices that a reliable message has been dropped, it will be + retransmitted. It will not be retransmitted again until a message after + the retransmit has been acknowledged and the reliable still failed to get there. + + if the sequence number is -1, the packet should be handled without a netcon + + The reliable message can be added to at any time by doing + MSG_Write* (&netchan.message, <data>). + + If the message buffer is overflowed, either by a single message, or by + multiple frames worth piling up while the last reliable transmit goes + unacknowledged, the netchan signals a fatal error. + + Reliable messages are always placed first in a packet, then the unreliable + message is included if there is sufficient room. + + To the receiver, there is no distinction between the reliable and unreliable + parts of the message, they are just processed out as a single larger message. + + Illogical packet sequence numbers cause the packet to be dropped, but do + not kill the connection. This, combined with the tight window of valid + reliable acknowledgement numbers provides protection against malicious + address spoofing. + + + The qport field is a workaround for bad address translating routers that + sometimes remap the client's source port on a packet during gameplay. + + If the base part of the net address matches and the qport matches, then the + channel matches even if the IP port differs. The IP port should be updated + to the new value before sending out any replies. + + + If there is no information that needs to be transfered on a given frame, + such as during the connection stage while waiting for the client to load, + then a packet only needs to be delivered if there is something in the + unacknowledged reliable + */ + + public static cvar_t showpackets; + public static cvar_t showdrop; + public static cvar_t qport; + + //public static netadr_t net_from = new netadr_t(); + public static sizebuf_t net_message = new sizebuf_t(); + public static byte net_message_buffer[] = new byte[Defines.MAX_MSGLEN]; + + /* + =============== + Netchan_Init + + =============== + */ + //ok. + public static void Netchan_Init() { + long port; + + // pick a port value that should be nice and random + port = Sys.Milliseconds() & 0xffff; + + showpackets = Cvar.Get("showpackets", "0", 0); + showdrop = Cvar.Get("showdrop", "0", 0); + qport = Cvar.Get("qport", "" + port, Defines.CVAR_NOSET); + } + + /* + =============== + Netchan_OutOfBand + + Sends an out-of-band datagram + ================ + */ + //ok. + public static void Netchan_OutOfBand(int net_socket, netadr_t adr, int length, byte data[]) { + sizebuf_t send = new sizebuf_t(); + byte send_buf[] = new byte[Defines.MAX_MSGLEN]; + + // write the packet header + SZ.Init(send, send_buf, Defines.MAX_MSGLEN); + + MSG.WriteInt(send, -1); // -1 sequence means out of band + SZ.Write(send, data, length); + + // send the datagram + NET.SendPacket(net_socket, send.cursize, send.data, adr); + } + + public static void OutOfBandPrint(int net_socket, netadr_t adr, String s) { + Netchan_OutOfBand(net_socket, adr, s.length(), s.getBytes()); + } + + /* + ============== + Netchan_Setup + + called to open a channel to a remote system + ============== + */ + public static void Setup(int sock, netchan_t chan, netadr_t adr, int qport) { + //memset (chan, 0, sizeof(*chan)); + + chan.clear(); + chan.sock = sock; + chan.remote_address = adr.copy(); + chan.qport = qport; + chan.last_received = (int) Globals.curtime; + chan.incoming_sequence = 0; + chan.outgoing_sequence = 1; + + SZ.Init(chan.message, chan.message_buf, chan.message_buf.length); + chan.message.allowoverflow = true; + } + + /* + =============== + Netchan_CanReliable + + Returns true if the last reliable message has acked + ================ + */ + public static boolean Netchan_CanReliable(netchan_t chan) { + if (chan.reliable_length != 0) + return false; // waiting for ack + return true; + } + // das ist richtig !!! + public static boolean Netchan_NeedReliable(netchan_t chan) { + boolean send_reliable; + + // if the remote side dropped the last reliable message, resend it + send_reliable = false; + + if (chan.incoming_acknowledged > chan.last_reliable_sequence && chan.incoming_reliable_acknowledged != chan.reliable_sequence) + send_reliable = true; + + // if the reliable transmit buffer is empty, copy the current message out + if (0 == chan.reliable_length && chan.message.cursize != 0) { + send_reliable = true; + } + + return send_reliable; + } + + /* + =============== + Netchan_Transmit + + tries to send an unreliable message to a connection, and handles the + transmition / retransmition of the reliable messages. + + A 0 length will still generate a packet and deal with the reliable messages. + ================ + */ + public static void Transmit(netchan_t chan, int length, byte data[]) { + sizebuf_t send = new sizebuf_t(); + byte send_buf[] = new byte[MAX_MSGLEN]; + int send_reliable; + int w1, w2; + + // check for message overflow + if (chan.message.overflowed) { + chan.fatal_error = true; + Com.Printf(NET.AdrToString(chan.remote_address) + ":Outgoing message overflow\n"); + return; + } + + send_reliable = Netchan_NeedReliable(chan) ? 1 : 0; + + if (chan.reliable_length == 0 && chan.message.cursize != 0) { + Lib.memcpy(chan.reliable_buf, chan.message_buf, chan.message.cursize); + chan.reliable_length = chan.message.cursize; + chan.message.cursize = 0; + chan.reliable_sequence ^= 1; + } + + // write the packet header + SZ.Init(send, send_buf, send_buf.length); + + w1 = (chan.outgoing_sequence & ~(1 << 31)) | (send_reliable << 31); + w2 = (chan.incoming_sequence & ~(1 << 31)) | (chan.incoming_reliable_sequence << 31); + + chan.outgoing_sequence++; + chan.last_sent = (int) Globals.curtime; + + MSG.WriteInt(send, w1); + MSG.WriteInt(send, w2); + + // send the qport if we are a client + if (chan.sock == Defines.NS_CLIENT) + MSG.WriteShort(send, (int) qport.value); + + // copy the reliable message to the packet first + if (send_reliable != 0) { + SZ.Write(send, chan.reliable_buf, chan.reliable_length); + chan.last_reliable_sequence = chan.outgoing_sequence; + } + + // add the unreliable part if space is available + if (send.maxsize - send.cursize >= length) + SZ.Write(send, data, length); + else + Com.Printf("Netchan_Transmit: dumped unreliable\n"); + + // send the datagram + NET.SendPacket(chan.sock, send.cursize, send.data, chan.remote_address); + + if (showpackets.value != 0) { + if (send_reliable != 0) + Com.Printf(//"send %4i : s=%i reliable=%i ack=%i rack=%i\n" + "send " + + send.cursize + + " : s=" + + (chan.outgoing_sequence - 1) + + " reliable=" + + chan.reliable_sequence + + " ack=" + + chan.incoming_sequence + + " rack=" + + chan.incoming_reliable_sequence + + "\n"); + else + Com.Printf(//"send %4i : s=%i ack=%i rack=%i\n" + "send " + + send.cursize + + " : s=" + + (chan.outgoing_sequence - 1) + + " ack=" + + chan.incoming_sequence + + " rack=" + + chan.incoming_reliable_sequence + + "\n"); + } + } + + /* + ================= + Netchan_Process + + called when the current net_message is from remote_address + modifies net_message so that it points to the packet payload + ================= + */ + public static boolean Process(netchan_t chan, sizebuf_t msg) { + int sequence, sequence_ack; + int reliable_ack, reliable_message; + int qport; + + // get sequence numbers + MSG.BeginReading(msg); + sequence = MSG.ReadLong(msg); + sequence_ack = MSG.ReadLong(msg); + + // read the qport if we are a server + if (chan.sock == NS_SERVER) + qport = MSG.ReadShort(msg); + + // achtung unsigned int + reliable_message = sequence >>> 31; + reliable_ack = sequence_ack >>> 31; + + sequence &= ~(1 << 31); + sequence_ack &= ~(1 << 31); + + if (showpackets.value != 0) { + if (reliable_message != 0) + Com.Printf(//"recv %4i : s=%i reliable=%i ack=%i rack=%i\n" + "recv " + + msg.cursize + + " : s=" + + sequence + + " reliable=" + + (chan.incoming_reliable_sequence ^ 1) + + " ack=" + + sequence_ack + + " rack=" + + reliable_ack + + "\n"); + else + Com.Printf(//"recv %4i : s=%i ack=%i rack=%i\n" + "recv " + msg.cursize + " : s=" + sequence + " ack=" + sequence_ack + " rack=" + reliable_ack + "\n"); + } + + // + // discard stale or duplicated packets + // + if (sequence <= chan.incoming_sequence) { + if (showdrop.value != 0) + Com.Printf( + NET.AdrToString(chan.remote_address) + + ":Out of order packet " + + sequence + + " at " + + chan.incoming_sequence + + "\n"); + return false; + } + + // + // dropped packets don't keep the message from being used + // + chan.dropped = sequence - (chan.incoming_sequence + 1); + if (chan.dropped > 0) { + if (showdrop.value != 0) + Com.Printf(NET.AdrToString(chan.remote_address) + ":Dropped " + chan.dropped + " packets at " + sequence + "\n"); + } + + // + // if the current outgoing reliable message has been acknowledged + // clear the buffer to make way for the next + // + if (reliable_ack == chan.reliable_sequence) + chan.reliable_length = 0; // it has been received + + // + // if this message contains a reliable message, bump incoming_reliable_sequence + // + chan.incoming_sequence = sequence; + chan.incoming_acknowledged = sequence_ack; + chan.incoming_reliable_acknowledged = reliable_ack; + if (reliable_message != 0) { + chan.incoming_reliable_sequence ^= 1; + } + + // + // the message can now be read from the current message pointer + // + chan.last_received = (int) Globals.curtime; + + return true; + } + +} diff --git a/src/jake2/qcommon/PMove.java b/src/jake2/qcommon/PMove.java new file mode 100644 index 0000000..ed82432 --- /dev/null +++ b/src/jake2/qcommon/PMove.java @@ -0,0 +1,1261 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 25.01.2004 by RST. +// $Id: PMove.java,v 1.1 2004-07-07 19:59:33 hzi Exp $ + +package jake2.qcommon; + +import jake2.*; +import jake2.client.*; +import jake2.game.*; +import jake2.qcommon.*; +import jake2.render.*; +import jake2.server.*; + +public class PMove extends Game +{ + + public final static int STEPSIZE = 18; + + // all of the locals will be zeroed before each + // pmove, just to make damn sure we don't have + // any differences when running on client or server + + public static class pml_t + { + float[] origin = { 0, 0, 0 }; // full float precision + float[] velocity = { 0, 0, 0 }; // full float precision + + float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 }; + float frametime; + + csurface_t groundsurface; + cplane_t groundplane = new cplane_t(); + int groundcontents; + + float[] previous_origin = { 0, 0, 0 }; + boolean ladder; + } + + public static pmove_t pm; + public static pml_t pml = new pml_t(); + + // movement parameters + public static float pm_stopspeed = 100; + public static float pm_maxspeed = 300; + public static float pm_duckspeed = 100; + public static float pm_accelerate = 10; + public static float pm_airaccelerate = 0; + public static float pm_wateraccelerate = 10; + public static float pm_friction = 6; + public static float pm_waterfriction = 1; + public static float pm_waterspeed = 400; + + /* + + walking up a step should kill some velocity + + */ + + /* + ================== + PM_ClipVelocity + + Slide off of the impacting object + returns the blocked flags (1 = floor, 2 = step / wall) + ================== + */ + public static final float STOP_EPSILON = 0.1f; + + public static void PM_ClipVelocity(float[] in, float[] normal, float[] out, float overbounce) + { + float backoff; + float change; + int i; + + backoff = DotProduct(in, normal) * overbounce; + + for (i = 0; i < 3; i++) + { + change = normal[i] * backoff; + out[i] = in[i] - change; + if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) + out[i] = 0; + } + } + + /* + ================== + PM_StepSlideMove + + Each intersection will try to step over the obstruction instead of + sliding along it. + + Returns a new origin, velocity, and contact entity + Does not modify any world state? + ================== + */ + public final static float MIN_STEP_NORMAL = 0.7f; // can't step up onto very steep slopes + + public static void PM_StepSlideMove_() + { + int bumpcount, numbumps; + float[] dir = { 0, 0, 0 }; + float d; + int numplanes; + float[] planes[] = new float[MAX_CLIP_PLANES][3]; + float[] primal_velocity = { 0, 0, 0 }; + int i, j; + trace_t trace; + float[] end = { 0, 0, 0 }; + float time_left; + + numbumps = 4; + + VectorCopy(pml.velocity, primal_velocity); + numplanes = 0; + + time_left = pml.frametime; + + for (bumpcount = 0; bumpcount < numbumps; bumpcount++) + { + for (i = 0; i < 3; i++) + end[i] = pml.origin[i] + time_left * pml.velocity[i]; + + trace = pm.trace.trace(pml.origin, pm.mins, pm.maxs, end); + + if (trace.allsolid) + { // entity is trapped in another solid + pml.velocity[2] = 0; // don't build up falling damage + return; + } + + if (trace.fraction > 0) + { // actually covered some distance + VectorCopy(trace.endpos, pml.origin); + numplanes = 0; + } + + if (trace.fraction == 1) + break; // moved the entire distance + + // save entity for contact + if (pm.numtouch < MAXTOUCH && trace.ent != null) + { + if (trace.ent.index != -1) + Com.p("touch: " + trace.ent.classname + " (" + trace.ent.index + ")" ); + + pm.touchents[pm.numtouch] = trace.ent; + pm.numtouch++; + } + + time_left -= time_left * trace.fraction; + + // slide along this plane + if (numplanes >= MAX_CLIP_PLANES) + { // this shouldn't really happen + VectorCopy(vec3_origin, pml.velocity); + break; + } + + VectorCopy(trace.plane.normal, planes[numplanes]); + numplanes++; + + // + // modify original_velocity so it parallels all of the clip planes + // + + for (i = 0; i < numplanes; i++) + { + PM_ClipVelocity(pml.velocity, planes[i], pml.velocity, 1.01f); + for (j = 0; j < numplanes; j++) + if (j != i) + { + if (DotProduct(pml.velocity, planes[j]) < 0) + break; // not ok + } + if (j == numplanes) + break; + } + + if (i != numplanes) + { // go along this plane + } + else + { // go along the crease + if (numplanes != 2) + { + // Con_Printf ("clip velocity, numplanes == %i\n",numplanes); + VectorCopy(vec3_origin, pml.velocity); + break; + } + CrossProduct(planes[0], planes[1], dir); + d = DotProduct(dir, pml.velocity); + VectorScale(dir, d, pml.velocity); + } + + // + // if velocity is against the original velocity, stop dead + // to avoid tiny occilations in sloping corners + // + if (DotProduct(pml.velocity, primal_velocity) <= 0) + { + VectorCopy(vec3_origin, pml.velocity); + break; + } + } + + if (pm.s.pm_time != 0) + { + VectorCopy(primal_velocity, pml.velocity); + } + } + + /* + ================== + PM_StepSlideMove + + ================== + */ + public static void PM_StepSlideMove() + { + float[] start_o = { 0, 0, 0 }, start_v = { 0, 0, 0 }; + float[] down_o = { 0, 0, 0 }, down_v = { 0, 0, 0 }; + trace_t trace; + float down_dist, up_dist; + // float [] delta; + float[] up = { 0, 0, 0 }, down = { 0, 0, 0 }; + + VectorCopy(pml.origin, start_o); + VectorCopy(pml.velocity, start_v); + + PM_StepSlideMove_(); + + VectorCopy(pml.origin, down_o); + VectorCopy(pml.velocity, down_v); + + VectorCopy(start_o, up); + up[2] += STEPSIZE; + + trace = pm.trace.trace(up, pm.mins, pm.maxs, up); + if (trace.allsolid) + return; // can't step up + + // try sliding above + VectorCopy(up, pml.origin); + VectorCopy(start_v, pml.velocity); + + PM_StepSlideMove_(); + + // push down the final amount + VectorCopy(pml.origin, down); + down[2] -= STEPSIZE; + trace = pm.trace.trace(pml.origin, pm.mins, pm.maxs, down); + if (!trace.allsolid) + { + VectorCopy(trace.endpos, pml.origin); + } + + VectorCopy(pml.origin, up); + + // decide which one went farther + down_dist = (down_o[0] - start_o[0]) * (down_o[0] - start_o[0]) + (down_o[1] - start_o[1]) * (down_o[1] - start_o[1]); + up_dist = (up[0] - start_o[0]) * (up[0] - start_o[0]) + (up[1] - start_o[1]) * (up[1] - start_o[1]); + + if (down_dist > up_dist || trace.plane.normal[2] < MIN_STEP_NORMAL) + { + VectorCopy(down_o, pml.origin); + VectorCopy(down_v, pml.velocity); + return; + } + //!! Special case + // if we were walking along a plane, then we need to copy the Z over + pml.velocity[2] = down_v[2]; + } + + /* + ================== + PM_Friction + + Handles both ground friction and water friction + ================== + */ + public static void PM_Friction() + { + float vel[]; + float speed, newspeed, control; + float friction; + float drop; + + vel = pml.velocity; + + speed = (float) (Math.sqrt(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2])); + if (speed < 1) + { + vel[0] = 0; + vel[1] = 0; + return; + } + + drop = 0; + + // apply ground friction + if ((pm.groundentity != null && pml.groundsurface != null && 0 == (pml.groundsurface.flags & SURF_SLICK)) || (pml.ladder)) + { + friction = pm_friction; + control = speed < pm_stopspeed ? pm_stopspeed : speed; + drop += control * friction * pml.frametime; + } + + // apply water friction + if (pm.waterlevel != 0 && !pml.ladder) + drop += speed * pm_waterfriction * pm.waterlevel * pml.frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + { + newspeed = 0; + } + newspeed /= speed; + + vel[0] = vel[0] * newspeed; + vel[1] = vel[1] * newspeed; + vel[2] = vel[2] * newspeed; + } + + /* + ============== + PM_Accelerate + + Handles user intended acceleration + ============== + */ + public static void PM_Accelerate(float[] wishdir, float wishspeed, float accel) + { + int i; + float addspeed, accelspeed, currentspeed; + + currentspeed = DotProduct(pml.velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + return; + accelspeed = accel * pml.frametime * wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i = 0; i < 3; i++) + pml.velocity[i] += accelspeed * wishdir[i]; + } + + public static void PM_AirAccelerate(float[] wishdir, float wishspeed, float accel) + { + int i; + float addspeed, accelspeed, currentspeed, wishspd = wishspeed; + + if (wishspd > 30) + wishspd = 30; + currentspeed = DotProduct(pml.velocity, wishdir); + addspeed = wishspd - currentspeed; + if (addspeed <= 0) + return; + accelspeed = accel * wishspeed * pml.frametime; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i = 0; i < 3; i++) + pml.velocity[i] += accelspeed * wishdir[i]; + } + + /* + ============= + PM_AddCurrents + ============= + */ + public static void PM_AddCurrents(float[] wishvel) + { + float[] v = { 0, 0, 0 }; + float s; + + // + // account for ladders + // + + if (pml.ladder && Math.abs(pml.velocity[2]) <= 200) + { + if ((pm.viewangles[PITCH] <= -15) && (pm.cmd.forwardmove > 0)) + wishvel[2] = 200; + else if ((pm.viewangles[PITCH] >= 15) && (pm.cmd.forwardmove > 0)) + wishvel[2] = -200; + else if (pm.cmd.upmove > 0) + wishvel[2] = 200; + else if (pm.cmd.upmove < 0) + wishvel[2] = -200; + else + wishvel[2] = 0; + + // limit horizontal speed when on a ladder + if (wishvel[0] < -25) + wishvel[0] = -25; + else if (wishvel[0] > 25) + wishvel[0] = 25; + + if (wishvel[1] < -25) + wishvel[1] = -25; + else if (wishvel[1] > 25) + wishvel[1] = 25; + } + + // + // add water currents + // + + if ((pm.watertype & MASK_CURRENT) != 0) + { + VectorClear(v); + + if ((pm.watertype & CONTENTS_CURRENT_0) != 0) + v[0] += 1; + if ((pm.watertype & CONTENTS_CURRENT_90) != 0) + v[1] += 1; + if ((pm.watertype & CONTENTS_CURRENT_180) != 0) + v[0] -= 1; + if ((pm.watertype & CONTENTS_CURRENT_270) != 0) + v[1] -= 1; + if ((pm.watertype & CONTENTS_CURRENT_UP) != 0) + v[2] += 1; + if ((pm.watertype & CONTENTS_CURRENT_DOWN) != 0) + v[2] -= 1; + + s = pm_waterspeed; + if ((pm.waterlevel == 1) && (pm.groundentity != null)) + s /= 2; + + VectorMA(wishvel, s, v, wishvel); + } + + // + // add conveyor belt velocities + // + + if (pm.groundentity != null) + { + VectorClear(v); + + if ((pml.groundcontents & CONTENTS_CURRENT_0) != 0) + v[0] += 1; + if ((pml.groundcontents & CONTENTS_CURRENT_90) != 0) + v[1] += 1; + if ((pml.groundcontents & CONTENTS_CURRENT_180) != 0) + v[0] -= 1; + if ((pml.groundcontents & CONTENTS_CURRENT_270) != 0) + v[1] -= 1; + if ((pml.groundcontents & CONTENTS_CURRENT_UP) != 0) + v[2] += 1; + if ((pml.groundcontents & CONTENTS_CURRENT_DOWN) != 0) + v[2] -= 1; + + VectorMA(wishvel, 100 /* pm.groundentity.speed */ + , v, wishvel); + } + } + + /* + =================== + PM_WaterMove + + =================== + */ + public static void PM_WaterMove() + { + int i; + float[] wishvel = { 0, 0, 0 }; + float wishspeed; + float[] wishdir = { 0, 0, 0 }; + + // + // user intentions + // + for (i = 0; i < 3; i++) + wishvel[i] = pml.forward[i] * pm.cmd.forwardmove + pml.right[i] * pm.cmd.sidemove; + + if (0 == pm.cmd.forwardmove && 0 == pm.cmd.sidemove && 0 == pm.cmd.upmove) + wishvel[2] -= 60; // drift towards bottom + else + wishvel[2] += pm.cmd.upmove; + + PM_AddCurrents(wishvel); + + VectorCopy(wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + if (wishspeed > pm_maxspeed) + { + VectorScale(wishvel, pm_maxspeed / wishspeed, wishvel); + wishspeed = pm_maxspeed; + } + wishspeed *= 0.5; + + PM_Accelerate(wishdir, wishspeed, pm_wateraccelerate); + + PM_StepSlideMove(); + } + + /* + =================== + PM_AirMove + + =================== + */ + public static void PM_AirMove() + { + int i; + float[] wishvel = { 0, 0, 0 }; + float fmove, smove; + float[] wishdir = { 0, 0, 0 }; + float wishspeed; + float maxspeed; + + fmove = pm.cmd.forwardmove; + smove = pm.cmd.sidemove; + + for (i = 0; i < 2; i++) + wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; + wishvel[2] = 0; + + PM_AddCurrents(wishvel); + + VectorCopy(wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // + // clamp to server defined max speed + // + maxspeed = (pm.s.pm_flags & PMF_DUCKED) != 0 ? pm_duckspeed : pm_maxspeed; + + if (wishspeed > maxspeed) + { + VectorScale(wishvel, maxspeed / wishspeed, wishvel); + wishspeed = maxspeed; + } + + if (pml.ladder) + { + PM_Accelerate(wishdir, wishspeed, pm_accelerate); + if (0 == wishvel[2]) + { + if (pml.velocity[2] > 0) + { + pml.velocity[2] -= pm.s.gravity * pml.frametime; + if (pml.velocity[2] < 0) + pml.velocity[2] = 0; + } + else + { + pml.velocity[2] += pm.s.gravity * pml.frametime; + if (pml.velocity[2] > 0) + pml.velocity[2] = 0; + } + } + PM_StepSlideMove(); + } + else if (pm.groundentity != null) + { // walking on ground + pml.velocity[2] = 0; //!!! this is before the accel + PM_Accelerate(wishdir, wishspeed, pm_accelerate); + + // PGM -- fix for negative trigger_gravity fields + // pml.velocity[2] = 0; + if (pm.s.gravity > 0) + pml.velocity[2] = 0; + else + pml.velocity[2] -= pm.s.gravity * pml.frametime; + // PGM + + if (0 == pml.velocity[0] && 0 == pml.velocity[1]) + return; + PM_StepSlideMove(); + } + else + { // not on ground, so little effect on velocity + if (pm_airaccelerate != 0) + PM_AirAccelerate(wishdir, wishspeed, pm_accelerate); + else + PM_Accelerate(wishdir, wishspeed, 1); + // add gravity + pml.velocity[2] -= pm.s.gravity * pml.frametime; + PM_StepSlideMove(); + } + } + + /* + ============= + PM_CatagorizePosition + ============= + */ + public static void PM_CatagorizePosition() + { + float[] point = { 0, 0, 0 }; + int cont; + trace_t trace; + int sample1; + int sample2; + + // if the player hull point one unit down is solid, the player + // is on ground + + // see if standing on something solid + point[0] = pml.origin[0]; + point[1] = pml.origin[1]; + point[2] = pml.origin[2] - 0.25f; + if (pml.velocity[2] > 180) //!!ZOID changed from 100 to 180 (ramp accel) + { + pm.s.pm_flags &= ~PMF_ON_GROUND; + pm.groundentity = null; + } + else + { + trace = pm.trace.trace(pml.origin, pm.mins, pm.maxs, point); + pml.groundplane = trace.plane; + pml.groundsurface = trace.surface; + pml.groundcontents = trace.contents; + + if (null == trace.ent || (trace.plane.normal[2] < 0.7 && !trace.startsolid)) + { + pm.groundentity = null; + pm.s.pm_flags &= ~PMF_ON_GROUND; + } + else + { + pm.groundentity = trace.ent; + + // hitting solid ground will end a waterjump + if ((pm.s.pm_flags & PMF_TIME_WATERJUMP) != 0) + { + pm.s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); + pm.s.pm_time = 0; + } + + if (0 == (pm.s.pm_flags & PMF_ON_GROUND)) + { // just hit the ground + pm.s.pm_flags |= PMF_ON_GROUND; + // don't do landing time if we were just going down a slope + if (pml.velocity[2] < -200) + { + pm.s.pm_flags |= PMF_TIME_LAND; + // don't allow another jump for a little while + if (pml.velocity[2] < -400) + pm.s.pm_time = 25; + else + pm.s.pm_time = 18; + } + } + } + + if (pm.numtouch < MAXTOUCH && trace.ent != null) + { + pm.touchents[pm.numtouch] = trace.ent; + pm.numtouch++; + } + } + + // + // get waterlevel, accounting for ducking + // + pm.waterlevel = 0; + pm.watertype = 0; + + sample2 = (int) (pm.viewheight - pm.mins[2]); + sample1 = sample2 / 2; + + point[2] = pml.origin[2] + pm.mins[2] + 1; + cont = pm.pointcontents.pointcontents(point); + + if ((cont & MASK_WATER) != 0) + { + pm.watertype = cont; + pm.waterlevel = 1; + point[2] = pml.origin[2] + pm.mins[2] + sample1; + cont = pm.pointcontents.pointcontents(point); + if ((cont & MASK_WATER) != 0) + { + pm.waterlevel = 2; + point[2] = pml.origin[2] + pm.mins[2] + sample2; + cont = pm.pointcontents.pointcontents(point); + if ((cont & MASK_WATER) != 0) + pm.waterlevel = 3; + } + } + + } + + /* + ============= + PM_CheckJump + ============= + */ + public static void PM_CheckJump() + { + if ((pm.s.pm_flags & PMF_TIME_LAND) != 0) + { // hasn't been long enough since landing to jump again + return; + } + + if (pm.cmd.upmove < 10) + { // not holding jump + pm.s.pm_flags &= ~PMF_JUMP_HELD; + return; + } + + // must wait for jump to be released + if ((pm.s.pm_flags & PMF_JUMP_HELD) != 0) + return; + + if (pm.s.pm_type == PM_DEAD) + return; + + if (pm.waterlevel >= 2) + { // swimming, not jumping + pm.groundentity = null; + + if (pml.velocity[2] <= -300) + return; + + if (pm.watertype == CONTENTS_WATER) + pml.velocity[2] = 100; + else if (pm.watertype == CONTENTS_SLIME) + pml.velocity[2] = 80; + else + pml.velocity[2] = 50; + return; + } + + if (pm.groundentity == null) + return; // in air, so no effect + + pm.s.pm_flags |= PMF_JUMP_HELD; + + pm.groundentity = null; + pml.velocity[2] += 270; + if (pml.velocity[2] < 270) + pml.velocity[2] = 270; + } + + /* + ============= + PM_CheckSpecialMovement + ============= + */ + public static void PM_CheckSpecialMovement() + { + float[] spot = { 0, 0, 0 }; + int cont; + float[] flatforward = { 0, 0, 0 }; + trace_t trace; + + if (pm.s.pm_time != 0) + return; + + pml.ladder = false; + + // check for ladder + flatforward[0] = pml.forward[0]; + flatforward[1] = pml.forward[1]; + flatforward[2] = 0; + VectorNormalize(flatforward); + + VectorMA(pml.origin, 1, flatforward, spot); + trace = pm.trace.trace(pml.origin, pm.mins, pm.maxs, spot); + if ((trace.fraction < 1) && (trace.contents & CONTENTS_LADDER) != 0) + pml.ladder = true; + + // check for water jump + if (pm.waterlevel != 2) + return; + + VectorMA(pml.origin, 30, flatforward, spot); + spot[2] += 4; + cont = pm.pointcontents.pointcontents(spot); + if (0 == (cont & CONTENTS_SOLID)) + return; + + spot[2] += 16; + cont = pm.pointcontents.pointcontents(spot); + if (cont != 0) + return; + // jump out of water + VectorScale(flatforward, 50, pml.velocity); + pml.velocity[2] = 350; + + pm.s.pm_flags |= PMF_TIME_WATERJUMP; + pm.s.pm_time = -1; // was 255 + } + + /* + =============== + PM_FlyMove + =============== + */ + public static void PM_FlyMove(boolean doclip) + { + float speed, drop, friction, control, newspeed; + float currentspeed, addspeed, accelspeed; + int i; + float[] wishvel = { 0, 0, 0 }; + float fmove, smove; + float[] wishdir = { 0, 0, 0 }; + float wishspeed; + float[] end = { 0, 0, 0 }; + trace_t trace; + + pm.viewheight = 22; + + // friction + + speed = VectorLength(pml.velocity); + if (speed < 1) + { + VectorCopy(vec3_origin, pml.velocity); + } + else + { + drop = 0; + + friction = pm_friction * 1.5f; // extra friction + control = speed < pm_stopspeed ? pm_stopspeed : speed; + drop += control * friction * pml.frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + VectorScale(pml.velocity, newspeed, pml.velocity); + } + + // accelerate + fmove = pm.cmd.forwardmove; + smove = pm.cmd.sidemove; + + VectorNormalize(pml.forward); + VectorNormalize(pml.right); + + for (i = 0; i < 3; i++) + wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; + wishvel[2] += pm.cmd.upmove; + + VectorCopy(wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // + // clamp to server defined max speed + // + if (wishspeed > pm_maxspeed) + { + VectorScale(wishvel, pm_maxspeed / wishspeed, wishvel); + wishspeed = pm_maxspeed; + } + + currentspeed = DotProduct(pml.velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + return; + accelspeed = pm_accelerate * pml.frametime * wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i = 0; i < 3; i++) + pml.velocity[i] += accelspeed * wishdir[i]; + + if (doclip) + { + for (i = 0; i < 3; i++) + end[i] = pml.origin[i] + pml.frametime * pml.velocity[i]; + + trace = pm.trace.trace(pml.origin, pm.mins, pm.maxs, end); + + VectorCopy(trace.endpos, pml.origin); + } + else + { + // move + VectorMA(pml.origin, pml.frametime, pml.velocity, pml.origin); + } + } + + /* + ============== + PM_CheckDuck + + Sets mins, maxs, and pm.viewheight + ============== + */ + public static void PM_CheckDuck() + { + trace_t trace; + + pm.mins[0] = -16; + pm.mins[1] = -16; + + pm.maxs[0] = 16; + pm.maxs[1] = 16; + + if (pm.s.pm_type == PM_GIB) + { + pm.mins[2] = 0; + pm.maxs[2] = 16; + pm.viewheight = 8; + return; + } + + pm.mins[2] = -24; + + if (pm.s.pm_type == PM_DEAD) + { + pm.s.pm_flags |= PMF_DUCKED; + } + else if (pm.cmd.upmove < 0 && (pm.s.pm_flags & PMF_ON_GROUND) != 0) + { // duck + pm.s.pm_flags |= PMF_DUCKED; + } + else + { // stand up if possible + if ((pm.s.pm_flags & PMF_DUCKED) != 0) + { + // try to stand up + pm.maxs[2] = 32; + trace = pm.trace.trace(pml.origin, pm.mins, pm.maxs, pml.origin); + if (!trace.allsolid) + pm.s.pm_flags &= ~PMF_DUCKED; + } + } + + if ((pm.s.pm_flags & PMF_DUCKED) != 0) + { + pm.maxs[2] = 4; + pm.viewheight = -2; + } + else + { + pm.maxs[2] = 32; + pm.viewheight = 22; + } + } + + /* + ============== + PM_DeadMove + ============== + */ + public static void PM_DeadMove() + { + float forward; + + if (null == pm.groundentity) + return; + + // extra friction + + forward = VectorLength(pml.velocity); + forward -= 20; + if (forward <= 0) + { + VectorClear(pml.velocity); + } + else + { + VectorNormalize(pml.velocity); + VectorScale(pml.velocity, forward, pml.velocity); + } + } + + public static boolean PM_GoodPosition() + { + trace_t trace; + float[] origin = { 0, 0, 0 }, end = { 0, 0, 0 }; + int i; + + if (pm.s.pm_type == PM_SPECTATOR) + return true; + + for (i = 0; i < 3; i++) + origin[i] = end[i] = pm.s.origin[i] * 0.125f; + trace = pm.trace.trace(origin, pm.mins, pm.maxs, end); + + return !trace.allsolid; + } + + /* + ================ + PM_SnapPosition + + On exit, the origin will have a value that is pre-quantized to the 0.125 + precision of the network channel and in a valid position. + ================ + */ + // try all single bits first + static int jitterbits[] = { 0, 4, 1, 2, 3, 5, 6, 7 }; + public static void PM_SnapPosition() + { + int sign[] = { 0, 0, 0 }; + int i, j, bits; + short base[] = { 0, 0, 0 }; + + // snap velocity to eigths + for (i = 0; i < 3; i++) + pm.s.velocity[i] = (short) (pml.velocity[i] * 8); + + for (i = 0; i < 3; i++) + { + if (pml.origin[i] >= 0) + sign[i] = 1; + else + sign[i] = -1; + pm.s.origin[i] = (short) (pml.origin[i] * 8); + if (pm.s.origin[i] * 0.125 == pml.origin[i]) + sign[i] = 0; + } + VectorCopy(pm.s.origin, base); + + // try all combinations + for (j = 0; j < 8; j++) + { + bits = jitterbits[j]; + VectorCopy(base, pm.s.origin); + for (i = 0; i < 3; i++) + if ((bits & (1 << i)) != 0) + pm.s.origin[i] += sign[i]; + + if (PM_GoodPosition()) + return; + } + + // go back to the last position + VectorCopy(pml.previous_origin, pm.s.origin); + // Com_DPrintf ("using previous_origin\n"); + } + + /* + ================ + PM_InitialSnapPosition + + ================ + */ + static int offset[] = { 0, -1, 1 }; + public static void PM_InitialSnapPosition() + { + int x, y, z; + short base[] = { 0, 0, 0 }; + + VectorCopy(pm.s.origin, base); + + for (z = 0; z < 3; z++) + { + pm.s.origin[2] = (short) (base[2] + offset[z]); + for (y = 0; y < 3; y++) + { + pm.s.origin[1] = (short) (base[1] + offset[y]); + for (x = 0; x < 3; x++) + { + pm.s.origin[0] = (short) (base[0] + offset[x]); + if (PM_GoodPosition()) + { + pml.origin[0] = pm.s.origin[0] * 0.125f; + pml.origin[1] = pm.s.origin[1] * 0.125f; + pml.origin[2] = pm.s.origin[2] * 0.125f; + VectorCopy(pm.s.origin, pml.previous_origin); + return; + } + } + } + } + + Com.DPrintf("Bad InitialSnapPosition\n"); + } + + /* + ================ + PM_ClampAngles + + ================ + */ + public static void PM_ClampAngles() + { + short temp; + int i; + + if ((pm.s.pm_flags & PMF_TIME_TELEPORT) != 0) + { + pm.viewangles[YAW] = SHORT2ANGLE(pm.cmd.angles[YAW] + pm.s.delta_angles[YAW]); + pm.viewangles[PITCH] = 0; + pm.viewangles[ROLL] = 0; + } + else + { + // circularly clamp the angles with deltas + for (i = 0; i < 3; i++) + { + temp = (short) (pm.cmd.angles[i] + pm.s.delta_angles[i]); + pm.viewangles[i] = SHORT2ANGLE(temp); + } + + // don't let the player look up or down more than 90 degrees + if (pm.viewangles[PITCH] > 89 && pm.viewangles[PITCH] < 180) + pm.viewangles[PITCH] = 89; + else if (pm.viewangles[PITCH] < 271 && pm.viewangles[PITCH] >= 180) + pm.viewangles[PITCH] = 271; + } + AngleVectors(pm.viewangles, pml.forward, pml.right, pml.up); + } + + /* + ================ + Pmove + + Can be called by either the server or the client + ================ + */ + public static void Pmove(pmove_t pmove) + { + pm = pmove; + + // clear results + pm.numtouch = 0; + VectorClear(pm.viewangles); + pm.viewheight = 0; + pm.groundentity = null; + pm.watertype = 0; + pm.waterlevel = 0; + + // clear all pmove local vars + //memset(& pml, 0, sizeof(pml)); + pml = new pml_t(); + + // convert origin and velocity to float values + pml.origin[0] = pm.s.origin[0] * 0.125f; + pml.origin[1] = pm.s.origin[1] * 0.125f; + pml.origin[2] = pm.s.origin[2] * 0.125f; + + pml.velocity[0] = pm.s.velocity[0] * 0.125f; + pml.velocity[1] = pm.s.velocity[1] * 0.125f; + pml.velocity[2] = pm.s.velocity[2] * 0.125f; + + // save old org in case we get stuck + VectorCopy(pm.s.origin, pml.previous_origin); + + pml.frametime = (pm.cmd.msec & 0xFF) * 0.001f; + + PM_ClampAngles(); + + if (pm.s.pm_type == PM_SPECTATOR) + { + PM_FlyMove(false); + PM_SnapPosition(); + return; + } + + if (pm.s.pm_type >= PM_DEAD) + { + pm.cmd.forwardmove = 0; + pm.cmd.sidemove = 0; + pm.cmd.upmove = 0; + } + + if (pm.s.pm_type == PM_FREEZE) + return; // no movement at all + + // set mins, maxs, and viewheight + PM_CheckDuck(); + + if (pm.snapinitial) + PM_InitialSnapPosition(); + + // set groundentity, watertype, and waterlevel + PM_CatagorizePosition(); + + if (pm.s.pm_type == PM_DEAD) + PM_DeadMove(); + + PM_CheckSpecialMovement(); + + // drop timing counter + if (pm.s.pm_time != 0) + { + int msec; + + // TOD o bugfix cwei + msec = pm.cmd.msec >>> 3; + if (msec == 0) + msec = 1; + if (msec >= (pm.s.pm_time & 0xFF)) + { + pm.s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); + pm.s.pm_time = 0; + } + else + pm.s.pm_time = (byte)((pm.s.pm_time & 0xFF) - msec); + } + + if ((pm.s.pm_flags & PMF_TIME_TELEPORT) != 0) + { // teleport pause stays exactly in place + } + else if ((pm.s.pm_flags & PMF_TIME_WATERJUMP) != 0) + { // waterjump has no control, but falls + pml.velocity[2] -= pm.s.gravity * pml.frametime; + if (pml.velocity[2] < 0) + { // cancel as soon as we are falling down again + pm.s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); + pm.s.pm_time = 0; + } + + PM_StepSlideMove(); + } + else + { + PM_CheckJump(); + + PM_Friction(); + + if (pm.waterlevel >= 2) + PM_WaterMove(); + else + { + float[] angles={0,0,0}; + + VectorCopy(pm.viewangles, angles); + if (angles[PITCH] > 180) + angles[PITCH] = angles[PITCH] - 360; + angles[PITCH] /= 3; + + AngleVectors(angles, pml.forward, pml.right, pml.up); + + PM_AirMove(); + } + } + + // set groundentity, watertype, and waterlevel for final spot + PM_CatagorizePosition(); + + PM_SnapPosition(); + } + +} diff --git a/src/jake2/qcommon/Qcommon.java b/src/jake2/qcommon/Qcommon.java new file mode 100644 index 0000000..b55f4d9 --- /dev/null +++ b/src/jake2/qcommon/Qcommon.java @@ -0,0 +1,315 @@ +/* + * Qcommon.java + * Copyright 2003 + * + * $Id: Qcommon.java,v 1.1 2004-07-07 19:59:33 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +import jake2.Globals; +import jake2.client.*; +import jake2.game.Cmd; +import jake2.game.Swap; +import jake2.server.SV_MAIN; +import jake2.sys.NET; +import jake2.sys.Sys; +import jake2.util.Vargs; + +import java.io.FileWriter; +import java.io.IOException; + +/** + * Qcommon contains some basic routines for the game engine + * namely initialization, shutdown and frame generation. + */ +public final class Qcommon extends Globals { + + /** + * This function initializes the different subsystems of + * the game engine. The setjmp/longjmp mechanism of the original + * was replaced with exceptions. + * @param args the original unmodified command line arguments + */ + public static void InitForTestMap(String[] args) { + try { + Z.chain.next= Z.chain.prev= Z.chain; + + // prepare enough of the subsystems to handle + // cvar and command buffer management + Com.InitArgv(args); + + Swap.Init(); + Cbuf.Init(); + + Cmd.Init(); + Cvar.Init(); + + Key.Init(); + + // we need to add the early commands twice, because + // a basedir or cddir needs to be set before execing + // config files, but we want other parms to override + // the settings of the config files + Cbuf.AddEarlyCommands(false); + Cbuf.Execute(); + + FS.InitFilesystem(); + + Cbuf.AddText("exec default.cfg\n"); + Cbuf.AddText("exec config.cfg\n"); + + Cbuf.AddEarlyCommands(true); + Cbuf.Execute(); + + // + // init commands and vars + // + Cmd.AddCommand("z_stats", Z.Stats_f); + Cmd.AddCommand("error", Com.Error_f); + + Globals.host_speeds= Cvar.Get("host_speeds", "0", 0); + Globals.log_stats= Cvar.Get("log_stats", "0", 0); + Globals.developer= Cvar.Get("developer", "0", 0); + Globals.timescale= Cvar.Get("timescale", "1", 0); + Globals.fixedtime= Cvar.Get("fixedtime", "0", 0); + Globals.logfile_active= Cvar.Get("logfile", "0", 0); + Globals.showtrace= Cvar.Get("showtrace", "0", 0); + Globals.dedicated= Cvar.Get("dedicated", "0", CVAR_NOSET); + + String s = Com.sprintf("%4.2f %s %s %s", + new Vargs(4) + .add(Globals.VERSION) + .add(Globals.CPUSTRING) + .add(Globals.__DATE__) + .add(Globals.BUILDSTRING)); + + Cvar.Get("version", s, CVAR_SERVERINFO | CVAR_NOSET); + + NET.NET_Init(); + Netchan.Netchan_Init(); + + //SV_MAIN.SV_Init(); + CL.Init(); + + // add + commands from command line + if (!Cbuf.AddLateCommands()) { + // if the user didn't give any commands, run default action + Cbuf.AddText("d1\n"); + Cbuf.Execute(); + } else { + // the user asked for something explicit + // so drop the loading plaque + SCR.EndLoadingPlaque(); + } + + Com.Printf("====== Quake2 Initialized ======\n\n"); + + } catch (longjmpException e) { + Sys.Error("Error during initialization"); + } + } + + + /** + * This function initializes the different subsystems of + * the game engine. The setjmp/longjmp mechanism of the original + * was replaced with exceptions. + * @param args the original unmodified command line arguments + */ + public static void Init(String[] args) { + try { + + // prepare enough of the subsystems to handle + // cvar and command buffer management + Com.InitArgv(args); + + Swap.Init(); + Cbuf.Init(); + + Cmd.Init(); + Cvar.Init(); + + Key.Init(); + + // we need to add the early commands twice, because + // a basedir or cddir needs to be set before execing + // config files, but we want other parms to override + // the settings of the config files + Cbuf.AddEarlyCommands(false); + Cbuf.Execute(); + + FS.InitFilesystem(); + + Cbuf.AddText("exec default.cfg\n"); + Cbuf.AddText("exec config.cfg\n"); + + Cbuf.AddEarlyCommands(true); + Cbuf.Execute(); + + // + // init commands and vars + // + Cmd.AddCommand("z_stats", Z.Stats_f); + Cmd.AddCommand("error", Com.Error_f); + + Globals.host_speeds= Cvar.Get("host_speeds", "0", 0); + Globals.log_stats= Cvar.Get("log_stats", "0", 0); + Globals.developer= Cvar.Get("developer", "0", 0); + Globals.timescale= Cvar.Get("timescale", "0", 0); + Globals.fixedtime= Cvar.Get("fixedtime", "0", 0); + Globals.logfile_active= Cvar.Get("logfile", "0", 0); + Globals.showtrace= Cvar.Get("showtrace", "0", 0); + Globals.dedicated= Cvar.Get("dedicated", "0", CVAR_NOSET); + + String s = Com.sprintf("%4.2f %s %s %s", + new Vargs(4) + .add(Globals.VERSION) + .add(Globals.CPUSTRING) + .add(Globals.__DATE__) + .add(Globals.BUILDSTRING)); + + Cvar.Get("version", s, CVAR_SERVERINFO | CVAR_NOSET); + + NET.NET_Init(); //ok + Netchan.Netchan_Init(); //ok + + SV_MAIN.SV_Init(); //ok + CL.Init(); + + // add + commands from command line + if (!Cbuf.AddLateCommands()) { + // if the user didn't give any commands, run default action +// Cbuf.AddText("d1\n"); + Cbuf.Execute(); + } else { + // the user asked for something explicit + // so drop the loading plaque + SCR.EndLoadingPlaque(); + } + + Com.Printf("====== Quake2 Initialized ======\n\n"); + + } catch (longjmpException e) { + Sys.Error("Error during initialization"); + } + } + + /** + * Trigger generation of a frame for the given time. The setjmp/longjmp + * mechanism of the original was replaced with exceptions. + * @param msec the current game time + */ + public static void Frame(int msec) { + try { + + if (Globals.log_stats.modified) { + Globals.log_stats.modified= false; + + if (Globals.log_stats.value != 0.0f) { + + if (Globals.log_stats_file != null) { + try { + Globals.log_stats_file.close(); + } catch (IOException e) { + } + Globals.log_stats_file= null; + } + + try { + Globals.log_stats_file= new FileWriter("stats.log"); + } catch (IOException e) { + Globals.log_stats_file= null; + } + if (Globals.log_stats_file != null) { + try { + Globals.log_stats_file.write("entities,dlights,parts,frame time\n"); + } catch (IOException e) { + } + } + + } else { + + if (Globals.log_stats_file != null) { + try { + Globals.log_stats_file.close(); + } catch (IOException e) { + } + Globals.log_stats_file= null; + } + } + } + + if (Globals.fixedtime.value != 0.0f) { + msec= (int) Globals.fixedtime.value; + } else if (Globals.timescale.value != 0.0f) { + msec *= Globals.timescale.value; + if (msec < 1) + msec= 1; + } + + if (Globals.showtrace.value != 0.0f) { + Com.Printf("%4i traces %4i points\n", + new Vargs(2).add(Globals.c_traces) + .add(Globals.c_pointcontents)); + + + Globals.c_traces= 0; + Globals.c_brush_traces= 0; + Globals.c_pointcontents= 0; + } + + Cbuf.Execute (); + + int time_before= 0; + int time_between= 0; + int time_after= 0; + + if (Globals.host_speeds.value != 0.0f) + time_before= Sys.Milliseconds(); + + SV_MAIN.SV_Frame(msec); + + if (Globals.host_speeds.value != 0.0f) + time_between= Sys.Milliseconds(); + + CL.Frame(msec); + + if (Globals.host_speeds.value != 0.0f) { + time_after= Sys.Milliseconds(); + + int all= time_after - time_before; + int sv= time_between - time_before; + int cl= time_after - time_between; + int gm= Globals.time_after_game - Globals.time_before_game; + int rf= Globals.time_after_ref - Globals.time_before_ref; + sv -= gm; + cl -= rf; + + Com.Printf("all:%3i sv:%3i gm:%3i cl:%3i rf:%3i\n", + new Vargs(5).add(all).add(sv).add(gm).add(cl).add(rf)); + } + + } catch (longjmpException e) { + } + } + +} diff --git a/src/jake2/qcommon/SZ.java b/src/jake2/qcommon/SZ.java new file mode 100644 index 0000000..f17daa2 --- /dev/null +++ b/src/jake2/qcommon/SZ.java @@ -0,0 +1,110 @@ +/* + * SZ.java + * Copyright (C) 2003 + * + * $Id: SZ.java,v 1.1 2004-07-07 19:59:33 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +import java.util.Arrays; + +import jake2.Defines; + +/** + * SZ + */ +public final class SZ { + + public static void Clear(sizebuf_t buf) { + buf.clear(); + } + + //=========================================================================== + + public static void Init(sizebuf_t buf, byte data[], int length) { + //memset (buf, 0, sizeof(*buf)); + //TODO: slow but safe; + Arrays.fill(data,(byte)0); + buf.data = data; + buf.maxsize = length; + } + + + /** Ask for the pointer using sizebuf_t.cursize (RST) */ + public static int GetSpace(sizebuf_t buf, int length) { + int oldsize; + + if (buf.cursize + length > buf.maxsize) { + if (!buf.allowoverflow) + Com.Error(Defines.ERR_FATAL, "SZ_GetSpace: overflow without allowoverflow set"); + + if (length > buf.maxsize) + Com.Error(Defines.ERR_FATAL, "SZ_GetSpace: " + length + " is > full buffer size"); + + Com.Printf("SZ_GetSpace: overflow\n"); + Clear(buf); + buf.overflowed = true; + } + + oldsize = buf.cursize; + buf.cursize += length; + + return oldsize; + } + + public static void Write(sizebuf_t buf, byte data[], int length) { + //memcpy(SZ_GetSpace(buf, length), data, length); + System.arraycopy(data, 0, buf.data, GetSpace(buf, length), length); + } + + public static void Write(sizebuf_t buf, byte data[], int offset, int length) { + System.arraycopy(data, offset, buf.data, GetSpace(buf, length), length); + } + + public static void Write(sizebuf_t buf, byte data[]) { + int length = data.length; + //memcpy(SZ_GetSpace(buf, length), data, length); + System.arraycopy(data, 0, buf.data, GetSpace(buf, length), length); + } + + // + public static void Print(sizebuf_t buf, String data) { + int length = data.length(); + byte str[] = data.getBytes(); + + if (buf.cursize != 0) { + + if (buf.data[buf.cursize - 1] != 0) { + //memcpy( SZ_GetSpace(buf, len), data, len); // no trailing 0 + System.arraycopy(str, 0, buf.data, GetSpace(buf, length+1), length); + } else { + System.arraycopy(str, 0, buf.data, GetSpace(buf, length)-1, length); + //memcpy(SZ_GetSpace(buf, len - 1) - 1, data, len); // write over trailing 0 + } + } else + // first print. + System.arraycopy(str, 0, buf.data, GetSpace(buf, length), length); + //memcpy(SZ_GetSpace(buf, len), data, len); + + buf.data[buf.cursize - 1]=0; + } +} diff --git a/src/jake2/qcommon/Z.java b/src/jake2/qcommon/Z.java new file mode 100644 index 0000000..f25e4eb --- /dev/null +++ b/src/jake2/qcommon/Z.java @@ -0,0 +1,63 @@ +/* + * Z.java + * Copyright (C) 2003 + * + * $Id: Z.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + */ + /* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +import java.nio.ByteBuffer; + + +/** + * Z implements special memory management. + */ +public final class Z { + + public static xcommand_t Stats_f = new xcommand_t() { + public void execute() { + Com.Printf("abstract xccommand_t not overloaded."); + } + }; + + static final zhead_t chain = new zhead_t(); + static final short MAGIC = 0x1d1d; + + static int count = 0; + static int bytes = 0; + + public static byte[] Malloc(int size) { + return TagMalloc(size, 0); + } + + static byte [] TagMalloc(int size, int tag) { + return new byte[size]; + } + + public static void Free(byte[] ptr) { + } + + public static void Free(ByteBuffer buffer) { + buffer.clear(); + buffer.limit(0); + } +} diff --git a/src/jake2/qcommon/cmd_function_t.java b/src/jake2/qcommon/cmd_function_t.java new file mode 100644 index 0000000..3dab141 --- /dev/null +++ b/src/jake2/qcommon/cmd_function_t.java @@ -0,0 +1,35 @@ +/* + * cmd_function_t.java + * Copyright (C) 2003 + * + * $Id: cmd_function_t.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +/** + * cmd_function_t + */ +public final class cmd_function_t { + public cmd_function_t next = null; + public String name = null; + public xcommand_t function; +} diff --git a/src/jake2/qcommon/longjmpException.java b/src/jake2/qcommon/longjmpException.java new file mode 100644 index 0000000..cf8610b --- /dev/null +++ b/src/jake2/qcommon/longjmpException.java @@ -0,0 +1,33 @@ +/* + * longjmpException.java + * Copyright (C) 2003 + * + * $Id: longjmpException.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +/** + * longjmpException is used to replace the setjmp/longjmp code. + */ +public final class longjmpException extends IllegalStateException { + +} diff --git a/src/jake2/qcommon/lump_t.java b/src/jake2/qcommon/lump_t.java new file mode 100644 index 0000000..ef69d5c --- /dev/null +++ b/src/jake2/qcommon/lump_t.java @@ -0,0 +1,35 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 02.01.2004 by RST. +// $Id: lump_t.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + +package jake2.qcommon; + +public class lump_t +{ + public lump_t(int offset, int len) + { + this.fileofs = offset; + this.filelen = len; + } + + public int fileofs, filelen; +} diff --git a/src/jake2/qcommon/miptex_t.java b/src/jake2/qcommon/miptex_t.java new file mode 100644 index 0000000..acb22e0 --- /dev/null +++ b/src/jake2/qcommon/miptex_t.java @@ -0,0 +1,49 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 02.01.2004 by RST. +// $Id: miptex_t.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + +package jake2.qcommon; + + +import jake2.*; +// import jake2.client.*; +// import jake2.game.*; +// import jake2.qcommon.*; +// import jake2.render.*; +// import jake2.server.*; + +public class miptex_t { + //char name[32]; + String name=""; + + int width, height; + + //unsigned offsets[MIPLEVELS]; // four mip maps stored + int offsets[] = new int[Defines.MIPLEVELS]; // four mip maps stored + + //char animname[32]; // next frame in animation chain + String animframe=""; + + int flags; + int contents; + int value; +} diff --git a/src/jake2/qcommon/netadr_t.java b/src/jake2/qcommon/netadr_t.java new file mode 100644 index 0000000..3d45b0c --- /dev/null +++ b/src/jake2/qcommon/netadr_t.java @@ -0,0 +1,65 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 27.11.2003 by RST. +// $Id: netadr_t.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + +package jake2.qcommon; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +public class netadr_t { + + public netadr_t() + {} + public netadr_t(netadr_t from) + { + type = from.type; + port = from.port; + ip= new byte [4]; + ip[0] = from.ip[0]; + ip[1] = from.ip[1]; + ip[2] = from.ip[2]; + ip[3] = from.ip[3]; + } + + public int type; + public int port; + public byte ip[] = {1,2,3,4}; + //public byte ipx[] = new byte[10]; + + InetAddress ia = null; + + public InetAddress getInetAddress() throws UnknownHostException + { + if (ia == null) + ia = InetAddress.getByAddress(ip); + + return ia; + } + + public netadr_t copy() + { + + // TODO Auto-generated method stub + return new netadr_t(this); + } +} diff --git a/src/jake2/qcommon/netchan_t.java b/src/jake2/qcommon/netchan_t.java new file mode 100644 index 0000000..06a283a --- /dev/null +++ b/src/jake2/qcommon/netchan_t.java @@ -0,0 +1,79 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 27.11.2003 by RST. +// $Id: netchan_t.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + +package jake2.qcommon; + +import jake2.*; + +public class netchan_t { + + + public boolean fatal_error; + + // was enum {NS_CLIENT, NS_SERVER} + public int sock; + + public int dropped; // between last packet and previous + + public int last_received; // for timeouts + public int last_sent; // for retransmits + + public netadr_t remote_address = new netadr_t(); + public int qport; // qport value to write when transmitting + + // sequencing variables + public int incoming_sequence; + public int incoming_acknowledged; + public int incoming_reliable_acknowledged; // single bit + + public int incoming_reliable_sequence; // single bit, maintained local + + public int outgoing_sequence; + public int reliable_sequence; // single bit + public int last_reliable_sequence; // sequence number of last send + + // reliable staging and holding areas + public sizebuf_t message = new sizebuf_t(); // writing buffer to send to server + public byte message_buf[] = new byte[Defines.MAX_MSGLEN - 16]; // leave space for header + + // message is copied to this buffer when it is first transfered + int reliable_length; + + byte reliable_buf[] = new byte[Defines.MAX_MSGLEN - 16]; // unpcked reliable message + + //ok. + public void clear() + { + sock=dropped=last_received=last_sent=0; + remote_address = new netadr_t(); + qport = incoming_sequence = incoming_acknowledged = incoming_reliable_acknowledged = incoming_reliable_sequence + = outgoing_sequence = reliable_sequence = last_reliable_sequence =0; + message = new sizebuf_t(); + + message_buf = new byte[Defines.MAX_MSGLEN - 16]; + + reliable_length =0; + reliable_buf = new byte[Defines.MAX_MSGLEN - 16]; + } + +} diff --git a/src/jake2/qcommon/qfiles.java b/src/jake2/qcommon/qfiles.java new file mode 100644 index 0000000..6a51fd4 --- /dev/null +++ b/src/jake2/qcommon/qfiles.java @@ -0,0 +1,655 @@ +/* + * qfiles.java + * Copyright (C) 2003 + * + * $Id: qfiles.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import jake2.*; +import jake2.client.*; +import jake2.game.*; +import jake2.render.*; +import jake2.server.*; + +/** + * qfiles + * + * @author cwei + */ +public class qfiles { + // + // 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 + + ======================================================================== + */ + + /* + ======================================================================== + + PCX files are used for as many images as possible + + ======================================================================== + */ + public static class pcx_t { + + // size of byte arrays + static final int PALETTE_SIZE = 48; + static final int FILLER_SIZE = 58; + + public byte manufacturer; + public byte version; + public byte encoding; + public byte bits_per_pixel; + public int xmin, ymin, xmax, ymax; // unsigned short + public int hres, vres; // unsigned short + public byte[] palette; //unsigned byte; size 48 + public byte reserved; + public byte color_planes; + public int bytes_per_line; // unsigned short + public int palette_type; // unsigned short + public byte[] filler; // size 58 + public ByteBuffer data; //unbounded data + + public pcx_t(byte[] dataBytes) { + this(ByteBuffer.wrap(dataBytes)); + } + + public pcx_t(ByteBuffer b) { + // is stored as little endian + b.order(ByteOrder.LITTLE_ENDIAN); + + // fill header + manufacturer = b.get(); + version = b.get(); + encoding = b.get(); + bits_per_pixel = b.get(); + xmin = b.getShort() & 0xffff; + ymin = b.getShort() & 0xffff; + xmax = b.getShort() & 0xffff; + ymax = b.getShort() & 0xffff; + hres = b.getShort() & 0xffff; + vres = b.getShort() & 0xffff; + b.get(palette = new byte[PALETTE_SIZE]); + reserved = b.get(); + color_planes = b.get(); + bytes_per_line = b.getShort() & 0xffff; + palette_type = b.getShort() & 0xffff; + b.get(filler = new byte[FILLER_SIZE]); + + // fill data + data = b.slice(); + } + } + + /* + ======================================================================== + + .MD2 triangle model file format + + ======================================================================== + */ + + public static final int IDALIASHEADER = (('2'<<24)+('P'<<16)+('D'<<8)+'I'); + public static final int ALIAS_VERSION = 8; + + public static final int MAX_TRIANGLES = 4096; + public static final int MAX_VERTS = 2048; + public static final int MAX_FRAMES = 512; + public static final int MAX_MD2SKINS = 32; + public static final int MAX_SKINNAME = 64; + + public static class dstvert_t { + public short s; + public short t; + + public dstvert_t(ByteBuffer b) { + s = b.getShort(); + t = b.getShort(); + } + } + + public static class dtriangle_t { + public short index_xyz[] = { 0, 0, 0 }; + public short index_st[] = { 0, 0, 0 }; + + public dtriangle_t(ByteBuffer b) { + index_xyz[0] = b.getShort(); + index_xyz[1] = b.getShort(); + index_xyz[2] = b.getShort(); + + index_st[0] = b.getShort(); + index_st[1] = b.getShort(); + index_st[2] = b.getShort(); + } + } + + public static class dtrivertx_t { + public int v[] = { 0, 0, 0 }; // byte 0..255 scaled byte to fit in frame mins/maxs + public int lightnormalindex; // byte 0 .. 255; + + public dtrivertx_t(ByteBuffer b) { + v[0] = b.get() & 0xff; // unsigned byte + v[1] = b.get() & 0xff; // unsigned byte + v[2] = b.get() & 0xff; // unsigned byte + lightnormalindex = b.get() & 0xff; // unsigned byte + } + } + + public static final int DTRIVERTX_V0 = 0; + public static final int DTRIVERTX_V1 = 1; + public static final int DTRIVERTX_V2 = 2; + public static final int DTRIVERTX_LNI = 3; + public static final int DTRIVERTX_SIZE = 4; + + public static class daliasframe_t { + public float[] scale = {0, 0, 0}; // multiply byte verts by this + public float[] translate = {0, 0, 0}; // then add this + public String name; // frame name from grabbing (size 16) + public dtrivertx_t[] verts; // variable sized + + public daliasframe_t(ByteBuffer b) { + scale[0] = b.getFloat(); scale[1] = b.getFloat(); scale[2] = b.getFloat(); + translate[0] = b.getFloat(); translate[1] = b.getFloat(); translate[2] = b.getFloat(); + byte[] nameBuf = new byte[16]; + b.get(nameBuf); + name = new String(nameBuf).trim(); + } + } + + // 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. + + public static class dmdl_t { + public int ident; + public int version; + + public int skinwidth; + public int skinheight; + public int framesize; // byte size of each frame + + public int num_skins; + public int num_xyz; + public int num_st; // greater than num_xyz for seams + public int num_tris; + public int num_glcmds; // dwords in strip/fan command list + public int num_frames; + + public int ofs_skins; // each skin is a MAX_SKINNAME string + public int ofs_st; // byte offset from start for stverts + public int ofs_tris; // offset for dtriangles + public int ofs_frames; // offset for first frame + public int ofs_glcmds; + public int ofs_end; // end of file + + // wird extra gebraucht + public String[] skinNames; + public dstvert_t[] stVerts; + public dtriangle_t[] triAngles; + public int[] glCmds; + public daliasframe_t[] aliasFrames; + + + public dmdl_t(ByteBuffer b) { + ident = b.getInt(); + version = b.getInt(); + + skinwidth = b.getInt(); + skinheight = b.getInt(); + framesize = b.getInt(); // byte size of each frame + + num_skins = b.getInt(); + num_xyz = b.getInt(); + num_st = b.getInt(); // greater than num_xyz for seams + num_tris = b.getInt(); + num_glcmds = b.getInt(); // dwords in strip/fan command list + num_frames = b.getInt(); + + ofs_skins = b.getInt(); // each skin is a MAX_SKINNAME string + ofs_st = b.getInt(); // byte offset from start for stverts + ofs_tris = b.getInt(); // offset for dtriangles + ofs_frames = b.getInt(); // offset for first frame + ofs_glcmds = b.getInt(); + ofs_end = b.getInt(); // end of file + } + } + + /* + ======================================================================== + + .SP2 sprite file format + + ======================================================================== + */ + // little-endian "IDS2" + public static final int IDSPRITEHEADER = (('2'<<24)+('S'<<16)+('D'<<8)+'I'); + public static final int SPRITE_VERSION = 2; + + public static class dsprframe_t { + public int width, height; + public int origin_x, origin_y; // raster coordinates inside pic + public String name; // name of pcx file (MAX_SKINNAME) + + public dsprframe_t(ByteBuffer b) { + width = b.getInt(); + height = b.getInt(); + origin_x = b.getInt(); + origin_y = b.getInt(); + + byte[] nameBuf = new byte[MAX_SKINNAME]; + b.get(nameBuf); + name = new String(nameBuf).trim(); + } + } + + public static class dsprite_t { + public int ident; + public int version; + public int numframes; + public dsprframe_t frames[]; // variable sized + + public dsprite_t(ByteBuffer b) { + ident = b.getInt(); + version = b.getInt(); + numframes = b.getInt(); + + frames = new dsprframe_t[numframes]; + for (int i=0; i < numframes; i++) { + frames[i] = new dsprframe_t(b); + } + } + } + + /* + ============================================================================== + + .WAL texture file format + + ============================================================================== + */ + public static class miptex_t { + + static final int MIPLEVELS = 4; + static final int NAME_SIZE = 32; + + public String name; // char name[32]; + public int width, height; + public int[] offsets = new int[MIPLEVELS]; // 4 mip maps stored + // next frame in animation chain + public String animname; // char animname[32]; + public int flags; + public int contents; + public int value; + + public miptex_t(byte[] dataBytes) { + this(ByteBuffer.wrap(dataBytes)); + } + + public miptex_t(ByteBuffer b) { + // is stored as little endian + b.order(ByteOrder.LITTLE_ENDIAN); + + byte[] nameBuf = new byte[NAME_SIZE]; + // fill header + b.get(nameBuf); + name = new String(nameBuf).trim(); + width = b.getInt(); + height = b.getInt(); + offsets[0] = b.getInt(); + offsets[1] = b.getInt(); + offsets[2] = b.getInt(); + offsets[3] = b.getInt(); + b.get(nameBuf); + animname = new String(nameBuf).trim(); + flags = b.getInt(); + contents = b.getInt(); + value = b.getInt(); + } + + } + + /* + ============================================================================== + + .BSP file format + + ============================================================================== + */ + + public static final int IDBSPHEADER = (('P'<<24)+('S'<<16)+('B'<<8)+'I'); + + // ============================================================================= + + public static class dheader_t { + + public dheader_t(ByteBuffer bb) { + bb.order(ByteOrder.LITTLE_ENDIAN); + this.ident = bb.getInt(); + this.version = bb.getInt(); + + for (int n = 0; n < Defines.HEADER_LUMPS; n++) + lumps[n] = new lump_t(bb.getInt(), bb.getInt()); + + } + + public int ident; + public int version; + public lump_t lumps[] = new lump_t[Defines.HEADER_LUMPS]; + } + + public static class dmodel_t { + + public dmodel_t(ByteBuffer bb) { + bb.order(ByteOrder.LITTLE_ENDIAN); + + for (int j = 0; j < 3; j++) + mins[j] = bb.getFloat(); + + for (int j = 0; j < 3; j++) + maxs[j] = bb.getFloat(); + + for (int j = 0; j < 3; j++) + origin[j] = bb.getFloat(); + + headnode = bb.getInt(); + firstface = bb.getInt(); + numfaces = bb.getInt(); + } + public float mins[] = { 0, 0, 0 }; + public float maxs[] = { 0, 0, 0 }; + public float origin[] = { 0, 0, 0 }; // for sounds or lights + public int headnode; + public int firstface, numfaces; // submodels just draw faces + // without walking the bsp tree + + public static int SIZE = 3 * 4 + 3 * 4 + 3 * 4 + 4 + 8; + } + + public static class dvertex_t { + + public static final int SIZE = 3 * 4; // 3 mal 32 bit float + + public float[] point = { 0, 0, 0 }; + + public dvertex_t(ByteBuffer b) { + point[0] = b.getFloat(); + point[1] = b.getFloat(); + point[2] = b.getFloat(); + } + } + + + // planes (x&~1) and (x&~1)+1 are always opposites + public static class dplane_t { + + public dplane_t(ByteBuffer bb) { + bb.order(ByteOrder.LITTLE_ENDIAN); + + normal[0] = (bb.getFloat()); + normal[1] = (bb.getFloat()); + normal[2] = (bb.getFloat()); + + dist = (bb.getFloat()); + type = (bb.getInt()); + } + + public float normal[] = { 0, 0, 0 }; + public float dist; + public int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate + + public static final int SIZE = 3 * 4 + 4 + 4; + } + + public static class dnode_t { + + public dnode_t(ByteBuffer bb) { + + bb.order(ByteOrder.LITTLE_ENDIAN); + planenum = bb.getInt(); + + children[0] = bb.getInt(); + children[1] = bb.getInt(); + + for (int j = 0; j < 3; j++) + mins[j] = bb.getShort(); + + for (int j = 0; j < 3; j++) + maxs[j] = bb.getShort(); + + firstface = bb.getShort() & 0xffff; + numfaces = bb.getShort() & 0xffff; + + } + + public int planenum; + public int children[] = { 0, 0 }; + // negative numbers are -(leafs+1), not nodes + public short mins[] = { 0, 0, 0 }; // for frustom culling + public short maxs[] = { 0, 0, 0 }; + + /* + unsigned short firstface; + unsigned short numfaces; // counting both sides + */ + + public int firstface; + public int numfaces; + + public static int SIZE = 4 + 8 + 6 + 6 + 2 + 2; // counting both sides + } + + + + // note that edge 0 is never used, because negative edge nums are used for + // counterclockwise use of the edge in a face + + public static class dedge_t { + // unsigned short v[2]; + int v[] = { 0, 0 }; + } + + public static class dface_t { + + public static final int SIZE = + 4 * Defines.SIZE_OF_SHORT + + 2 * Defines.SIZE_OF_INT + + Defines.MAXLIGHTMAPS; + + //unsigned short planenum; + public int planenum; + public short side; + + public int firstedge; // we must support > 64k edges + public short numedges; + public short texinfo; + + // lighting info + public byte styles[] = new byte[Defines.MAXLIGHTMAPS]; + public int lightofs; // start of [numstyles*surfsize] samples + + public dface_t(ByteBuffer b) { + planenum = b.getShort() & 0xFFFF; + side = b.getShort(); + firstedge = b.getInt(); + numedges = b.getShort(); + texinfo = b.getShort(); + b.get(styles); + lightofs = b.getInt(); + } + + } + + public static class dleaf_t { + + public dleaf_t(byte[] cmod_base, int i, int j) { + this(ByteBuffer.wrap(cmod_base, i, j).order(ByteOrder.LITTLE_ENDIAN)); + } + + public dleaf_t(ByteBuffer bb) { + contents = bb.getInt(); + cluster = bb.getShort(); + area = bb.getShort(); + + mins[0] = bb.getShort(); + mins[1] = bb.getShort(); + mins[2] = bb.getShort(); + + maxs[0] = bb.getShort(); + maxs[1] = bb.getShort(); + maxs[2] = bb.getShort(); + + firstleafface = bb.getShort() & 0xffff; + numleaffaces = bb.getShort() & 0xffff; + + firstleafbrush = bb.getShort() & 0xffff; + numleafbrushes = bb.getShort() & 0xffff; + } + + public static final int SIZE = 4 + 8 * 2 + 4 * 2; + + public int contents; // OR of all brushes (not needed?) + + public short cluster; + public short area; + + public short mins[] = { 0, 0, 0 }; // for frustum culling + public short maxs[] = { 0, 0, 0 }; + + public int firstleafface; // unsigned short + public int numleaffaces; // unsigned short + + public int firstleafbrush; // unsigned short + public int numleafbrushes; // unsigned short + } + + public static class dbrushside_t { + + public dbrushside_t(ByteBuffer bb) { + bb.order(ByteOrder.LITTLE_ENDIAN); + + planenum = bb.getShort() & 0xffff; + texinfo = bb.getShort(); + } + + //unsigned short planenum; + int planenum; // facing out of the leaf + + short texinfo; + + public static int SIZE = 4; + } + + public static class dbrush_t { + + public dbrush_t(ByteBuffer bb) { + bb.order(ByteOrder.LITTLE_ENDIAN); + firstside = bb.getInt(); + numsides = bb.getInt(); + contents = bb.getInt(); + } + + public static int SIZE = 3 * 4; + + int firstside; + int numsides; + int contents; + } + + // #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 + + public static class dvis_t { + + public dvis_t(ByteBuffer bb) { + numclusters = bb.getInt(); + bitofs = new int[numclusters][2]; + + for (int i = 0; i < numclusters; i++) { + bitofs[i][0] = bb.getInt(); + bitofs[i][1] = bb.getInt(); + } + } + + public int numclusters; + public int bitofs[][] = new int[8][2]; // bitofs[numclusters][2] + } + + // 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 + + public static class dareaportal_t { + + public dareaportal_t() { + } + + public dareaportal_t(ByteBuffer bb) { + bb.order(ByteOrder.LITTLE_ENDIAN); + + portalnum = bb.getShort(); + otherarea = bb.getShort(); + } + + int portalnum; + int otherarea; + + public static int SIZE = 8; + } + + public static class darea_t { + + public darea_t(ByteBuffer bb) { + + bb.order(ByteOrder.LITTLE_ENDIAN); + + numareaportals = bb.getInt(); + firstareaportal = bb.getInt(); + + } + int numareaportals; + int firstareaportal; + + public static int SIZE = 8; + } + +}
\ No newline at end of file diff --git a/src/jake2/qcommon/sizebuf_t.java b/src/jake2/qcommon/sizebuf_t.java new file mode 100644 index 0000000..d6d6d13 --- /dev/null +++ b/src/jake2/qcommon/sizebuf_t.java @@ -0,0 +1,48 @@ +/* + * sizebuf_t.java + * Copyright (C) 2003 + * + * $Id: sizebuf_t.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +import java.util.Arrays; + +/** + * sizebuf_t + */ +public final class sizebuf_t { + public boolean allowoverflow = false; + public boolean overflowed = false; + public byte[] data = null; + public int maxsize = 0; + public int cursize = 0; + public int readcount = 0; + + public void clear() + { + if (data!=null) + Arrays.fill(data,(byte)0); + cursize = 0; + overflowed = false; + } +} diff --git a/src/jake2/qcommon/texinfo_t.java b/src/jake2/qcommon/texinfo_t.java new file mode 100644 index 0000000..aa23894 --- /dev/null +++ b/src/jake2/qcommon/texinfo_t.java @@ -0,0 +1,67 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 02.01.2004 by RST. +// $Id: texinfo_t.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + +package jake2.qcommon; + +import jake2.game.EndianHandler; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import jake2.util.*; + +public class texinfo_t { + + // works fine. + public texinfo_t(byte[] cmod_base, int o, int len) { + this(ByteBuffer.wrap(cmod_base, o, len).order(ByteOrder.LITTLE_ENDIAN)); + } + + public texinfo_t(ByteBuffer bb) { + + byte str[] = new byte[32]; + + vecs[0] = new float[] { bb.getFloat(), bb.getFloat(), bb.getFloat(), bb.getFloat()}; + vecs[1] = new float[] { bb.getFloat(), bb.getFloat(), bb.getFloat(), bb.getFloat()}; + + flags = bb.getInt(); + value = bb.getInt(); + + bb.get(str); + texture = new String(str, 0, Lib.strlen(str)); + nexttexinfo = bb.getInt(); + } + + public static final int SIZE = 32 + 4 + 4 + 32 + 4; + + //float vecs[2][4]; // [s/t][xyz offset] + public float vecs[][] = { + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 } + }; + public int flags; // miptex flags + overrides + public int value; // light emission, etc + //char texture[32]; // texture name (textures/*.wal) + public String texture=""; + public int nexttexinfo; // for animations, -1 = end of chain +} diff --git a/src/jake2/qcommon/xcommand_t.java b/src/jake2/qcommon/xcommand_t.java new file mode 100644 index 0000000..d038411 --- /dev/null +++ b/src/jake2/qcommon/xcommand_t.java @@ -0,0 +1,34 @@ +/* + * xcommand_t.java + * Copyright (C) 2003 + * + * $Id: xcommand_t.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + */ + /* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +/** + * xcommand_t + */ +public abstract class xcommand_t { + + abstract public void execute(); +} diff --git a/src/jake2/qcommon/zhead_t.java b/src/jake2/qcommon/zhead_t.java new file mode 100644 index 0000000..f2f1119 --- /dev/null +++ b/src/jake2/qcommon/zhead_t.java @@ -0,0 +1,38 @@ +/* + * zhead_t.java + * Copyright (C) 2003 + * + * $Id: zhead_t.java,v 1.1 2004-07-07 19:59:34 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.qcommon; + +/** + * zhead_t + */ +public final class zhead_t { + zhead_t prev = null; + zhead_t next = null; + short magic = 0; + short tag = 0; + int size = 0; + byte[] buf = null; +} |