1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7 
8 This file is part of the OpenJK source code.
9 
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23 
24 /*****************************************************************************
25  * name:		be_aas_file.c
26  *
27  * desc:		AAS file loading/writing
28  *
29  * $Archive: /MissionPack/code/botlib/be_aas_file.c $
30  * $Author: Zaphod $
31  * $Revision: 5 $
32  * $Modtime: 5/16/01 2:36p $
33  * $Date: 5/16/01 2:41p $
34  *
35  *****************************************************************************/
36 
37 #include "qcommon/q_shared.h"
38 #include "l_memory.h"
39 #include "l_script.h"
40 #include "l_precomp.h"
41 #include "l_struct.h"
42 #include "l_libvar.h"
43 #include "l_utils.h"
44 #include "aasfile.h"
45 #include "botlib.h"
46 #include "be_aas.h"
47 #include "be_aas_funcs.h"
48 #include "be_interface.h"
49 #include "be_aas_def.h"
50 
51 //#define AASFILEDEBUG
52 
53 //===========================================================================
54 //
55 // Parameter:				-
56 // Returns:					-
57 // Changes Globals:		-
58 //===========================================================================
AAS_SwapAASData(void)59 void AAS_SwapAASData(void)
60 {
61 	int i, j;
62 	//bounding boxes
63 	for (i = 0; i < aasworld.numbboxes; i++)
64 	{
65 		aasworld.bboxes[i].presencetype = LittleLong(aasworld.bboxes[i].presencetype);
66 		aasworld.bboxes[i].flags = LittleLong(aasworld.bboxes[i].flags);
67 		for (j = 0; j < 3; j++)
68 		{
69 			aasworld.bboxes[i].mins[j] = LittleLong(aasworld.bboxes[i].mins[j]);
70 			aasworld.bboxes[i].maxs[j] = LittleLong(aasworld.bboxes[i].maxs[j]);
71 		} //end for
72 	} //end for
73 	//vertexes
74 	for (i = 0; i < aasworld.numvertexes; i++)
75 	{
76 		for (j = 0; j < 3; j++)
77 			aasworld.vertexes[i][j] = LittleFloat(aasworld.vertexes[i][j]);
78 	} //end for
79 	//planes
80 	for (i = 0; i < aasworld.numplanes; i++)
81 	{
82 		for (j = 0; j < 3; j++)
83 			aasworld.planes[i].normal[j] = LittleFloat(aasworld.planes[i].normal[j]);
84 		aasworld.planes[i].dist = LittleFloat(aasworld.planes[i].dist);
85 		aasworld.planes[i].type = LittleLong(aasworld.planes[i].type);
86 	} //end for
87 	//edges
88 	for (i = 0; i < aasworld.numedges; i++)
89 	{
90 		aasworld.edges[i].v[0] = LittleLong(aasworld.edges[i].v[0]);
91 		aasworld.edges[i].v[1] = LittleLong(aasworld.edges[i].v[1]);
92 	} //end for
93 	//edgeindex
94 	for (i = 0; i < aasworld.edgeindexsize; i++)
95 	{
96 		aasworld.edgeindex[i] = LittleLong(aasworld.edgeindex[i]);
97 	} //end for
98 	//faces
99 	for (i = 0; i < aasworld.numfaces; i++)
100 	{
101 		aasworld.faces[i].planenum = LittleLong(aasworld.faces[i].planenum);
102 		aasworld.faces[i].faceflags = LittleLong(aasworld.faces[i].faceflags);
103 		aasworld.faces[i].numedges = LittleLong(aasworld.faces[i].numedges);
104 		aasworld.faces[i].firstedge = LittleLong(aasworld.faces[i].firstedge);
105 		aasworld.faces[i].frontarea = LittleLong(aasworld.faces[i].frontarea);
106 		aasworld.faces[i].backarea = LittleLong(aasworld.faces[i].backarea);
107 	} //end for
108 	//face index
109 	for (i = 0; i < aasworld.faceindexsize; i++)
110 	{
111 		aasworld.faceindex[i] = LittleLong(aasworld.faceindex[i]);
112 	} //end for
113 	//convex areas
114 	for (i = 0; i < aasworld.numareas; i++)
115 	{
116 		aasworld.areas[i].areanum = LittleLong(aasworld.areas[i].areanum);
117 		aasworld.areas[i].numfaces = LittleLong(aasworld.areas[i].numfaces);
118 		aasworld.areas[i].firstface = LittleLong(aasworld.areas[i].firstface);
119 		for (j = 0; j < 3; j++)
120 		{
121 			aasworld.areas[i].mins[j] = LittleFloat(aasworld.areas[i].mins[j]);
122 			aasworld.areas[i].maxs[j] = LittleFloat(aasworld.areas[i].maxs[j]);
123 			aasworld.areas[i].center[j] = LittleFloat(aasworld.areas[i].center[j]);
124 		} //end for
125 	} //end for
126 	//area settings
127 	for (i = 0; i < aasworld.numareasettings; i++)
128 	{
129 		aasworld.areasettings[i].contents = LittleLong(aasworld.areasettings[i].contents);
130 		aasworld.areasettings[i].areaflags = LittleLong(aasworld.areasettings[i].areaflags);
131 		aasworld.areasettings[i].presencetype = LittleLong(aasworld.areasettings[i].presencetype);
132 		aasworld.areasettings[i].cluster = LittleLong(aasworld.areasettings[i].cluster);
133 		aasworld.areasettings[i].clusterareanum = LittleLong(aasworld.areasettings[i].clusterareanum);
134 		aasworld.areasettings[i].numreachableareas = LittleLong(aasworld.areasettings[i].numreachableareas);
135 		aasworld.areasettings[i].firstreachablearea = LittleLong(aasworld.areasettings[i].firstreachablearea);
136 	} //end for
137 	//area reachability
138 	for (i = 0; i < aasworld.reachabilitysize; i++)
139 	{
140 		aasworld.reachability[i].areanum = LittleLong(aasworld.reachability[i].areanum);
141 		aasworld.reachability[i].facenum = LittleLong(aasworld.reachability[i].facenum);
142 		aasworld.reachability[i].edgenum = LittleLong(aasworld.reachability[i].edgenum);
143 		for (j = 0; j < 3; j++)
144 		{
145 			aasworld.reachability[i].start[j] = LittleFloat(aasworld.reachability[i].start[j]);
146 			aasworld.reachability[i].end[j] = LittleFloat(aasworld.reachability[i].end[j]);
147 		} //end for
148 		aasworld.reachability[i].traveltype = LittleLong(aasworld.reachability[i].traveltype);
149 		aasworld.reachability[i].traveltime = LittleShort(aasworld.reachability[i].traveltime);
150 	} //end for
151 	//nodes
152 	for (i = 0; i < aasworld.numnodes; i++)
153 	{
154 		aasworld.nodes[i].planenum = LittleLong(aasworld.nodes[i].planenum);
155 		aasworld.nodes[i].children[0] = LittleLong(aasworld.nodes[i].children[0]);
156 		aasworld.nodes[i].children[1] = LittleLong(aasworld.nodes[i].children[1]);
157 	} //end for
158 	//cluster portals
159 	for (i = 0; i < aasworld.numportals; i++)
160 	{
161 		aasworld.portals[i].areanum = LittleLong(aasworld.portals[i].areanum);
162 		aasworld.portals[i].frontcluster = LittleLong(aasworld.portals[i].frontcluster);
163 		aasworld.portals[i].backcluster = LittleLong(aasworld.portals[i].backcluster);
164 		aasworld.portals[i].clusterareanum[0] = LittleLong(aasworld.portals[i].clusterareanum[0]);
165 		aasworld.portals[i].clusterareanum[1] = LittleLong(aasworld.portals[i].clusterareanum[1]);
166 	} //end for
167 	//cluster portal index
168 	for (i = 0; i < aasworld.portalindexsize; i++)
169 	{
170 		aasworld.portalindex[i] = LittleLong(aasworld.portalindex[i]);
171 	} //end for
172 	//cluster
173 	for (i = 0; i < aasworld.numclusters; i++)
174 	{
175 		aasworld.clusters[i].numareas = LittleLong(aasworld.clusters[i].numareas);
176 		aasworld.clusters[i].numreachabilityareas = LittleLong(aasworld.clusters[i].numreachabilityareas);
177 		aasworld.clusters[i].numportals = LittleLong(aasworld.clusters[i].numportals);
178 		aasworld.clusters[i].firstportal = LittleLong(aasworld.clusters[i].firstportal);
179 	} //end for
180 } //end of the function AAS_SwapAASData
181 //===========================================================================
182 // dump the current loaded aas file
183 //
184 // Parameter:				-
185 // Returns:					-
186 // Changes Globals:		-
187 //===========================================================================
AAS_DumpAASData(void)188 void AAS_DumpAASData(void)
189 {
190 	aasworld.numbboxes = 0;
191 	if (aasworld.bboxes) FreeMemory(aasworld.bboxes);
192 	aasworld.bboxes = NULL;
193 	aasworld.numvertexes = 0;
194 	if (aasworld.vertexes) FreeMemory(aasworld.vertexes);
195 	aasworld.vertexes = NULL;
196 	aasworld.numplanes = 0;
197 	if (aasworld.planes) FreeMemory(aasworld.planes);
198 	aasworld.planes = NULL;
199 	aasworld.numedges = 0;
200 	if (aasworld.edges) FreeMemory(aasworld.edges);
201 	aasworld.edges = NULL;
202 	aasworld.edgeindexsize = 0;
203 	if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex);
204 	aasworld.edgeindex = NULL;
205 	aasworld.numfaces = 0;
206 	if (aasworld.faces) FreeMemory(aasworld.faces);
207 	aasworld.faces = NULL;
208 	aasworld.faceindexsize = 0;
209 	if (aasworld.faceindex) FreeMemory(aasworld.faceindex);
210 	aasworld.faceindex = NULL;
211 	aasworld.numareas = 0;
212 	if (aasworld.areas) FreeMemory(aasworld.areas);
213 	aasworld.areas = NULL;
214 	aasworld.numareasettings = 0;
215 	if (aasworld.areasettings) FreeMemory(aasworld.areasettings);
216 	aasworld.areasettings = NULL;
217 	aasworld.reachabilitysize = 0;
218 	if (aasworld.reachability) FreeMemory(aasworld.reachability);
219 	aasworld.reachability = NULL;
220 	aasworld.numnodes = 0;
221 	if (aasworld.nodes) FreeMemory(aasworld.nodes);
222 	aasworld.nodes = NULL;
223 	aasworld.numportals = 0;
224 	if (aasworld.portals) FreeMemory(aasworld.portals);
225 	aasworld.portals = NULL;
226 	aasworld.numportals = 0;
227 	if (aasworld.portalindex) FreeMemory(aasworld.portalindex);
228 	aasworld.portalindex = NULL;
229 	aasworld.portalindexsize = 0;
230 	if (aasworld.clusters) FreeMemory(aasworld.clusters);
231 	aasworld.clusters = NULL;
232 	aasworld.numclusters = 0;
233 	//
234 	aasworld.loaded = qfalse;
235 	aasworld.initialized = qfalse;
236 	aasworld.savefile = qfalse;
237 } //end of the function AAS_DumpAASData
238 //===========================================================================
239 //
240 // Parameter:				-
241 // Returns:					-
242 // Changes Globals:		-
243 //===========================================================================
244 #ifdef AASFILEDEBUG
AAS_FileInfo(void)245 void AAS_FileInfo(void)
246 {
247 	int i, n, optimized;
248 
249 	botimport.Print(PRT_MESSAGE, "version = %d\n", AASVERSION);
250 	botimport.Print(PRT_MESSAGE, "numvertexes = %d\n", aasworld.numvertexes);
251 	botimport.Print(PRT_MESSAGE, "numplanes = %d\n", aasworld.numplanes);
252 	botimport.Print(PRT_MESSAGE, "numedges = %d\n", aasworld.numedges);
253 	botimport.Print(PRT_MESSAGE, "edgeindexsize = %d\n", aasworld.edgeindexsize);
254 	botimport.Print(PRT_MESSAGE, "numfaces = %d\n", aasworld.numfaces);
255 	botimport.Print(PRT_MESSAGE, "faceindexsize = %d\n", aasworld.faceindexsize);
256 	botimport.Print(PRT_MESSAGE, "numareas = %d\n", aasworld.numareas);
257 	botimport.Print(PRT_MESSAGE, "numareasettings = %d\n", aasworld.numareasettings);
258 	botimport.Print(PRT_MESSAGE, "reachabilitysize = %d\n", aasworld.reachabilitysize);
259 	botimport.Print(PRT_MESSAGE, "numnodes = %d\n", aasworld.numnodes);
260 	botimport.Print(PRT_MESSAGE, "numportals = %d\n", aasworld.numportals);
261 	botimport.Print(PRT_MESSAGE, "portalindexsize = %d\n", aasworld.portalindexsize);
262 	botimport.Print(PRT_MESSAGE, "numclusters = %d\n", aasworld.numclusters);
263 	//
264 	for (n = 0, i = 0; i < aasworld.numareasettings; i++)
265 	{
266 		if (aasworld.areasettings[i].areaflags & AREA_GROUNDED) n++;
267 	} //end for
268 	botimport.Print(PRT_MESSAGE, "num grounded areas = %d\n", n);
269 	//
270 	botimport.Print(PRT_MESSAGE, "planes size %d bytes\n", aasworld.numplanes * sizeof(aas_plane_t));
271 	botimport.Print(PRT_MESSAGE, "areas size %d bytes\n", aasworld.numareas * sizeof(aas_area_t));
272 	botimport.Print(PRT_MESSAGE, "areasettings size %d bytes\n", aasworld.numareasettings * sizeof(aas_areasettings_t));
273 	botimport.Print(PRT_MESSAGE, "nodes size %d bytes\n", aasworld.numnodes * sizeof(aas_node_t));
274 	botimport.Print(PRT_MESSAGE, "reachability size %d bytes\n", aasworld.reachabilitysize * sizeof(aas_reachability_t));
275 	botimport.Print(PRT_MESSAGE, "portals size %d bytes\n", aasworld.numportals * sizeof(aas_portal_t));
276 	botimport.Print(PRT_MESSAGE, "clusters size %d bytes\n", aasworld.numclusters * sizeof(aas_cluster_t));
277 
278 	optimized = aasworld.numplanes * sizeof(aas_plane_t) +
279 					aasworld.numareas * sizeof(aas_area_t) +
280 					aasworld.numareasettings * sizeof(aas_areasettings_t) +
281 					aasworld.numnodes * sizeof(aas_node_t) +
282 					aasworld.reachabilitysize * sizeof(aas_reachability_t) +
283 					aasworld.numportals * sizeof(aas_portal_t) +
284 					aasworld.numclusters * sizeof(aas_cluster_t);
285 	botimport.Print(PRT_MESSAGE, "optimzed size %d KB\n", optimized >> 10);
286 } //end of the function AAS_FileInfo
287 #endif //AASFILEDEBUG
288 //===========================================================================
289 // allocate memory and read a lump of a AAS file
290 //
291 // Parameter:				-
292 // Returns:					-
293 // Changes Globals:		-
294 //===========================================================================
AAS_LoadAASLump(fileHandle_t fp,int offset,int length,int * lastoffset,int size)295 char *AAS_LoadAASLump(fileHandle_t fp, int offset, int length, int *lastoffset, int size)
296 {
297 	char *buf;
298 	//
299 	if (!length)
300 	{
301 		//just alloc a dummy
302 		return (char *) GetClearedHunkMemory(size+1);
303 	} //end if
304 	//seek to the data
305 	if (offset != *lastoffset)
306 	{
307 		botimport.Print(PRT_WARNING, "AAS file not sequentially read\n");
308 		if (botimport.FS_Seek(fp, offset, FS_SEEK_SET))
309 		{
310 			AAS_Error("can't seek to aas lump\n");
311 			AAS_DumpAASData();
312 			botimport.FS_FCloseFile(fp);
313 			return 0;
314 		} //end if
315 	} //end if
316 	//allocate memory
317 	buf = (char *) GetClearedHunkMemory(length+1);
318 	//read the data
319 	if (length)
320 	{
321 		botimport.FS_Read(buf, length, fp );
322 		*lastoffset += length;
323 	} //end if
324 	return buf;
325 } //end of the function AAS_LoadAASLump
326 //===========================================================================
327 //
328 // Parameter:			-
329 // Returns:				-
330 // Changes Globals:		-
331 //===========================================================================
AAS_DData(unsigned char * data,int size)332 void AAS_DData(unsigned char *data, int size)
333 {
334 	int i;
335 
336 	for (i = 0; i < size; i++)
337 	{
338 		data[i] ^= (unsigned char) i * 119;
339 	} //end for
340 } //end of the function AAS_DData
341 //===========================================================================
342 // load an aas file
343 //
344 // Parameter:			-
345 // Returns:				-
346 // Changes Globals:		-
347 //===========================================================================
AAS_LoadAASFile(char * filename)348 int AAS_LoadAASFile(char *filename)
349 {
350 	fileHandle_t fp;
351 	aas_header_t header;
352 	int offset, length, lastoffset;
353 
354 	botimport.Print(PRT_MESSAGE, "trying to load %s\n", filename);
355 	//dump current loaded aas file
356 	AAS_DumpAASData();
357 	//open the file
358 	botimport.FS_FOpenFile( filename, &fp, FS_READ );
359 	if (!fp)
360 	{
361 		AAS_Error("can't open %s\n", filename);
362 		return BLERR_CANNOTOPENAASFILE;
363 	} //end if
364 	//read the header
365 	botimport.FS_Read(&header, sizeof(aas_header_t), fp );
366 	lastoffset = sizeof(aas_header_t);
367 	//check header identification
368 	header.ident = LittleLong(header.ident);
369 	if (header.ident != AASID)
370 	{
371 		AAS_Error("%s is not an AAS file\n", filename);
372 		botimport.FS_FCloseFile(fp);
373 		return BLERR_WRONGAASFILEID;
374 	} //end if
375 	//check the version
376 	header.version = LittleLong(header.version);
377 	//
378 	if (header.version != AASVERSION_OLD && header.version != AASVERSION)
379 	{
380 		AAS_Error("aas file %s is version %i, not %i\n", filename, header.version, AASVERSION);
381 		botimport.FS_FCloseFile(fp);
382 		return BLERR_WRONGAASFILEVERSION;
383 	} //end if
384 	//
385 	if (header.version == AASVERSION)
386 	{
387 		AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8);
388 	} //end if
389 	//
390 	aasworld.bspchecksum = atoi(LibVarGetString( "sv_mapChecksum"));
391 	if (LittleLong(header.bspchecksum) != aasworld.bspchecksum)
392 	{
393 		AAS_Error("aas file %s is out of date\n", filename);
394 		botimport.FS_FCloseFile(fp);
395 		return BLERR_WRONGAASFILEVERSION;
396 	} //end if
397 	//load the lumps:
398 	//bounding boxes
399 	offset = LittleLong(header.lumps[AASLUMP_BBOXES].fileofs);
400 	length = LittleLong(header.lumps[AASLUMP_BBOXES].filelen);
401 	aasworld.bboxes = (aas_bbox_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_bbox_t));
402 	aasworld.numbboxes = length / sizeof(aas_bbox_t);
403 	if (aasworld.numbboxes && !aasworld.bboxes) return BLERR_CANNOTREADAASLUMP;
404 	//vertexes
405 	offset = LittleLong(header.lumps[AASLUMP_VERTEXES].fileofs);
406 	length = LittleLong(header.lumps[AASLUMP_VERTEXES].filelen);
407 	aasworld.vertexes = (aas_vertex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_vertex_t));
408 	aasworld.numvertexes = length / sizeof(aas_vertex_t);
409 	if (aasworld.numvertexes && !aasworld.vertexes) return BLERR_CANNOTREADAASLUMP;
410 	//planes
411 	offset = LittleLong(header.lumps[AASLUMP_PLANES].fileofs);
412 	length = LittleLong(header.lumps[AASLUMP_PLANES].filelen);
413 	aasworld.planes = (aas_plane_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_plane_t));
414 	aasworld.numplanes = length / sizeof(aas_plane_t);
415 	if (aasworld.numplanes && !aasworld.planes) return BLERR_CANNOTREADAASLUMP;
416 	//edges
417 	offset = LittleLong(header.lumps[AASLUMP_EDGES].fileofs);
418 	length = LittleLong(header.lumps[AASLUMP_EDGES].filelen);
419 	aasworld.edges = (aas_edge_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edge_t));
420 	aasworld.numedges = length / sizeof(aas_edge_t);
421 	if (aasworld.numedges && !aasworld.edges) return BLERR_CANNOTREADAASLUMP;
422 	//edgeindex
423 	offset = LittleLong(header.lumps[AASLUMP_EDGEINDEX].fileofs);
424 	length = LittleLong(header.lumps[AASLUMP_EDGEINDEX].filelen);
425 	aasworld.edgeindex = (aas_edgeindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edgeindex_t));
426 	aasworld.edgeindexsize = length / sizeof(aas_edgeindex_t);
427 	if (aasworld.edgeindexsize && !aasworld.edgeindex) return BLERR_CANNOTREADAASLUMP;
428 	//faces
429 	offset = LittleLong(header.lumps[AASLUMP_FACES].fileofs);
430 	length = LittleLong(header.lumps[AASLUMP_FACES].filelen);
431 	aasworld.faces = (aas_face_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_face_t));
432 	aasworld.numfaces = length / sizeof(aas_face_t);
433 	if (aasworld.numfaces && !aasworld.faces) return BLERR_CANNOTREADAASLUMP;
434 	//faceindex
435 	offset = LittleLong(header.lumps[AASLUMP_FACEINDEX].fileofs);
436 	length = LittleLong(header.lumps[AASLUMP_FACEINDEX].filelen);
437 	aasworld.faceindex = (aas_faceindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_faceindex_t));
438 	aasworld.faceindexsize = length / sizeof(aas_faceindex_t);
439 	if (aasworld.faceindexsize && !aasworld.faceindex) return BLERR_CANNOTREADAASLUMP;
440 	//convex areas
441 	offset = LittleLong(header.lumps[AASLUMP_AREAS].fileofs);
442 	length = LittleLong(header.lumps[AASLUMP_AREAS].filelen);
443 	aasworld.areas = (aas_area_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_area_t));
444 	aasworld.numareas = length / sizeof(aas_area_t);
445 	if (aasworld.numareas && !aasworld.areas) return BLERR_CANNOTREADAASLUMP;
446 	//area settings
447 	offset = LittleLong(header.lumps[AASLUMP_AREASETTINGS].fileofs);
448 	length = LittleLong(header.lumps[AASLUMP_AREASETTINGS].filelen);
449 	aasworld.areasettings = (aas_areasettings_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_areasettings_t));
450 	aasworld.numareasettings = length / sizeof(aas_areasettings_t);
451 	if (aasworld.numareasettings && !aasworld.areasettings) return BLERR_CANNOTREADAASLUMP;
452 	//reachability list
453 	offset = LittleLong(header.lumps[AASLUMP_REACHABILITY].fileofs);
454 	length = LittleLong(header.lumps[AASLUMP_REACHABILITY].filelen);
455 	aasworld.reachability = (aas_reachability_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_reachability_t));
456 	aasworld.reachabilitysize = length / sizeof(aas_reachability_t);
457 	if (aasworld.reachabilitysize && !aasworld.reachability) return BLERR_CANNOTREADAASLUMP;
458 	//nodes
459 	offset = LittleLong(header.lumps[AASLUMP_NODES].fileofs);
460 	length = LittleLong(header.lumps[AASLUMP_NODES].filelen);
461 	aasworld.nodes = (aas_node_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_node_t));
462 	aasworld.numnodes = length / sizeof(aas_node_t);
463 	if (aasworld.numnodes && !aasworld.nodes) return BLERR_CANNOTREADAASLUMP;
464 	//cluster portals
465 	offset = LittleLong(header.lumps[AASLUMP_PORTALS].fileofs);
466 	length = LittleLong(header.lumps[AASLUMP_PORTALS].filelen);
467 	aasworld.portals = (aas_portal_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portal_t));
468 	aasworld.numportals = length / sizeof(aas_portal_t);
469 	if (aasworld.numportals && !aasworld.portals) return BLERR_CANNOTREADAASLUMP;
470 	//cluster portal index
471 	offset = LittleLong(header.lumps[AASLUMP_PORTALINDEX].fileofs);
472 	length = LittleLong(header.lumps[AASLUMP_PORTALINDEX].filelen);
473 	aasworld.portalindex = (aas_portalindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portalindex_t));
474 	aasworld.portalindexsize = length / sizeof(aas_portalindex_t);
475 	if (aasworld.portalindexsize && !aasworld.portalindex) return BLERR_CANNOTREADAASLUMP;
476 	//clusters
477 	offset = LittleLong(header.lumps[AASLUMP_CLUSTERS].fileofs);
478 	length = LittleLong(header.lumps[AASLUMP_CLUSTERS].filelen);
479 	aasworld.clusters = (aas_cluster_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_cluster_t));
480 	aasworld.numclusters = length / sizeof(aas_cluster_t);
481 	if (aasworld.numclusters && !aasworld.clusters) return BLERR_CANNOTREADAASLUMP;
482 	//swap everything
483 	AAS_SwapAASData();
484 	//aas file is loaded
485 	aasworld.loaded = qtrue;
486 	//close the file
487 	botimport.FS_FCloseFile(fp);
488 	//
489 #ifdef AASFILEDEBUG
490 	AAS_FileInfo();
491 #endif //AASFILEDEBUG
492 	//
493 	return BLERR_NOERROR;
494 } //end of the function AAS_LoadAASFile
495 //===========================================================================
496 //
497 // Parameter:				-
498 // Returns:					-
499 // Changes Globals:		-
500 //===========================================================================
501 static int AAS_WriteAASLump_offset;
502 
AAS_WriteAASLump(fileHandle_t fp,aas_header_t * h,int lumpnum,void * data,int length)503 int AAS_WriteAASLump(fileHandle_t fp, aas_header_t *h, int lumpnum, void *data, int length)
504 {
505 	aas_lump_t *lump;
506 
507 	lump = &h->lumps[lumpnum];
508 
509 	lump->fileofs = LittleLong(AAS_WriteAASLump_offset);	//LittleLong(ftell(fp));
510 	lump->filelen = LittleLong(length);
511 
512 	if (length > 0)
513 	{
514 		botimport.FS_Write(data, length, fp );
515 	} //end if
516 
517 	AAS_WriteAASLump_offset += length;
518 
519 	return qtrue;
520 } //end of the function AAS_WriteAASLump
521 //===========================================================================
522 // aas data is useless after writing to file because it is byte swapped
523 //
524 // Parameter:				-
525 // Returns:					-
526 // Changes Globals:		-
527 //===========================================================================
AAS_WriteAASFile(char * filename)528 qboolean AAS_WriteAASFile(char *filename)
529 {
530 	aas_header_t header;
531 	fileHandle_t fp;
532 
533 	botimport.Print(PRT_MESSAGE, "writing %s\n", filename);
534 	//swap the aas data
535 	AAS_SwapAASData();
536 	//initialize the file header
537 	Com_Memset(&header, 0, sizeof(aas_header_t));
538 	header.ident = LittleLong(AASID);
539 	header.version = LittleLong(AASVERSION);
540 	header.bspchecksum = LittleLong(aasworld.bspchecksum);
541 	//open a new file
542 	botimport.FS_FOpenFile( filename, &fp, FS_WRITE );
543 	if (!fp)
544 	{
545 		botimport.Print(PRT_ERROR, "error opening %s\n", filename);
546 		return qfalse;
547 	} //end if
548 	//write the header
549 	botimport.FS_Write(&header, sizeof(aas_header_t), fp);
550 	AAS_WriteAASLump_offset = sizeof(aas_header_t);
551 	//add the data lumps to the file
552 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_BBOXES, aasworld.bboxes,
553 		aasworld.numbboxes * sizeof(aas_bbox_t))) return qfalse;
554 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_VERTEXES, aasworld.vertexes,
555 		aasworld.numvertexes * sizeof(aas_vertex_t))) return qfalse;
556 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_PLANES, aasworld.planes,
557 		aasworld.numplanes * sizeof(aas_plane_t))) return qfalse;
558 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGES, aasworld.edges,
559 		aasworld.numedges * sizeof(aas_edge_t))) return qfalse;
560 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGEINDEX, aasworld.edgeindex,
561 		aasworld.edgeindexsize * sizeof(aas_edgeindex_t))) return qfalse;
562 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACES, aasworld.faces,
563 		aasworld.numfaces * sizeof(aas_face_t))) return qfalse;
564 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACEINDEX, aasworld.faceindex,
565 		aasworld.faceindexsize * sizeof(aas_faceindex_t))) return qfalse;
566 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREAS, aasworld.areas,
567 		aasworld.numareas * sizeof(aas_area_t))) return qfalse;
568 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREASETTINGS, aasworld.areasettings,
569 		aasworld.numareasettings * sizeof(aas_areasettings_t))) return qfalse;
570 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_REACHABILITY, aasworld.reachability,
571 		aasworld.reachabilitysize * sizeof(aas_reachability_t))) return qfalse;
572 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_NODES, aasworld.nodes,
573 		aasworld.numnodes * sizeof(aas_node_t))) return qfalse;
574 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALS, aasworld.portals,
575 		aasworld.numportals * sizeof(aas_portal_t))) return qfalse;
576 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALINDEX, aasworld.portalindex,
577 		aasworld.portalindexsize * sizeof(aas_portalindex_t))) return qfalse;
578 	if (!AAS_WriteAASLump(fp, &header, AASLUMP_CLUSTERS, aasworld.clusters,
579 		aasworld.numclusters * sizeof(aas_cluster_t))) return qfalse;
580 	//rewrite the header with the added lumps
581 	botimport.FS_Seek(fp, 0, FS_SEEK_SET);
582 	AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8);
583 	botimport.FS_Write(&header, sizeof(aas_header_t), fp);
584 	//close the file
585 	botimport.FS_FCloseFile(fp);
586 	return qtrue;
587 } //end of the function AAS_WriteAASFile
588