1 /***************************************************************************
2                       rttrack.cpp -- Track utilities functions
3                              -------------------
4     created              : Sat Aug 14 23:03:22 CEST 1999
5     copyright            : (C) 1999-2014 by Eric Espie, Bernhard Wymann
6     email                : torcs@free.fr
7     version              : $Id: rttrack.cpp,v 1.21.2.16 2014/05/20 14:07:08 berniw Exp $
8  ***************************************************************************/
9 
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  ***************************************************************************/
18 
19 /** @file
20     Common functions for robots.
21     @author Bernhard Wymann, Eric Espie
22     @version	$Id: rttrack.cpp,v 1.21.2.16 2014/05/20 14:07:08 berniw Exp $
23 */
24 
25 /** @defgroup tracktools Robot Track Tools API
26     API to gather information about the track and the cars state relative to the track.
27     @ingroup robottools
28 */
29 
30 /** @defgroup setuptools Robot Setup Tools API
31     API to load and store car setups in a unified way, and to support setup changes during pit stops.
32     @ingroup robottools
33 */
34 
35 
36 #include <portability.h>
37 #include <stdlib.h>
38 #include <math.h>
39 #ifdef WIN32
40 #include <windows.h>
41 #endif
42 #include <tgf.h>
43 #include <car.h>
44 #include <track.h>
45 
46 #include <robottools.h>
47 
48 /** Get the track width at the specified point.
49     @ingroup	tracktools
50     @param	seg	Segment
51     @param	toStart	Distance from the beginning of the segment.
52     		<br>The units are:
53 		- meters for straights
54 		- radians for turns
55     @return	Width of the track at this point.
56     @note	The Pit lane and the track have different width, and the side segments have variable width.
57  */
58 tdble
RtTrackGetWidth(tTrackSeg * seg,tdble toStart)59 RtTrackGetWidth(tTrackSeg *seg, tdble toStart)
60 {
61 	return fabs(seg->startWidth + toStart * seg->Kyl);
62 }
63 
64 
65 /** Convert a Local position (segment, toRight, toStart)
66     @ingroup	tracktools
67     into a Global one (X, Y)
68     The ToStart position refers to the current segment,
69     the function will not search for next segment if toStart
70     is greater than the segment length.
71     toStart represent an angle in radian for curves
72     and a length in meters for straights.
73     @param	p	Local position
74     @param	X	returned X position
75     @param	Y	returned Y position
76     @param	flag	Local position use:
77 			- TR_TOMIDDLE the toMiddle field is used
78 			- TR_TORIGHT the toRight field is used
79 			- TR_TOLEFT the toLeft field is used
80 */
81 void
RtTrackLocal2Global(tTrkLocPos * p,tdble * X,tdble * Y,int flag)82 RtTrackLocal2Global(tTrkLocPos *p, tdble *X, tdble *Y, int flag)
83 {
84 	tdble CosA, SinA, r, a;
85 	tdble tr;
86 
87 	tTrackSeg *seg = p->seg;
88 	switch (flag) {
89 		case TR_TOMIDDLE:
90 			switch(seg->type) {
91 				case TR_STR:
92 					CosA = cos(seg->angle[TR_ZS]);
93 					SinA = sin(seg->angle[TR_ZS]);
94 					/* Jussi Pajala: must be divided by two to get middle of the track ! */
95 					tr = p->toMiddle + seg->startWidth / 2.0;
96 					*X = seg->vertex[TR_SR].x + p->toStart * CosA - tr * SinA;
97 					*Y = seg->vertex[TR_SR].y + p->toStart * SinA + tr * CosA;
98 					break;
99 
100 				case TR_LFT:
101 					a = seg->angle[TR_ZS] + p->toStart;
102 					r = seg->radius - p->toMiddle;
103 					*X = seg->center.x + r * sin(a);
104 					*Y = seg->center.y - r * cos(a);
105 					break;
106 
107 				case TR_RGT:
108 					a = seg->angle[TR_ZS] - p->toStart;
109 					r = seg->radius + p->toMiddle;
110 					*X = seg->center.x - r * sin(a);
111 					*Y = seg->center.y + r * cos(a);
112 					break;
113 
114 			}
115 			break;
116 
117 		case TR_TORIGHT:
118 			switch(seg->type) {
119 				case TR_STR:
120 					CosA = cos(seg->angle[TR_ZS]);
121 					SinA = sin(seg->angle[TR_ZS]);
122 					switch (seg->type2) {
123 						case TR_MAIN:
124 						case TR_LSIDE:
125 						case TR_LBORDER:
126 							tr = p->toRight;
127 							break;
128 						case TR_RSIDE:
129 						case TR_RBORDER:
130 							tr = p->toRight - seg->Kyl * p->toStart;
131 							break;
132 						default:
133 							tr = 0;
134 							break;
135 					}
136 					*X = seg->vertex[TR_SR].x + p->toStart * CosA - tr * SinA;
137 					*Y = seg->vertex[TR_SR].y + p->toStart * SinA + tr * CosA;
138 					break;
139 
140 				case TR_LFT:
141 					a = seg->angle[TR_ZS] + p->toStart;
142 					switch (seg->type2) {
143 						case TR_MAIN:
144 						case TR_LSIDE:
145 						case TR_LBORDER:
146 							r = seg->radiusr - p->toRight ;
147 							break;
148 						case TR_RSIDE:
149 						case TR_RBORDER:
150 							r = seg->radiusl + seg->startWidth + seg->Kyl * p->toStart - p->toRight ;
151 							break;
152 						default:
153 							r = 0;
154 							break;
155 					}
156 					*X = seg->center.x + r * sin(a);
157 					*Y = seg->center.y - r * cos(a);
158 					break;
159 
160 				case TR_RGT:
161 					a = seg->angle[TR_ZS] - p->toStart;
162 					switch (seg->type2) {
163 						case TR_MAIN:
164 						case TR_LSIDE:
165 						case TR_LBORDER:
166 							r = seg->radiusr + p->toRight ;
167 							break;
168 						case TR_RSIDE:
169 						case TR_RBORDER:
170 							r = seg->radiusl - seg->startWidth - seg->Kyl * p->toStart + p->toRight ;
171 							break;
172 						default:
173 							r = 0;
174 							break;
175 					}
176 					*X = seg->center.x - r * sin(a);
177 					*Y = seg->center.y + r * cos(a);
178 					break;
179 			}
180 			break;
181 
182 		case TR_TOLEFT:
183 			switch(seg->type) {
184 				case TR_STR:
185 					CosA = cos(seg->angle[TR_ZS]);
186 					SinA = sin(seg->angle[TR_ZS]);
187 					tr = seg->startWidth + seg->Kyl * p->toStart - p->toLeft;
188 					*X = seg->vertex[TR_SR].x + p->toStart * CosA - tr * SinA;
189 					*Y = seg->vertex[TR_SR].y + p->toStart * SinA + tr * CosA;
190 					break;
191 
192 				case TR_LFT:
193 					a = seg->angle[TR_ZS] + p->toStart;
194 					r = seg->radiusl + p->toLeft;
195 					*X = seg->center.x + r * sin(a);
196 					*Y = seg->center.y - r * cos(a);
197 					break;
198 
199 				case TR_RGT:
200 					a = seg->angle[TR_ZS] - p->toStart;
201 					r = seg->radiusr + seg->startWidth + seg->Kyl * p->toStart - p->toLeft;
202 					*X = seg->center.x - r * sin(a);
203 					*Y = seg->center.y + r * cos(a);
204 					break;
205 			}
206 			break;
207 	}
208 }
209 
210 /** Convert a Global (segment, X, Y) position into a Local one (segment, toRight, toStart)
211     @ingroup	tracktools
212     The segment in the Global position is used to start the search of a good segment
213     in term of toStart value.
214     The segments are scanned in order to find a toStart value between 0 and the length
215     of the segment for straights or the arc of the curve.
216     The sides parameters is to indicate wether to use the track sides (1) or not (0) in
217     the toRight computation.
218     @param	segment	Current segment
219     @param	X	Current X position
220     @param	Y	Current Y position
221     @param	p	Returned local position
222     @param	type	Type of local position desired:
223     			- TR_LPOS_MAIN relative to the main segment
224 			- TR_LPOS_SEGMENT if the point is on a side, relative to this side
225 			- TR_LPOS_TRACK local pos includes all the track width
226  */
227 
228 void
RtTrackGlobal2Local(tTrackSeg * segment,tdble X,tdble Y,tTrkLocPos * p,int type)229 RtTrackGlobal2Local(tTrackSeg *segment, tdble X, tdble Y, tTrkLocPos *p, int type)
230 {
231 	int segnotfound = 1;
232 	tdble x, y;
233 	tTrackSeg *seg = segment;
234 	tTrackSeg *sseg;
235 	tdble theta, a2;
236 	int depl = 0;
237 	tdble curWidth;
238 
239 	p->type = type;
240 
241 	while (segnotfound) {
242 
243 		switch(seg->type) {
244 			case TR_STR:
245 				/* rotation */
246 				tdble sine, cosine;
247 				tdble ts;
248 
249 				sine = sin(seg->angle[TR_ZS]);
250 				cosine = cos(seg->angle[TR_ZS]);
251 				x = X - seg->vertex[TR_SR].x;
252 				y = Y - seg->vertex[TR_SR].y;
253 				ts = x * cosine + y * sine;
254 				p->seg = seg;
255 				p->toStart = ts;
256 				p->toRight = y * cosine - x * sine;
257 				if ((ts < 0) && (depl < 1)) {
258 					/* get back */
259 					seg = seg->prev;
260 					depl = -1;
261 				} else if ((ts > seg->length) && (depl > -1)) {
262 					seg = seg->next;
263 					depl = 1;
264 				} else {
265 					segnotfound = 0;
266 				}
267 				break;
268 
269 			case TR_LFT:
270 				/* rectangular to polar */
271 				x = X - seg->center.x;
272 				y = Y - seg->center.y;
273 				a2 = seg->arc / 2.0;
274 				theta = atan2(y, x) - (seg->angle[TR_CS] + a2);
275 				NORM_PI_PI(theta);
276 				p->seg = seg;
277 				p->toStart = theta + a2;
278 				p->toRight = seg->radiusr - sqrt(x*x + y*y);
279 				if ((theta < -a2) && (depl < 1)) {
280 					seg = seg->prev;
281 					depl = -1;
282 				} else if ((theta > a2) && (depl > -1)) {
283 					seg = seg->next;
284 					depl = 1;
285 				} else {
286 					segnotfound = 0;
287 				}
288 				break;
289 
290 			case TR_RGT:
291 				/* rectangular to polar */
292 
293 				x = X - seg->center.x;
294 				y = Y - seg->center.y;
295 				a2 = seg->arc / 2.0;
296 				theta = seg->angle[TR_CS] - a2 - atan2(y, x);
297 				NORM_PI_PI(theta);
298 				p->seg = seg;
299 				p->toStart = theta + a2;
300 				p->toRight = sqrt(x*x + y*y) - seg->radiusr;
301 				if ((theta < -a2) && (depl < 1)) {
302 					seg = seg->prev;
303 					depl = -1;
304 				} else if ((theta > a2) && (depl > -1)) {
305 					seg = seg->next;
306 					depl = 1;
307 				} else {
308 					segnotfound = 0;
309 				}
310 				break;
311 		}
312 	}
313 
314 	/* The track is of constant width */
315 	/* This is subject to change */
316 	p->toMiddle = p->toRight - seg->width / 2.0;
317 	p->toLeft = seg->width - p->toRight;
318 
319 	/* Consider all the track with the sides */
320 	/* Stay on main segment */
321 	if (type == TR_LPOS_TRACK) {
322 		if (seg->rside != NULL) {
323 			sseg = seg->rside;
324 			p->toRight += RtTrackGetWidth(sseg, p->toStart);
325 			sseg = sseg->rside;
326 			if (sseg) {
327 				p->toRight += RtTrackGetWidth(sseg, p->toStart);
328 			}
329 		}
330 		if (seg->lside != NULL) {
331 			sseg = seg->lside;
332 			p->toLeft += RtTrackGetWidth(sseg, p->toStart);
333 			sseg = sseg->lside;
334 			if (sseg) {
335 				p->toLeft += RtTrackGetWidth(sseg, p->toStart);
336 			}
337 		}
338 	}
339 
340 	/* Relative to a segment, change to the side segment if necessary */
341 	if (type == TR_LPOS_SEGMENT) {
342 		if ((p->toRight < 0) && (seg->rside != NULL)) {
343 			sseg = seg->rside;
344 			p->seg = sseg;
345 			curWidth = RtTrackGetWidth(sseg, p->toStart);
346 			p->toRight +=  curWidth;
347 			p->toLeft -= seg->width;
348 			p->toMiddle += (seg->width + curWidth) / 2.0;
349 			if ((p->toRight < 0) && (sseg->rside != NULL)) {
350 				p->toLeft -= curWidth;
351 				p->toMiddle += curWidth / 2.0;
352 				seg = sseg;
353 				sseg = seg->rside;
354 				curWidth = RtTrackGetWidth(sseg, p->toStart);
355 				p->seg = sseg;
356 				p->toRight +=  curWidth;
357 				p->toMiddle += curWidth / 2.0;
358 			}
359 		} else if ((p->toLeft < 0) && (seg->lside != NULL)) {
360 			sseg = seg->lside;
361 			p->seg = sseg;
362 			curWidth = RtTrackGetWidth(sseg, p->toStart);
363 			p->toRight += -seg->width;
364 			p->toMiddle -= (seg->width + curWidth) / 2.0;
365 			p->toLeft += curWidth;
366 			if ((p->toLeft < 0) && (sseg->lside != NULL)) {
367 				p->toRight -= curWidth;
368 				p->toMiddle -= curWidth / 2.0;
369 				seg = sseg;
370 				sseg = seg->lside;
371 				curWidth = RtTrackGetWidth(sseg, p->toStart);
372 				p->seg = sseg;
373 				p->toMiddle -= curWidth / 2.0;
374 				p->toLeft += curWidth;
375 			}
376 		}
377 	}
378 }
379 
380 /** Returns the absolute height in meters of the road
381     at the Local position p.
382     If the point lies outside the track (and sides)
383     the height is computed using the tangent to the banking
384     of the segment (or side).
385     @verbatim
386                  + Point given
387                 .^
388                . |
389               .  |
390              .   |
391             /    | heigth
392            /     |
393     ______/      v
394     ^    ^^  ^
395     |    ||  |
396     track side
397 	@endverbatim
398     @ingroup	tracktools
399     @param	p	Local position
400     @return	Height in meters
401  */
402 tdble
RtTrackHeightL(tTrkLocPos * p)403 RtTrackHeightL(tTrkLocPos *p)
404 {
405 	tdble lg;
406 	tdble tr = p->toRight;
407 	tTrackSeg *seg = p->seg;
408 
409 	//bool left_side = true;
410 	if ((tr < 0) && (seg->rside != NULL)) {
411 		//left_side = false;
412 
413 		seg = seg->rside;
414 		tr += seg->width;
415 
416 		if ((tr < 0) && (seg->rside != NULL)) {
417 			seg = seg->rside;
418 			tr += RtTrackGetWidth(seg, p->toStart);
419 		}
420 	} else if ((tr > seg->width) && (seg->lside != NULL)) {
421 		tr -= seg->width;
422 		seg = seg->lside;
423 		if ((tr > seg->width) && (seg->lside != NULL)) {
424 			tr -= RtTrackGetWidth(seg, p->toStart);
425 			seg = seg->lside;
426 		}
427 	}
428 
429 	switch (seg->type) {
430 		case TR_STR:
431 			lg = p->toStart;
432 			break;
433 		default:
434 			lg = p->toStart * seg->radius;
435 			break;
436 	}
437 
438 	if (seg->style == TR_CURB) {
439 		// The final height = starting height + height difference due
440 		// to track angle + height difference due to curb (this seems
441 		// to be the way it is implemented in the graphics too: the
442 		// curb does not adding an angle to the main track, but a
443 		// height in global coords).
444 		if (seg->type2 == TR_RBORDER) {
445 			// alpha shows how far we've moved into this segment.
446 			tdble alpha = seg->width - tr;
447 			tdble angle = seg->angle[TR_XS] + p->toStart * seg->Kzw;
448 			tdble noise = seg->surface->kRoughness * sin(seg->surface->kRoughWaveLen * lg) * alpha / seg->width;
449 			tdble start_height = seg->vertex[TR_SR].z + p->toStart * seg->Kzl;
450 			return start_height + tr * tan(angle) + alpha * atan2(seg->height, seg->width) + noise;
451 		}
452 
453 		return
454 			seg->vertex[TR_SR].z + p->toStart * seg->Kzl +
455 			tr * (tan(seg->angle[TR_XS] + p->toStart * seg->Kzw) +
456 			atan2(seg->height, seg->width)) +
457 			seg->surface->kRoughness * sin(seg->surface->kRoughWaveLen * lg) * tr / seg->width;
458 	}
459 
460 	return seg->vertex[TR_SR].z + p->toStart * seg->Kzl + tr * tan(seg->angle[TR_XS] + p->toStart * seg->Kzw) +
461 	seg->surface->kRoughness * sin(seg->surface->kRoughWaveLen * tr) * sin(seg->surface->kRoughWaveLen * lg);
462 }
463 
464 /* get the real segment */
465 tTrackSeg *
RtTrackGetSeg(tTrkLocPos * p)466 RtTrackGetSeg(tTrkLocPos *p)
467 {
468 	tdble tr = p->toRight;
469 	tTrackSeg *seg = p->seg;
470 
471 	if ((tr < 0) && (seg->rside != NULL)) {
472 		seg = seg->rside;
473 		tr += seg->width;
474 		if ((tr < 0) && (seg->rside != NULL)) {
475 			seg = seg->rside;
476 			tr += RtTrackGetWidth(seg, p->toStart);
477 		}
478 	} else if ((tr > seg->width) && (seg->lside != NULL)) {
479 		tr -= seg->width;
480 		seg = seg->lside;
481 		if ((tr > seg->width) && (seg->lside != NULL)) {
482 			tr -= RtTrackGetWidth(seg, p->toStart);
483 			seg = seg->lside;
484 		}
485 	}
486 	return seg;
487 }
488 
489 /** Returns the absolute height in meters of the road
490     at the Global position (segment, X, Y)
491     @ingroup	tracktools
492     @param	seg	Segment
493     @param	X	Global X position
494     @param	Y	Global Y position
495     @return	Height in meters
496  */
497 tdble
RtTrackHeightG(tTrackSeg * seg,tdble X,tdble Y)498 RtTrackHeightG(tTrackSeg *seg, tdble X, tdble Y)
499 {
500 	tTrkLocPos p;
501 
502 	RtTrackGlobal2Local(seg, X, Y, &p, TR_LPOS_SEGMENT);
503 	return RtTrackHeightL(&p);
504 }
505 
506 /** Give the normal vector of the border of the track
507     including the sides.
508     The side parameter is used to indicate the right (TR_RGT)
509     of the left (TR_LFT) side to consider.
510     The Global position given (segment, X, Y) is used
511     to project the point on the border, it is not necessary
512     to give a point directly on the border itself.
513     The vector is normalized.
514     @ingroup	tracktools
515     @param	seg	Current segment
516     @param	X	Global X position
517     @param	Y	Global Y position
518     @param	side	Side where the normal is wanted
519 			- TR_LFT for left side
520 			- TR_RGT for right side
521     @param	norm	Returned normalized side normal vector
522     @todo	RtTrackSideNormalG: Give the correct normal for variable width sides.
523  */
524 void
RtTrackSideNormalG(tTrackSeg * seg,tdble X,tdble Y,int side,t3Dd * norm)525 RtTrackSideNormalG(tTrackSeg *seg, tdble X, tdble Y, int side, t3Dd *norm)
526 {
527 	tdble lg;
528 
529 	switch (seg->type) {
530 		case TR_STR:
531 			if (side == TR_RGT) {
532 				norm->x = seg->rgtSideNormal.x;
533 				norm->y = seg->rgtSideNormal.y;
534 			} else {
535 				norm->x = -seg->rgtSideNormal.x;
536 				norm->y = -seg->rgtSideNormal.y;
537 			}
538 			break;
539 		case TR_RGT:
540 			if (side == TR_LFT) {
541 				norm->x = seg->center.x - X;
542 				norm->y = seg->center.y - Y;
543 			} else {
544 				norm->x = X - seg->center.x;
545 				norm->y = Y - seg->center.y;
546 			}
547 			lg = 1.0 / sqrt(norm->x * norm->x + norm->y * norm->y);
548 			norm->x *= lg;
549 			norm->y *= lg;
550 			break;
551 		case TR_LFT:
552 			if (side == TR_RGT) {
553 				norm->x = seg->center.x - X;
554 				norm->y = seg->center.y - Y;
555 			} else {
556 				norm->x = X - seg->center.x;
557 				norm->y = Y - seg->center.y;
558 			}
559 			lg = 1.0 / sqrt(norm->x * norm->x + norm->y * norm->y);
560 			norm->x *= lg;
561 			norm->y *= lg;
562 		break;
563 	}
564 }
565 
566 /** Used to get the tangent angle for a track position
567     The angle is given in radian.
568     the angle 0 is parallel to the first segment start.
569     @ingroup	tracktools
570     @param	p	Local position
571     @return	Tagent angle in radian.
572     @note	For side segment, the track side is used for the tangent.
573  */
574 
575 tdble
RtTrackSideTgAngleL(tTrkLocPos * p)576 RtTrackSideTgAngleL(tTrkLocPos *p)
577 {
578 	switch (p->seg->type) {
579 		case TR_STR:
580 			return p->seg->angle[TR_ZS];
581 			break;
582 		case TR_RGT:
583 			return p->seg->angle[TR_ZS] - p->toStart;
584 			break;
585 		case TR_LFT:
586 			return p->seg->angle[TR_ZS] + p->toStart;
587 			break;
588 	}
589 	return 0;
590 }
591 
592 
593 /** Used to get the normal vector of the road (pointing upward).
594     Local coordinates are used to locate the point where to
595     get the road normal vector.
596     The vector is normalized.
597     @ingroup	tracktools
598     @param	p	Local position
599     @param	norm	Returned normalized road normal vector
600  */
601 void
RtTrackSurfaceNormalL(tTrkLocPos * p,t3Dd * norm)602 RtTrackSurfaceNormalL(tTrkLocPos *p, t3Dd *norm)
603 {
604 	tTrkLocPos p1;
605 	t3Dd px1, px2, py1, py2;
606 	t3Dd v1, v2;
607 	tdble lg;
608 
609 	p1.seg = p->seg;
610 
611 	p1.toStart = 0;
612 	p1.toRight = p->toRight;
613 	RtTrackLocal2Global(&p1, &px1.x, &px1.y, TR_TORIGHT);
614 	px1.z = RtTrackHeightL(&p1);
615 
616 	if (p1.seg->type == TR_STR) {
617 		p1.toStart = p1.seg->length;
618 	} else {
619 		p1.toStart = p1.seg->arc;
620 	}
621 
622 	RtTrackLocal2Global(&p1, &px2.x, &px2.y, TR_TORIGHT);
623 	px2.z = RtTrackHeightL(&p1);
624 
625 	p1.toRight = 0;
626 	p1.toStart = p->toStart;
627 	RtTrackLocal2Global(&p1, &py1.x, &py1.y, TR_TORIGHT);
628 	py1.z = RtTrackHeightL(&p1);
629 
630 	p1.toRight = p1.seg->width;
631 	RtTrackLocal2Global(&p1, &py2.x, &py2.y, TR_TORIGHT);
632 	py2.z = RtTrackHeightL(&p1);
633 
634 
635 	v1.x = px2.x - px1.x;
636 	v1.y = px2.y - px1.y;
637 	v1.z = px2.z - px1.z;
638 	v2.x = py2.x - py1.x;
639 	v2.y = py2.y - py1.y;
640 	v2.z = py2.z - py1.z;
641 
642 	norm->x = v1.y * v2.z - v2.y * v1.z;
643 	norm->y = v2.x * v1.z - v1.x * v2.z;
644 	norm->z = v1.x * v2.y - v2.x * v1.y;
645 	lg = sqrt(norm->x * norm->x + norm->y * norm->y + norm->z * norm->z);
646 
647 	if (lg == 0.0) {
648 		lg = 1.0;
649 	} else {
650 		lg = 1.0 / lg;
651 	}
652 
653 	norm->x *= lg;
654 	norm->y *= lg;
655 	norm->z *= lg;
656 }
657 
658 
659 /** Get the distance from the start lane.
660     @ingroup	tracktools
661     @param	car 	the concerned car.
662     @return	The distance between the start lane and the car.
663  */
664 tdble
RtGetDistFromStart(tCarElt * car)665 RtGetDistFromStart(tCarElt *car)
666 {
667 	tTrackSeg *seg;
668 	tdble lg;
669 
670 	seg = car->_trkPos.seg;
671 	lg = seg->lgfromstart;
672 
673 	switch (seg->type) {
674 		case TR_STR:
675 			lg += car->_trkPos.toStart;
676 			break;
677 		default:
678 			lg += car->_trkPos.toStart * seg->radius;
679 			break;
680 	}
681 
682 	return lg;
683 }
684 
685 /** Get the distance from the start lane.
686     @ingroup	tracktools
687     @param	p	Local position
688     @return	The distance between the start lane and the car.
689  */
690 tdble
RtGetDistFromStart2(tTrkLocPos * p)691 RtGetDistFromStart2(tTrkLocPos *p)
692 {
693 	tTrackSeg *seg;
694 	tdble lg;
695 
696 	seg = p->seg;
697 	lg = seg->lgfromstart;
698 
699 	switch (seg->type) {
700 		case TR_STR:
701 			lg += p->toStart;
702 			break;
703 		default:
704 			lg += p->toStart * seg->radius;
705 			break;
706 	}
707 
708 	return lg;
709 }
710 
711 
712 /** Get the distance to the pit stop.
713     @ingroup	tracktools
714     @param	car 	The concerned car.
715     @param	track	The current Track
716     @param	dL	Length to the pits
717     @param	dW	Width to the pits
718     @return	0
719     @note	dW > 0 if the pit is on the right
720 */
721 int
RtDistToPit(struct CarElt * car,tTrack * track,tdble * dL,tdble * dW)722 RtDistToPit(struct CarElt *car, tTrack *track, tdble *dL, tdble *dW)
723 {
724 	tTrkLocPos *pitpos;
725 	tTrkLocPos *carpos;
726 	tdble pitts;
727 	tdble carts;
728 
729 	if (car->_pit == NULL) return 1;
730 
731 	pitpos = &(car->_pit->pos);
732 	carpos = &(car->_trkPos);
733 
734 	if (carpos->seg->radius) {
735 		carts = carpos->toStart * carpos->seg->radius;
736 	} else {
737 		carts = carpos->toStart;
738 	}
739 
740 	if (pitpos->seg->radius) {
741 		pitts = pitpos->toStart * pitpos->seg->radius;
742 	} else {
743 		pitts = pitpos->toStart;
744 	}
745 
746 	*dL = pitpos->seg->lgfromstart - carpos->seg->lgfromstart + pitts - carts;
747 	if (*dL < 0.0f) {
748 		*dL += track->length;
749 	} else if (*dL > track->length) {
750 		*dL -= track->length;
751 	}
752 
753 	*dW = pitpos->toRight - carpos->toRight;
754 
755 	return 0;
756 }
757 
758 
RtReadCarPitSetupEntry(tCarPitSetupValue * v,const char * path,const char * key,void * hdle,bool minmaxonly)759 static void RtReadCarPitSetupEntry(tCarPitSetupValue* v, const char* path, const char* key, void *hdle, bool minmaxonly)
760 {
761 	if (!minmaxonly) {
762 		v->value = GfParmGetNum(hdle, path, key, (char*)NULL, 0.0f);
763 	}
764 	GfParmGetNumBoundaries(hdle, path, key, &v->min, &v->max);
765 }
766 
767 
768 /** Initialize tCarPitSetup from data in parameter set given in handle @e hdle.
769  *  @ingroup setuptools
770  *  @param[in] hdle			Handle to setup parameter set
771  *  @param[in,out] s		Pointer to tCarPitSetup struct to initialize
772  *  @param[in] minmaxonly	If true, just the min/max values of s are modified
773  */
RtInitCarPitSetup(void * hdle,tCarPitSetup * s,bool minmaxonly)774 void RtInitCarPitSetup(void *hdle,  tCarPitSetup* s, bool minmaxonly)
775 {
776 	static const char *WheelSect[4] = {SECT_FRNTRGTWHEEL, SECT_FRNTLFTWHEEL, SECT_REARRGTWHEEL, SECT_REARLFTWHEEL};
777 	static const char *SuspSect[4] = {SECT_FRNTRGTSUSP, SECT_FRNTLFTSUSP, SECT_REARRGTSUSP, SECT_REARLFTSUSP};
778 	const int BUFSIZE = 256;
779 	char path[BUFSIZE];
780 
781 	// Steer
782 	RtReadCarPitSetupEntry(&s->steerLock, SECT_STEER, PRM_STEERLOCK, hdle, minmaxonly);
783 
784 	int i;
785 	for (i=0; i < 4; i++) {
786 		// Wheel
787 		RtReadCarPitSetupEntry(&s->wheelcamber[i], WheelSect[i], PRM_CAMBER, hdle, minmaxonly);
788 		RtReadCarPitSetupEntry(&s->wheeltoe[i], WheelSect[i], PRM_TOE, hdle, minmaxonly);
789 		RtReadCarPitSetupEntry(&s->wheelrideheight[i], WheelSect[i], PRM_RIDEHEIGHT, hdle, minmaxonly);
790 
791 		// Suspension
792 		RtReadCarPitSetupEntry(&s->suspspring[i], SuspSect[i], PRM_SPR, hdle, minmaxonly);
793 		RtReadCarPitSetupEntry(&s->susppackers[i], SuspSect[i], PRM_PACKERS, hdle, minmaxonly);
794 		RtReadCarPitSetupEntry(&s->suspslowbump[i], SuspSect[i], PRM_SLOWBUMP, hdle, minmaxonly);
795 		RtReadCarPitSetupEntry(&s->suspslowrebound[i], SuspSect[i], PRM_SLOWREBOUND, hdle, minmaxonly);
796 		RtReadCarPitSetupEntry(&s->suspfastbump[i], SuspSect[i], PRM_FASTBUMP, hdle, minmaxonly);
797 		RtReadCarPitSetupEntry(&s->suspfastrebound[i], SuspSect[i], PRM_FASTREBOUND, hdle, minmaxonly);
798 	}
799 
800 	// Brake
801 	RtReadCarPitSetupEntry(&s->brakeRepartition, SECT_BRKSYST, PRM_BRKREP, hdle, minmaxonly);
802 	RtReadCarPitSetupEntry(&s->brakePressure, SECT_BRKSYST, PRM_BRKPRESS, hdle, minmaxonly);
803 
804 	// Anti roll bar
805 	static const char *ArbSect[2] = {SECT_FRNTARB, SECT_REARARB};
806 	for (i=0; i < 2; i++) {
807 		RtReadCarPitSetupEntry(&s->arbspring[i], ArbSect[i], PRM_SPR, hdle, minmaxonly);
808 	}
809 
810 	// Third element
811 	static const char *AxleSect[2] = {SECT_FRNTAXLE, SECT_REARAXLE};
812 	for (i=0; i < 2; i++) {
813 		RtReadCarPitSetupEntry(&s->thirdspring[i], AxleSect[i], PRM_SPR, hdle, minmaxonly);
814 		RtReadCarPitSetupEntry(&s->thirdbump[i], AxleSect[i], PRM_SLOWBUMP, hdle, minmaxonly);
815 		RtReadCarPitSetupEntry(&s->thirdrebound[i], AxleSect[i], PRM_SLOWREBOUND, hdle, minmaxonly);
816 		RtReadCarPitSetupEntry(&s->thirdX0[i], AxleSect[i], PRM_SUSPCOURSE, hdle, minmaxonly);
817 	}
818 
819 	// Gears
820 	for (i=0; i < 8; i++) {
821 		snprintf(path, BUFSIZE, "%s/%s/%d", SECT_GEARBOX, ARR_GEARS, i+1);
822 		RtReadCarPitSetupEntry(&s->gearsratio[i], path, PRM_RATIO, hdle, minmaxonly);
823 	}
824 
825 	// Wings
826 	static const char *WingSect[2] = {SECT_FRNTWING, SECT_REARWING};
827 	for (i=0; i < 2; i++) {
828 		RtReadCarPitSetupEntry(&s->wingangle[i], WingSect[i], PRM_WINGANGLE, hdle, minmaxonly);
829 	}
830 
831 	// Differentials
832 	static const char *DiffSect[3] = {SECT_FRNTDIFFERENTIAL, SECT_REARDIFFERENTIAL, SECT_CENTRALDIFFERENTIAL};
833 	for (i=0; i < 3; i++) {
834 		RtReadCarPitSetupEntry(&s->diffratio[i], DiffSect[i], PRM_RATIO, hdle, minmaxonly);
835 		RtReadCarPitSetupEntry(&s->diffmintqbias[i], DiffSect[i], PRM_MIN_TQ_BIAS, hdle, minmaxonly);
836 		RtReadCarPitSetupEntry(&s->diffmaxtqbias[i], DiffSect[i], PRM_MAX_TQ_BIAS, hdle, minmaxonly);
837 		RtReadCarPitSetupEntry(&s->diffslipbias[i], DiffSect[i], PRM_MAX_SLIP_BIAS, hdle, minmaxonly);
838 		RtReadCarPitSetupEntry(&s->difflockinginputtq[i], DiffSect[i], PRM_LOCKING_TQ, hdle, minmaxonly);
839 		RtReadCarPitSetupEntry(&s->difflockinginputbraketq[i], DiffSect[i], PRM_LOCKINGBRAKE_TQ, hdle, minmaxonly);
840 
841 		const char* type = GfParmGetStr(hdle, DiffSect[i], PRM_TYPE, VAL_DIFF_NONE);
842 		if (strcmp(type, VAL_DIFF_LIMITED_SLIP) == 0) {
843 			s->diffType[i] = tCarPitSetup::LIMITED_SLIP;
844 		} else if (strcmp(type, VAL_DIFF_VISCOUS_COUPLER) == 0) {
845 			s->diffType[i] = tCarPitSetup::VISCOUS_COUPLER;
846 		} else if (strcmp(type, VAL_DIFF_SPOOL) == 0) {
847 			s->diffType[i] = tCarPitSetup::SPOOL;
848 		}  else if (strcmp(type, VAL_DIFF_FREE) == 0) {
849 			s->diffType[i] = tCarPitSetup::FREE;
850 		} else {
851 			s->diffType[i] = tCarPitSetup::NONE;
852 		}
853 	}
854 }
855 
856 
857 /** Array with names for rtCarPitSetupType enumeration */
858 static const char* CarPitSetupFilenames[6] = { "practice", "qualifying", "race", "backup1", "backup2", "backup3" };
859 
860 
861 /** Compose filename from given strings
862  *  @ingroup setuptools
863 	@param[in] type			Setup type
864 	@param[in] robidx		Index of robot
865 	@param[in] carname		TORCS internal name car name
866 	@param[in] trackname	TORCS internal name name of track
867 	@param[in,out] filename	Buffer for result
868 	@param[in] len			Buffer size
869  */
RtGetCarPitSetupFilename(rtCarPitSetupType type,int robidx,const char * carname,const char * trackname,char * filename,const int len)870 void RtGetCarPitSetupFilename(
871 	rtCarPitSetupType type,
872 	int robidx,
873 	const char* carname,
874 	const char* trackname,
875 	char* filename,
876 	const int len
877 )
878 {
879 	snprintf(filename, len, "%s_%s_%d_%s" , carname, trackname, robidx, CarPitSetupFilenames[type]);
880 }
881 
882 
883 /**	Robottool internal: Set parameter if min != max, save as well min and max values
884 	@ingroup setuptools
885 	@param[in,out] hdlesetup	Handle to parameter set to write into
886 	@param[in] path				path of parameter
887 	@param[in]	key				key	name
888 	@param[in]	unit			unit to convert the result to (NULL if SI wanted)
889 	@param[in]	v				tCarPitSetupValue to set
890 */
RtParmSetNum(void * hdlesetup,const char * path,const char * key,const char * unit,tCarPitSetupValue * v)891 static void RtParmSetNum(void* hdlesetup, const char* path, const char* key, const char* unit, tCarPitSetupValue* v)
892 {
893 	// If min == max there is nothing to adjust, so we do not need to write the value.
894 	if (fabs(v->min - v->max) >= 0.0001f) {
895 		GfParmSetNumEx(hdlesetup, path, key, unit, GfParmSI2Unit(unit, v->value), GfParmSI2Unit(unit, v->min), GfParmSI2Unit(unit, v->max));
896 	}
897 }
898 
899 
900 /**	Save a custom car setup to a given filename. The setup is validated against the setup given in hdlecar.
901 	@ingroup setuptools
902 	@param[in] hdlecar		Handle to "master setup" (parameter set) to validate against (min/max and other checks)
903 	@param[in] s			Pointer to tCarPitSetup struct to save
904 	@param[in] filepath		Full path to setup file
905 	@param[in] carname		TORCS internal name car name
906  */
RtSaveCarPitSetupFile(void * hdlecar,tCarPitSetup * s,const char * filepath,const char * carname)907 void RtSaveCarPitSetupFile(
908 	void *hdlecar,			// handle to car definition file, for min/max merge
909 	tCarPitSetup* s,		// the setup data to save
910 	const char* filepath,	// full path including filename and extension
911 	const char* carname
912 )
913 {
914 	const int BUFSIZE = 256;
915 	char path[BUFSIZE];
916 	void* hdlesetup = GfParmReadFile(filepath, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
917 
918 	// Steer
919 	RtParmSetNum(hdlesetup, SECT_STEER, PRM_STEERLOCK, "deg", &s->steerLock);
920 
921 	static const char *WheelSect[4] = {SECT_FRNTRGTWHEEL, SECT_FRNTLFTWHEEL, SECT_REARRGTWHEEL, SECT_REARLFTWHEEL};
922 	static const char *SuspSect[4] = {SECT_FRNTRGTSUSP, SECT_FRNTLFTSUSP, SECT_REARRGTSUSP, SECT_REARLFTSUSP};
923 
924 	int i;
925 	for (i=0; i < 4; i++) {
926 		// Wheel
927 		RtParmSetNum(hdlesetup, WheelSect[i], PRM_CAMBER, "deg", &s->wheelcamber[i]);
928 		RtParmSetNum(hdlesetup, WheelSect[i], PRM_TOE, "deg", &s->wheeltoe[i]);
929 		RtParmSetNum(hdlesetup, WheelSect[i], PRM_RIDEHEIGHT, "mm", &s->wheelrideheight[i]);
930 
931 		// Suspension
932 		RtParmSetNum(hdlesetup, SuspSect[i], PRM_SPR, "lbs/in", &s->suspspring[i]);
933 		RtParmSetNum(hdlesetup, SuspSect[i], PRM_PACKERS, "mm", &s->susppackers[i]);
934 		RtParmSetNum(hdlesetup, SuspSect[i], PRM_SLOWBUMP, "lbs/in/s", &s->suspslowbump[i]);
935 		RtParmSetNum(hdlesetup, SuspSect[i], PRM_SLOWREBOUND, "lbs/in/s", &s->suspslowrebound[i]);
936 		RtParmSetNum(hdlesetup, SuspSect[i], PRM_FASTBUMP, "lbs/in/s", &s->suspfastbump[i]);
937 		RtParmSetNum(hdlesetup, SuspSect[i], PRM_FASTREBOUND, "lbs/in/s", &s->suspfastrebound[i]);
938 	}
939 
940 	// Brake
941 	RtParmSetNum(hdlesetup, SECT_BRKSYST, PRM_BRKREP, NULL, &s->brakeRepartition);
942 	RtParmSetNum(hdlesetup, SECT_BRKSYST, PRM_BRKPRESS, "kPa", &s->brakePressure);
943 
944 	// Anti roll bar
945 	static const char *ArbSect[2] = {SECT_FRNTARB, SECT_REARARB};
946 	for (i=0; i < 2; i++) {
947 		RtParmSetNum(hdlesetup, ArbSect[i], PRM_SPR, "lbs/in", &s->arbspring[i]);
948 	}
949 
950 	// Third element
951 	static const char *AxleSect[2] = {SECT_FRNTAXLE, SECT_REARAXLE};
952 	for (i=0; i < 2; i++) {
953 		RtParmSetNum(hdlesetup, AxleSect[i], PRM_SPR, "lbs/in", &s->thirdspring[i]);
954 		RtParmSetNum(hdlesetup, AxleSect[i], PRM_SLOWBUMP, "lbs/in/s", &s->thirdbump[i]);
955 		RtParmSetNum(hdlesetup, AxleSect[i], PRM_SLOWREBOUND, "lbs/in/s", &s->thirdrebound[i]);
956 		RtParmSetNum(hdlesetup, AxleSect[i], PRM_SUSPCOURSE, "mm", &s->thirdX0[i]);
957 	}
958 
959 	// Gears
960 	for (i=0; i < 8; i++) {
961 		snprintf(path, BUFSIZE, "%s/%s/%d", SECT_GEARBOX, ARR_GEARS, i+1);
962 		RtParmSetNum(hdlesetup, path, PRM_RATIO, NULL, &s->gearsratio[i]);
963 	}
964 
965 	// Wings
966 	static const char *WingSect[2] = {SECT_FRNTWING, SECT_REARWING};
967 	for (i=0; i < 2; i++) {
968 		RtParmSetNum(hdlesetup, WingSect[i], PRM_WINGANGLE, "deg", &s->wingangle[i]);
969 	}
970 
971 	// Differentials
972 	static const char *DiffSect[3] = {SECT_FRNTDIFFERENTIAL, SECT_REARDIFFERENTIAL, SECT_CENTRALDIFFERENTIAL};
973 	static const char *DiffType[5] = {VAL_DIFF_NONE, VAL_DIFF_SPOOL, VAL_DIFF_FREE, VAL_DIFF_LIMITED_SLIP, VAL_DIFF_VISCOUS_COUPLER};
974 	for (i=0; i < 3; i++) {
975 		RtParmSetNum(hdlesetup, DiffSect[i], PRM_RATIO, NULL, &s->diffratio[i]);
976 		RtParmSetNum(hdlesetup, DiffSect[i], PRM_MIN_TQ_BIAS, NULL, &s->diffmintqbias[i]);
977 		RtParmSetNum(hdlesetup, DiffSect[i], PRM_MAX_TQ_BIAS, NULL, &s->diffmaxtqbias[i]);
978 		RtParmSetNum(hdlesetup, DiffSect[i], PRM_MAX_SLIP_BIAS, NULL, &s->diffslipbias[i]);
979 		RtParmSetNum(hdlesetup, DiffSect[i], PRM_LOCKING_TQ, "N.m", &s->difflockinginputtq[i]);
980 		RtParmSetNum(hdlesetup, DiffSect[i], PRM_LOCKINGBRAKE_TQ, "N.m", &s->difflockinginputbraketq[i]);
981 
982 		if (s->diffType[i] != tCarPitSetup::NONE) {
983 			GfParmSetStr(hdlesetup, DiffSect[i], PRM_TYPE, DiffType[s->diffType[i]]);
984 		}
985 	}
986 
987 	hdlesetup = GfParmMergeHandles(hdlecar, hdlesetup, GFPARM_MMODE_DST | GFPARM_MMODE_RELDST);
988 	GfParmWriteFile(filepath, hdlesetup, carname);
989 	GfParmReleaseHandle(hdlesetup);
990 }
991 
992 
993 /**	Save a custom car setup for a given robot, car, track and session (race, practice, qualifying, ...) type.
994 	The setup is validated against the setup given in hdlecar.
995 	@ingroup setuptools
996 	@param[in] hdlecar		Handle to "master setup" (parameter set) to validate against (min/max and other checks)
997 	@param[in] s			Pointer to tCarPitSetup struct to save
998 	@param[in] type			Setup type
999 	@param[in] modulename	Name of robot module without extension
1000 	@param[in] robidx		Index of robot
1001 	@param[in] trackname	TORCS internal name name of track
1002 	@param[in] carname		TORCS internal name car name
1003 	@note Robot, car, track and session information are used to compose a standard setup filename
1004 	@see RtSaveCarPitSetupFile
1005  */
RtSaveCarPitSetup(void * hdlecar,tCarPitSetup * s,rtCarPitSetupType type,const char * modulename,int robidx,const char * trackname,const char * carname)1006 void RtSaveCarPitSetup(
1007 	void *hdlecar,
1008 	tCarPitSetup* s,
1009 	rtCarPitSetupType type,
1010 	const char* modulename,
1011 	int robidx,
1012 	const char* trackname,
1013 	const char* carname
1014 )
1015 {
1016 	const int filelen = 256;
1017 	char filename[filelen];
1018 	RtGetCarPitSetupFilename(type, robidx, carname, trackname, filename, filelen);
1019 
1020 	const int pathlen = 1024;
1021 	char path[pathlen];
1022 
1023 	snprintf(path, pathlen, "%sdrivers/%s/setups", GetLocalDir(), modulename);
1024 	if (GfCreateDir(path) == GF_DIR_CREATED) {
1025 		snprintf(path, pathlen, "%sdrivers/%s/setups/%s.xml", GetLocalDir(), modulename, filename);
1026 		RtSaveCarPitSetupFile(hdlecar, s, path, carname);
1027 	} else {
1028 		GfError("RtSaveCarPitSetup, could not create %s\n", path);
1029 	}
1030 }
1031 
1032 
1033 /**	Checks if a specific car setup is available
1034 	@ingroup setuptools
1035 	@param[in] type			Setup type
1036 	@param[in] modulename	Name of robot module without extension
1037 	@param[in] robidx		Index of robot
1038 	@param[in] trackname	TORCS internal name name of track
1039 	@param[in] carname		TORCS internal name car name
1040 	@return	True on success, false on failure
1041  */
RtCarPitSetupExists(rtCarPitSetupType type,const char * modulename,int robidx,const char * trackname,const char * carname)1042 bool RtCarPitSetupExists(
1043 	rtCarPitSetupType type,
1044 	const char* modulename,
1045 	int robidx,
1046 	const char* trackname,
1047 	const char* carname
1048 )
1049 {
1050 	const int filelen = 256;
1051 	char filename[filelen];
1052 	RtGetCarPitSetupFilename(type, robidx, carname, trackname, filename, filelen);
1053 
1054 	const int pathlen = 1024;
1055 	char path[pathlen];
1056 
1057 	snprintf(path, pathlen, "%sdrivers/%s/setups/%s.xml", GetLocalDir(), modulename, filename);
1058 	FILE* file = fopen(path, "r");	// TODO: maybe "stat" would be enough here
1059 	if (file) {
1060 		fclose(file);
1061 		return true;
1062 	}
1063 
1064 	return false;
1065 }
1066 
1067 
1068 /**	Load a custom car setup from a given filename. The setup is validated against the setup given in hdlecar.
1069 	@ingroup setuptools
1070 	@param[in] hdlecar		Handle to "master setup" (parameter set) to validate against (min/max and other checks)
1071 	@param[in] filepath		Full path to setup file
1072 	@param[in,out] s		Pointer to tCarPitSetup struct to fill/initialize
1073 	@param[in] minmaxonly	If true, just the min/max values of s are modified
1074 	@return	True on success, false on failure
1075  */
RtLoadCarPitSetupFilename(void * hdlecar,const char * filepath,tCarPitSetup * s,bool minmaxonly)1076 bool RtLoadCarPitSetupFilename(void* hdlecar, const char* filepath,  tCarPitSetup* s, bool minmaxonly)
1077 {
1078 	void* hdlesetup = GfParmReadFile(filepath, GFPARM_RMODE_STD);
1079 	if (hdlesetup) {
1080 		hdlesetup = GfParmMergeHandles(hdlecar, hdlesetup, GFPARM_MMODE_DST | GFPARM_MMODE_RELDST);
1081 		RtInitCarPitSetup(hdlesetup, s, minmaxonly);
1082 		GfParmReleaseHandle(hdlesetup);
1083 		return true;
1084 	} else {
1085 		return false;
1086 	}
1087 }
1088 
1089 
1090 /**	Load a custom car setup for a given robot, car, track and session (race, practice, qualifying, ...) type.
1091 	The setup is validated against the setup given in hdlecar.
1092 	@ingroup setuptools
1093 	@param[in] hdlecar		Handle to "master setup" (parameter set) to validate against (min/max and other checks)
1094 	@param[in,out] s		Pointer to tCarPitSetup struct to fill/initialize
1095 	@param[in] type			Setup type
1096 	@param[in] modulename	Name of robot module without extension
1097 	@param[in] robidx		Index of robot
1098 	@param[in] trackname	TORCS internal name name of track
1099 	@param[in] carname		TORCS internal name car name
1100 	@param[in] minmaxonly	If true, just the min/max values of s are modified
1101 	@return	true on success, false on failure
1102 	@note Robot, car, track and session information are used to compose a standard setup filename
1103 	@see RtLoadCarPitSetupFilename
1104  */
RtLoadCarPitSetup(void * hdlecar,tCarPitSetup * s,rtCarPitSetupType type,const char * modulename,int robidx,const char * trackname,const char * carname,bool minmaxonly)1105 bool RtLoadCarPitSetup(
1106 	void* hdlecar,
1107 	tCarPitSetup* s,
1108 	rtCarPitSetupType type,
1109 	const char* modulename,
1110 	int robidx,
1111 	const char* trackname,
1112 	const char* carname,
1113 	bool minmaxonly
1114 )
1115 {
1116 	const int filelen = 256;
1117 	char filename[filelen];
1118 	RtGetCarPitSetupFilename(type, robidx, carname, trackname, filename, filelen);
1119 
1120 	const int pathlen = 1024;
1121 	char path[pathlen];
1122 
1123 	snprintf(path, pathlen, "%sdrivers/%s/setups/%s.xml", GetLocalDir(), modulename, filename);
1124 	return RtLoadCarPitSetupFilename(hdlecar, path, s, minmaxonly);
1125 }
1126 
1127 
1128 /** Gets a handle to a parameter file containing the original TORCS car setup, that means the car setup
1129 	merged with the cars category setup
1130     @ingroup setuptools
1131 	@param[in] carname TORCS internal name of the car (directory/filename)
1132     @return	NULL on failure, a valid handle otherwise
1133  */
RtLoadOriginalCarSettings(const char * carname)1134 void* RtLoadOriginalCarSettings(const char* carname)
1135 {
1136 	const int BUFSIZE = 1024;
1137 	char buf[BUFSIZE];
1138 
1139 	// Fetch car handle
1140 	snprintf(buf, BUFSIZE, "%scars/%s/%s.xml", GetDataDir(), carname, carname);
1141 	void* carhdle = GfParmReadFile(buf, GFPARM_RMODE_STD);
1142 	if (carhdle == 0) {
1143 		GfError("carhdle NULL in %s, line %d\n", __FILE__, __LINE__);
1144 		return NULL;
1145 	}
1146 
1147 	// Get category
1148 	const char* category = GfParmGetStr(carhdle, SECT_CAR, PRM_CATEGORY, NULL);
1149 	if (category == 0) {
1150 		GfError("category string NULL in %s, line %d\n", __FILE__, __LINE__);
1151 		GfParmReleaseHandle(carhdle);
1152 		return NULL;
1153 	}
1154 
1155 	// Fetch category handle
1156 	snprintf(buf, BUFSIZE, "%scategories/%s.xml", GetDataDir(), category);
1157 	void* cathdle = GfParmReadFile(buf, GFPARM_RMODE_STD);
1158 	if (cathdle == 0) {
1159 		GfError("cathdle NULL in %s, line %d\n", __FILE__, __LINE__);
1160 		GfParmReleaseHandle(carhdle);
1161 		return NULL;
1162 	}
1163 
1164 	// Compose final result, MergeHandles releases source handles with given parameters
1165 	cathdle = GfParmReadFile(buf, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
1166 	carhdle = GfParmMergeHandles(cathdle, carhdle, GFPARM_MMODE_SRC | GFPARM_MMODE_DST | GFPARM_MMODE_RELSRC | GFPARM_MMODE_RELDST);
1167 
1168 	return carhdle;
1169 }
1170 
1171 
1172 /** Initialize the given tCarPitSetup with the original TORCS setup, that means the car setup
1173 	merged with the cars category setup
1174     @ingroup setuptools
1175     @param[in,out] s	Pointer to tCarPitSetup struct to fill/initialize
1176 	@param[in] carname	TORCS internal name of the car (directory/filename)
1177     @return	True on success, false on failure
1178  */
RtInitCarPitSetupFromDefault(tCarPitSetup * s,const char * carname)1179 bool RtInitCarPitSetupFromDefault(tCarPitSetup* s, const char* carname)
1180 {
1181 	void* carhandle = RtLoadOriginalCarSettings(carname);
1182 	if (carhandle == 0) {
1183 		GfError("carhandle NULL in %s, line %d\n", __FILE__, __LINE__);
1184 		return false;
1185 	}
1186 
1187 	RtInitCarPitSetup(carhandle, s, false);
1188 	GfParmReleaseHandle(carhandle);
1189 	return true;
1190 }
1191 
1192 
1193 /**	Load a custom car setup file for a given robot, car, track and session (race, practice, qualifying, ...) type.
1194 	@ingroup setuptools
1195 	@param[in] type			Setup type
1196 	@param[in] modulename	Name of robot module without extension
1197 	@param[in] robidx		Index of robot
1198 	@param[in] trackname	TORCS internal name name of track
1199 	@param[in] carname		TORCS internal name car name
1200 	@return	Handle to data, or NULL on failure (e.g. if file is not available)
1201 	@note Robot, car, track and session information are used to compose a standard setup filename
1202  */
RtParmReadSetup(rtCarPitSetupType type,const char * modulename,int robidx,const char * trackname,const char * carname)1203 void* RtParmReadSetup(
1204 	rtCarPitSetupType type,
1205 	const char* modulename,
1206 	int robidx,
1207 	const char* trackname,
1208 	const char* carname
1209 )
1210 {
1211 	const int filelen = 256;
1212 	char filename[filelen];
1213 	RtGetCarPitSetupFilename(type, robidx, carname, trackname, filename, filelen);
1214 
1215 	const int pathlen = 1024;
1216 	char path[pathlen];
1217 
1218 	snprintf(path, pathlen, "%sdrivers/%s/setups/%s.xml", GetLocalDir(), modulename, filename);
1219 	return GfParmReadFile(path, GFPARM_RMODE_STD);
1220 }
1221