1 /**
2 * @file
3 * @brief
4 */
5
6 /*
7 Copyright (C) 1997-2001 Id Software, Inc.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
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.
17
18 See the GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 */
25
26 #include "shared.h"
27 #include "bspfile.h"
28 #include "scriplib.h"
29 #include "../bsp.h"
30 #include <errno.h>
31
32 /**
33 * @brief Compress the routing data of a map
34 * @sa CMod_DeCompressRouting
35 * @sa CMod_LoadRouting
36 */
CompressRouting(byte * dataStart,byte * destStart,int l)37 byte* CompressRouting (byte* dataStart, byte* destStart, int l)
38 {
39 int c;
40 byte val;
41 byte* data, *dend, *dest_p;
42
43 dest_p = destStart;
44 data = dataStart;
45 dend = dataStart + l;
46
47 while (data < dend) {
48 byte* count_p;
49 if (data + 1 < dend && *data == *(data + 1)) {
50 /* repetitions */
51 val = *data++;
52 data++; /* Advance data again. The first two bytes are identical!!! */
53 c = 0; /* This means 2 bytes are the same. Total bytes the same is 2 + c */
54 /* Loop while the piece of data being looked at equals val */
55 while (data + 1 < dend && val == *(data)) {
56 if (c >= SCHAR_MAX) /* must fit into one byte */
57 break;
58 data++;
59 c++;
60 }
61 count_p = dest_p;
62 *dest_p++ = (byte)(c) | 0x80;
63 *dest_p++ = val;
64 } else {
65 /* identities */
66 count_p = dest_p++;
67 c = 0;
68 while ((data + 1 < dend && *data != *(data + 1)) || data == dend - 1) {
69 if (c >= SCHAR_MAX) /* must fit into one byte */
70 break;
71 *dest_p++ = *data++;
72 c++;
73 }
74 *count_p = (byte)c;
75 }
76 }
77
78 /* terminate compressed data */
79 *dest_p++ = 0;
80
81 return dest_p;
82 }
83
84 /**
85 * @brief Byte swaps all data in a bsp file.
86 */
SwapBSPFile(void)87 static void SwapBSPFile (void)
88 {
89 int i, j, k;
90
91 /* models */
92 for (i = 0; i < curTile->nummodels; i++) {
93 dBspModel_t* d = &curTile->models[i];
94
95 d->firstface = LittleLong(d->firstface);
96 d->numfaces = LittleLong(d->numfaces);
97 d->headnode = LittleLong(d->headnode);
98
99 for (j = 0; j < 3; j++) {
100 d->mins[j] = LittleFloat(d->mins[j]);
101 d->maxs[j] = LittleFloat(d->maxs[j]);
102 d->origin[j] = LittleFloat(d->origin[j]);
103 }
104 }
105
106 /* vertexes */
107 for (i = 0; i < curTile->numvertexes; i++) {
108 dBspVertex_t* vertexes = &curTile->vertexes[i];
109 for (j = 0; j < 3; j++)
110 vertexes->point[j] = LittleFloat(vertexes->point[j]);
111 }
112
113 /* planes */
114 for (i = 0; i < curTile->numplanes; i++) {
115 dBspPlane_t* plane = &curTile->planes[i];
116 for (j = 0; j < 3; j++)
117 plane->normal[j] = LittleFloat(plane->normal[j]);
118 plane->dist = LittleFloat(plane->dist);
119 plane->type = LittleLong(plane->type);
120 }
121
122 /* texinfos */
123 for (i = 0; i < curTile->numtexinfo; i++) {
124 dBspTexinfo_t* texinfo = &curTile->texinfo[i];
125 for (j = 0; j < 2; j++)
126 for (k = 0; k < 4; k++)
127 texinfo->vecs[j][k] = LittleFloat(texinfo->vecs[j][k]);
128 texinfo->surfaceFlags = LittleLong(texinfo->surfaceFlags);
129 texinfo->value = LittleLong(texinfo->value);
130 }
131
132 /* faces */
133 for (i = 0; i < curTile->numfaces; i++) {
134 dBspSurface_t* face = &curTile->faces[i];
135 face->texinfo = LittleShort(face->texinfo);
136 face->planenum = LittleShort(face->planenum);
137 face->side = LittleShort(face->side);
138 for (j = 0; j < LIGHTMAP_MAX; j++)
139 face->lightofs[j] = LittleLong(face->lightofs[j]);
140 face->firstedge = LittleLong(face->firstedge);
141 face->numedges = LittleShort(face->numedges);
142 }
143
144 /* nodes */
145 for (i = 0; i < curTile->numnodes; i++) {
146 dBspNode_t* node = &curTile->nodes[i];
147 /* planenum might be -1 here - special case for pathfinding nodes */
148 node->planenum = LittleLong(node->planenum);
149 for (j = 0; j < 3; j++) {
150 node->mins[j] = LittleShort(node->mins[j]);
151 node->maxs[j] = LittleShort(node->maxs[j]);
152 }
153 node->children[0] = LittleLong(node->children[0]);
154 node->children[1] = LittleLong(node->children[1]);
155 node->firstface = LittleShort(node->firstface);
156 node->numfaces = LittleShort(node->numfaces);
157 }
158
159 /* leafs */
160 for (i = 0; i < curTile->numleafs; i++) {
161 dBspLeaf_t* leaf = &curTile->leafs[i];
162 leaf->contentFlags = LittleLong(leaf->contentFlags);
163 leaf->area = LittleShort(leaf->area);
164 for (j = 0; j < 3; j++) {
165 leaf->mins[j] = LittleShort(leaf->mins[j]);
166 leaf->maxs[j] = LittleShort(leaf->maxs[j]);
167 }
168
169 leaf->firstleafbrush = LittleShort(leaf->firstleafbrush);
170 leaf->numleafbrushes = LittleShort(leaf->numleafbrushes);
171 }
172
173 /* leafbrushes */
174 for (i = 0; i < curTile->numleafbrushes; i++)
175 curTile->leafbrushes[i] = LittleShort(curTile->leafbrushes[i]);
176
177 /* surfedges */
178 for (i = 0; i < curTile->numsurfedges; i++)
179 curTile->surfedges[i] = LittleLong(curTile->surfedges[i]);
180
181 /* edges */
182 for (i = 0; i < curTile->numedges; i++) {
183 dBspEdge_t* edge = &curTile->edges[i];
184 edge->v[0] = LittleShort(edge->v[0]);
185 edge->v[1] = LittleShort(edge->v[1]);
186 }
187
188 /* dbrushes */
189 for (i = 0; i < curTile->numbrushes; i++) {
190 dBspBrush_t* dbrush = &curTile->dbrushes[i];
191 dbrush->firstbrushside = LittleLong(dbrush->firstbrushside);
192 dbrush->numsides = LittleLong(dbrush->numsides);
193 dbrush->contentFlags = LittleLong(dbrush->contentFlags);
194 }
195
196 /* brushes */
197 for (i = 0; i < curTile->numbrushes; i++) {
198 cBspBrush_t* cbrush = &curTile->brushes[i];
199 cbrush->firstbrushside = LittleLong(cbrush->firstbrushside);
200 cbrush->numsides = LittleLong(cbrush->numsides);
201 cbrush->contentFlags = LittleLong(cbrush->contentFlags);
202 }
203
204 /* brushsides */
205 for (i = 0; i < curTile->numbrushsides; i++) {
206 dBspBrushSide_t* brushSide = &curTile->brushsides[i];
207 brushSide->planenum = LittleShort(brushSide->planenum);
208 brushSide->texinfo = LittleShort(brushSide->texinfo);
209 }
210 }
211
CopyLump(const dBspHeader_t * header,int lumpIdx,void * dest,size_t size)212 static uint32_t CopyLump (const dBspHeader_t* header, int lumpIdx, void* dest, size_t size)
213 {
214 const lump_t* lump = &header->lumps[lumpIdx];
215 const uint32_t length = lump->filelen;
216 const uint32_t ofs = lump->fileofs;
217
218 if (length == 0)
219 return 0;
220 if (length % size)
221 Sys_Error("LoadBSPFile: odd lump size");
222
223 memcpy(dest, (const byte*)header + ofs, length);
224
225 return length / size;
226 }
227
228 /**
229 * @sa WriteBSPFile
230 */
LoadBSPFile(const char * filename)231 dMapTile_t* LoadBSPFile (const char* filename)
232 {
233 int size;
234 unsigned int i;
235 dBspHeader_t* header;
236
237 /* Create this shortcut to mapTiles[0] */
238 curTile = &mapTiles.mapTiles[0];
239 /* Set the number of tiles to 1. */
240 mapTiles.numTiles = 1;
241
242 /* load the file header */
243 size = FS_LoadFile(filename, (byte**)&header);
244 if (size == -1)
245 Sys_Error("'%s' doesn't exist", filename);
246
247 /* swap the header */
248 BSP_SwapHeader(header, filename);
249
250 if (header->ident != IDBSPHEADER)
251 Sys_Error("%s is not a IBSP file", filename);
252 if (header->version != BSPVERSION)
253 Sys_Error("%s is version %i, not %i", filename, header->version, BSPVERSION);
254
255 curTile->nummodels = CopyLump(header, LUMP_MODELS, curTile->models, sizeof(dBspModel_t));
256 curTile->numvertexes = CopyLump(header, LUMP_VERTEXES, curTile->vertexes, sizeof(dBspVertex_t));
257 curTile->numplanes = CopyLump(header, LUMP_PLANES, curTile->planes, sizeof(dBspPlane_t));
258 curTile->numleafs = CopyLump(header, LUMP_LEAFS, curTile->leafs, sizeof(dBspLeaf_t));
259 curTile->numnormals = CopyLump(header, LUMP_NORMALS, curTile->normals, sizeof(dBspNormal_t));
260 curTile->numnodes = CopyLump(header, LUMP_NODES, curTile->nodes, sizeof(dBspNode_t));
261 curTile->numtexinfo = CopyLump(header, LUMP_TEXINFO, curTile->texinfo, sizeof(dBspTexinfo_t));
262 curTile->numfaces = CopyLump(header, LUMP_FACES, curTile->faces, sizeof(dBspSurface_t));
263 curTile->numleafbrushes = CopyLump(header, LUMP_LEAFBRUSHES, curTile->leafbrushes, sizeof(curTile->leafbrushes[0]));
264 curTile->numsurfedges = CopyLump(header, LUMP_SURFEDGES, curTile->surfedges, sizeof(curTile->surfedges[0]));
265 curTile->numedges = CopyLump(header, LUMP_EDGES, curTile->edges, sizeof(dBspEdge_t));
266 curTile->numbrushes = CopyLump(header, LUMP_BRUSHES, curTile->dbrushes, sizeof(dBspBrush_t));
267 curTile->numbrushsides = CopyLump(header, LUMP_BRUSHSIDES, curTile->brushsides, sizeof(dBspBrushSide_t));
268 curTile->routedatasize = CopyLump(header, LUMP_ROUTING, curTile->routedata, 1);
269 curTile->lightdatasize[LIGHTMAP_NIGHT] = CopyLump(header, LUMP_LIGHTING_NIGHT, curTile->lightdata[LIGHTMAP_NIGHT], 1);
270 curTile->lightdatasize[LIGHTMAP_DAY] = CopyLump(header, LUMP_LIGHTING_DAY, curTile->lightdata[LIGHTMAP_DAY], 1);
271 curTile->entdatasize = CopyLump(header, LUMP_ENTITIES, curTile->entdata, 1);
272
273 /* Because the tracing functions use cBspBrush_t and not dBspBrush_t,
274 * copy data from curTile->dbrushes into curTile->cbrushes */
275 OBJZERO(curTile->brushes);
276 for (i = 0; i < curTile->numbrushes; i++) {
277 dBspBrush_t* dbrush = &curTile->dbrushes[i];
278 cBspBrush_t* brush = &curTile->brushes[i];
279 brush->firstbrushside = dbrush->firstbrushside;
280 brush->numsides = dbrush->numsides;
281 brush->contentFlags = dbrush->contentFlags;
282 }
283
284 /* everything has been copied out */
285 FS_FreeFile(header);
286
287 /* swap everything */
288 SwapBSPFile();
289
290 return curTile;
291 }
292
293 /**
294 * @sa WriteBSPFile
295 * @todo Implement this without the ftell stuff - don't write the bsp file twice
296 */
AddLump(qFILE * bspfile,dBspHeader_t * header,int lumpnum,void * data,int len)297 static void AddLump (qFILE *bspfile, dBspHeader_t* header, int lumpnum, void* data, int len)
298 {
299 lump_t* lump;
300 long offset;
301
302 lump = &header->lumps[lumpnum];
303
304 offset = ftell(bspfile->f);
305 if (offset == -1) {
306 Sys_Error("Overflow in AddLump for lump %i (%s) %s", lumpnum, bspfile->name, strerror(errno));
307 }
308 lump->fileofs = LittleLong(offset);
309 lump->filelen = LittleLong(len);
310 /* 4 byte align */
311 FS_Write(data, (len + 3) &~ 3, bspfile);
312 }
313
314 /**
315 * @brief Swaps the bsp file in place, so it should not be referenced again
316 * @sa LoadBSPFile
317 */
WriteBSPFile(const char * filename)318 long WriteBSPFile (const char* filename)
319 {
320 dBspHeader_t outheader;
321 long size;
322
323 OBJZERO(outheader);
324
325 SwapBSPFile();
326
327 outheader.ident = LittleLong(IDBSPHEADER);
328 outheader.version = LittleLong(BSPVERSION);
329
330 ScopedFile bspfile;
331 FS_OpenFile(filename, &bspfile, FILE_WRITE);
332 if (!bspfile)
333 Sys_Error("Could not write bsp file");
334 FS_Write(&outheader, sizeof(outheader), &bspfile); /* overwritten later */
335
336 AddLump(&bspfile, &outheader, LUMP_PLANES, curTile->planes, curTile->numplanes * sizeof(dBspPlane_t));
337 AddLump(&bspfile, &outheader, LUMP_LEAFS, curTile->leafs, curTile->numleafs * sizeof(dBspLeaf_t));
338 AddLump(&bspfile, &outheader, LUMP_VERTEXES, curTile->vertexes, curTile->numvertexes * sizeof(dBspVertex_t));
339 AddLump(&bspfile, &outheader, LUMP_NORMALS, curTile->normals, curTile->numnormals * sizeof(dBspNormal_t));
340 AddLump(&bspfile, &outheader, LUMP_NODES, curTile->nodes, curTile->numnodes * sizeof(dBspNode_t));
341 AddLump(&bspfile, &outheader, LUMP_TEXINFO, curTile->texinfo, curTile->numtexinfo * sizeof(dBspTexinfo_t));
342 AddLump(&bspfile, &outheader, LUMP_FACES, curTile->faces, curTile->numfaces * sizeof(dBspSurface_t));
343 AddLump(&bspfile, &outheader, LUMP_BRUSHES, curTile->dbrushes, curTile->numbrushes * sizeof(dBspBrush_t));
344 AddLump(&bspfile, &outheader, LUMP_BRUSHSIDES, curTile->brushsides, curTile->numbrushsides * sizeof(dBspBrushSide_t));
345 AddLump(&bspfile, &outheader, LUMP_LEAFBRUSHES, curTile->leafbrushes, curTile->numleafbrushes * sizeof(curTile->leafbrushes[0]));
346 AddLump(&bspfile, &outheader, LUMP_SURFEDGES, curTile->surfedges, curTile->numsurfedges * sizeof(curTile->surfedges[0]));
347 AddLump(&bspfile, &outheader, LUMP_EDGES, curTile->edges, curTile->numedges * sizeof(dBspEdge_t));
348 AddLump(&bspfile, &outheader, LUMP_MODELS, curTile->models, curTile->nummodels * sizeof(dBspModel_t));
349 AddLump(&bspfile, &outheader, LUMP_LIGHTING_NIGHT, curTile->lightdata[0], curTile->lightdatasize[0]);
350 AddLump(&bspfile, &outheader, LUMP_LIGHTING_DAY, curTile->lightdata[1], curTile->lightdatasize[1]);
351 AddLump(&bspfile, &outheader, LUMP_ROUTING, curTile->routedata, curTile->routedatasize);
352 AddLump(&bspfile, &outheader, LUMP_ENTITIES, curTile->entdata, curTile->entdatasize);
353 size = ftell(bspfile.getFile());
354
355 fseek(bspfile.getFile(), 0L, SEEK_SET);
356 FS_Write(&outheader, sizeof(outheader), &bspfile);
357
358 SwapBSPFile();
359
360 return size;
361 }
362
363 /**
364 * @brief Dumps info about current file
365 */
PrintBSPFileSizes(void)366 void PrintBSPFileSizes (void)
367 {
368 if (!num_entities)
369 ParseEntities();
370
371 Com_Printf("amount type size in bytes\n");
372 Com_Printf("================================\n");
373 Com_Printf("%5i models %7i\n", curTile->nummodels, (int)(curTile->nummodels * sizeof(cBspModel_t)));
374 Com_Printf("%5i brushes %7i\n", curTile->numbrushes, (int)(curTile->numbrushes * sizeof(dBspBrush_t)));
375 Com_Printf("%5i brushsides %7i\n", curTile->numbrushsides, (int)(curTile->numbrushsides * sizeof(dBspBrushSide_t)));
376 Com_Printf("%5i planes %7i\n", curTile->numplanes, (int)(curTile->numplanes * sizeof(dBspPlane_t)));
377 Com_Printf("%5i texinfo %7i\n", curTile->numtexinfo, (int)(curTile->numtexinfo * sizeof(dBspTexinfo_t)));
378 Com_Printf("%5i entdata %7i\n", num_entities, curTile->entdatasize);
379
380 Com_Printf("\n");
381
382 Com_Printf("%5i normales %7i\n", curTile->numnormals, (int)(curTile->numnormals * sizeof(dBspNormal_t)));
383 Com_Printf("%5i vertexes %7i\n", curTile->numvertexes, (int)(curTile->numvertexes * sizeof(dBspVertex_t)));
384 Com_Printf("%5i nodes %7i\n", curTile->numnodes, (int)(curTile->numnodes * sizeof(dBspNode_t)));
385 Com_Printf("%5i faces %7i\n", curTile->numfaces, (int)(curTile->numfaces * sizeof(dBspSurface_t)));
386 Com_Printf("%5i leafs %7i\n", curTile->numleafs, (int)(curTile->numleafs * sizeof(dBspLeaf_t)));
387 Com_Printf("%5i leafbrushes %7i\n", curTile->numleafbrushes, (int)(curTile->numleafbrushes * sizeof(curTile->leafbrushes[0])));
388 Com_Printf("%5i surfedges %7i\n", curTile->numsurfedges, (int)(curTile->numsurfedges * sizeof(curTile->surfedges[0])));
389 Com_Printf("%5i edges %7i\n", curTile->numedges, (int)(curTile->numedges * sizeof(dBspEdge_t)));
390 Com_Printf("night lightdata %7i\n", curTile->lightdatasize[0]);
391 Com_Printf(" day lightdata %7i\n", curTile->lightdatasize[1]);
392 Com_Printf(" routedata %7i\n", curTile->routedatasize);
393 }
394
395
396 int num_entities;
397 entity_t entities[MAX_MAP_ENTITIES];
398
399 /**
400 * @brief Removes trailing whitespaces from the given string
401 * @param[in,out] str The string to clean up
402 * @note Whitespaces are converted to \\0
403 */
StripTrailingWhitespaces(char * str)404 static void StripTrailingWhitespaces (char* str)
405 {
406 char* s;
407
408 s = str + strlen(str) - 1;
409 while (s >= str && *s <= ' ') {
410 *s = '\0';
411 s--;
412 }
413 }
414
IsInvalidEntityToken(const char * token)415 static inline bool IsInvalidEntityToken (const char* token)
416 {
417 return Q_streq(token, "}") || Q_streq(token, "{");
418 }
419
AddEpair(const char * key,const char * value,int entNum)420 epair_t* AddEpair (const char* key, const char* value, int entNum)
421 {
422 epair_t *e = Mem_AllocType(epair_t);
423
424 if (IsInvalidEntityToken(value) || IsInvalidEntityToken(key))
425 Sys_Error("Invalid entity %i found with key '%s' and value '%s'", entNum, key, value);
426
427 if (strlen(key) >= MAX_KEY - 1)
428 Sys_Error("ParseEpar: token too long");
429 e->key = key;
430 if (strlen(value) >= MAX_VALUE - 1)
431 Sys_Error("ParseEpar: token too long");
432 e->value = value;
433
434 return e;
435 }
436
437 /**
438 * @brief Parses one key and value for an entity from the current tokens
439 * @sa parsedToken
440 * @sa GetToken
441 * @sa ParseEntity
442 * @sa ParseMapEntity
443 */
ParseEpair(int entNum)444 epair_t* ParseEpair (int entNum)
445 {
446 StripTrailingWhitespaces(parsedToken);
447 const char* key = Mem_StrDup(parsedToken);
448 GetToken();
449 StripTrailingWhitespaces(parsedToken);
450 const char* value = Mem_StrDup(parsedToken);
451 return AddEpair(key, value, entNum);
452 }
453
454 /**
455 * @sa ParseEntities
456 */
ParseEntity(void)457 static entity_t* ParseEntity (void)
458 {
459 entity_t* mapent;
460
461 if (Q_strnull(GetToken()))
462 return nullptr;
463
464 if (parsedToken[0] != '{')
465 Sys_Error("ParseEntity: { not found");
466
467 if (num_entities >= MAX_MAP_ENTITIES)
468 Sys_Error("num_entities >= MAX_MAP_ENTITIES (%i)", num_entities);
469
470 mapent = &entities[num_entities];
471 num_entities++;
472
473 do {
474 if (Q_strnull(GetToken()))
475 Sys_Error("ParseEntity: EOF without closing brace");
476 if (*parsedToken == '}') {
477 break;
478 } else {
479 epair_t* e = ParseEpair(num_entities);
480 e->next = mapent->epairs;
481 mapent->epairs = e;
482 }
483 } while (1);
484
485 return mapent;
486 }
487
488 /**
489 * @brief Parses the curTile->entdata string into entities
490 * @sa UnparseEntities
491 * @sa ParseEntity
492 */
ParseEntities(void)493 void ParseEntities (void)
494 {
495 num_entities = 0;
496 ParseFromMemory(curTile->entdata, curTile->entdatasize);
497
498 while (ParseEntity() != nullptr) {
499 }
500 }
501
502 /**
503 * @brief Generates the curTile->entdata string from all the entities
504 * @sa ParseEntities
505 */
UnparseEntities(void)506 const char* UnparseEntities (void)
507 {
508 char key[1024];
509 char value[1024];
510
511 curTile->entdata[0] = '\0';
512
513 for (int i = 0; i < num_entities; i++) {
514 const epair_t* ep = entities[i].epairs;
515 if (!ep)
516 continue; /* ent got removed */
517
518 Q_strcat(curTile->entdata, sizeof(curTile->entdata), "{\n");
519
520 for (ep = entities[i].epairs; ep; ep = ep->next) {
521 Q_strncpyz(key, ep->key, sizeof(key));
522 StripTrailingWhitespaces(key);
523 Q_strncpyz(value, ep->value, sizeof(value));
524 StripTrailingWhitespaces(value);
525
526 if (IsInvalidEntityToken(value) || IsInvalidEntityToken(key))
527 Sys_Error("Invalid entity %i found with key '%s' and value '%s'", i, key, value);
528 Q_strcat(curTile->entdata, sizeof(curTile->entdata), "\"%s\" \"%s\"\n", key, value);
529 }
530 Q_strcat(curTile->entdata, sizeof(curTile->entdata), "}\n");
531 }
532 curTile->entdatasize = strlen(curTile->entdata);
533
534 return curTile->entdata;
535 }
536
SetKeyValue(entity_t * ent,const char * key,const char * value)537 void SetKeyValue (entity_t* ent, const char* key, const char* value)
538 {
539 epair_t* ep;
540
541 for (ep = ent->epairs; ep; ep = ep->next)
542 if (Q_streq(ep->key, key)) {
543 ep->value = Mem_StrDup(value);
544 return;
545 }
546 ep = Mem_AllocType(epair_t);
547 ep->next = ent->epairs;
548 ent->epairs = ep;
549 ep->key = Mem_StrDup(key);
550 ep->value = Mem_StrDup(value);
551 }
552
ValueForKey(const entity_t * ent,const char * key)553 const char* ValueForKey (const entity_t* ent, const char* key)
554 {
555 const epair_t* ep;
556
557 for (ep = ent->epairs; ep; ep = ep->next)
558 if (Q_streq(ep->key, key))
559 return ep->value;
560 return "";
561 }
562
FloatForKey(const entity_t * ent,const char * key)563 vec_t FloatForKey (const entity_t* ent, const char* key)
564 {
565 const char* k;
566
567 k = ValueForKey(ent, key);
568 return atof(k);
569 }
570
571 /**
572 * @brief Converts a string into a @c vec3_t
573 */
GetVectorFromString(const char * value,vec3_t vec)574 void GetVectorFromString (const char* value, vec3_t vec)
575 {
576 if (value[0] != '\0') {
577 double v1, v2, v3;
578
579 /* scanf into doubles, then assign, so it is vec_t size independent */
580 v1 = v2 = v3 = 0;
581 if (sscanf(value, "%lf %lf %lf", &v1, &v2, &v3) != 3)
582 Sys_Error("invalid vector statement given: '%s'", value);
583 VectorSet(vec, v1, v2, v3);
584 } else
585 VectorClear(vec);
586 }
587
588 /**
589 * @brief Converts the value of a entity parameter into a @c vec3_t
590 */
GetVectorForKey(const entity_t * ent,const char * key,vec3_t vec)591 void GetVectorForKey (const entity_t* ent, const char* key, vec3_t vec)
592 {
593 const char* k = ValueForKey(ent, key);
594 GetVectorFromString(k, vec);
595 }
596