Bug Summary

File:client/ui/ui_parse.cpp
Location:line 1149, column 7
Description:Access to field 'type' results in a dereference of a null pointer (loaded from variable 'v')

Annotated Source Code

1/**
2 * @file
3 * @todo remove all "token" param from function and use Com_UnParseLastToken
4 * @todo reduce use of uiGlobal (create global functions to add/get/... entities)
5 * @todo remove Com_EParseValue and use Com_ParseValue
6 */
7
8/*
9Copyright (C) 2002-2011 UFO: Alien Invasion.
10
11This program is free software; you can redistribute it and/or
12modify it under the terms of the GNU General Public License
13as published by the Free Software Foundation; either version 2
14of the License, or (at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
20See the GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26*/
27
28#include "../client.h"
29#include "ui_parse.h"
30#include "ui_main.h"
31#include "ui_node.h"
32#include "ui_data.h"
33#include "ui_internal.h"
34#include "ui_actions.h"
35#include "ui_sprite.h"
36#include "ui_components.h"
37#include "node/ui_node_window.h"
38#include "node/ui_node_selectbox.h"
39#include "node/ui_node_abstractnode.h"
40#include "node/ui_node_abstractoption.h"
41
42#include "../../shared/parse.h"
43
44/** prototypes */
45static bool UI_ParseProperty(void* object, const value_t *property, const char* objectName, const char **text, const char **token);
46static uiAction_t *UI_ParseActionList(uiNode_t *node, const char **text, const char **token);
47static uiNode_t *UI_ParseNode(uiNode_t * parent, const char **text, const char **token, const char *errhead);
48
49/** @brief valid properties for a UI model definition */
50static const value_t uiModelProperties[] = {
51 {"model", V_HUNK_STRING, offsetof(uiModel_t, model)__builtin_offsetof(uiModel_t, model), 0},
52 {"need", V_NULL, 0, 0},
53 {"anim", V_HUNK_STRING, offsetof(uiModel_t, anim)__builtin_offsetof(uiModel_t, anim), 0},
54 {"skin", V_INT, offsetof(uiModel_t, skin)__builtin_offsetof(uiModel_t, skin), sizeof(int)},
55 {"color", V_COLOR, offsetof(uiModel_t, color)__builtin_offsetof(uiModel_t, color), sizeof(vec4_t)},
56 {"tag", V_HUNK_STRING, offsetof(uiModel_t, tag)__builtin_offsetof(uiModel_t, tag), 0},
57 {"parent", V_HUNK_STRING, offsetof(uiModel_t, parent)__builtin_offsetof(uiModel_t, parent), 0},
58
59 {NULL__null, V_NULL, 0, 0},
60};
61
62/** @brief reserved token preventing calling a node with it
63 * @todo Use dichotomic search
64 */
65static char const* const reservedTokens[] = {
66 "this",
67 "parent",
68 "root",
69 "null",
70 "super",
71 "node",
72 "cvar",
73 "int",
74 "float",
75 "string",
76 "var",
77 NULL__null
78};
79
80static bool UI_TokenIsReserved (const char *name)
81{
82 char const* const* token = reservedTokens;
83 while (*token) {
84 if (Q_streq(*token, name)(strcmp(*token, name) == 0))
85 return true;
86 token++;
87 }
88 return false;
89}
90
91static bool UI_TokenIsValue (const char *name, bool isQuoted)
92{
93 assert(name)(__builtin_expect(!(name), 0) ? __assert_rtn(__func__, "src/client/ui/ui_parse.cpp"
, 93, "name") : (void)0)
;
94 if (isQuoted)
95 return true;
96 /* is it a number */
97 if ((name[0] >= '0' && name[0] <= '9') || name[0] == '-' || name[0] == '.')
98 return true;
99 /* is it a var (*cvar:...) */
100 if (name[0] == '*')
101 return true;
102 if (Q_streq(name, "true")(strcmp(name, "true") == 0))
103 return true;
104 if (Q_streq(name, "false")(strcmp(name, "false") == 0))
105 return true;
106
107 /* uppercase const name */
108 if ((name[0] >= 'A' && name[0] <= 'Z') || name[0] == '_') {
109 bool onlyUpperCase = true;
110 while (*name != '\0') {
111 if ((name[0] >= 'A' && name[0] <= 'Z') || name[0] == '_' || (name[0] >= '0' && name[0] <= '9')) {
112 /* available chars */
113 } else {
114 return false;
115 }
116 name++;
117 }
118 return onlyUpperCase;
119 }
120
121 return false;
122}
123
124static bool UI_TokenIsName (const char *name, bool isQuoted)
125{
126 assert(name)(__builtin_expect(!(name), 0) ? __assert_rtn(__func__, "src/client/ui/ui_parse.cpp"
, 126, "name") : (void)0)
;
127 if (isQuoted)
128 return false;
129 if ((name[0] >= 'a' && name[0] <= 'z') || (name[0] >= 'A' && name[0] <= 'Z') || name[0] == '_') {
130 bool onlyUpperCase = true;
131 while (*name != '\0') {
132 if (name[0] >= 'a' && name[0] <= 'z') {
133 onlyUpperCase = false;
134 } else if ((name[0] >= '0' && name[0] <= '9') || (name[0] >= 'A' && name[0] <= 'Z') || name[0] == '_') {
135 /* available chars */
136 } else {
137 return false;
138 }
139 name++;
140 }
141 return !onlyUpperCase;
142 }
143 return false;
144}
145
146/**
147 * @brief Find a value_t by name into a array of value_t
148 * @param[in] propertyList Array of value_t, with null termination
149 * @param[in] name Property name we search
150 * @return A value_t with the requested name, else NULL
151 */
152const value_t* UI_FindPropertyByName (const value_t* propertyList, const char* name)
153{
154 const value_t* current = propertyList;
155 while (current->string != NULL__null) {
156 if (!Q_strcasecmp(name, current->string)strcasecmp((name), (current->string)))
157 return current;
158 current++;
159 }
160 return NULL__null;
161}
162
163/**
164 * @brief Allocate a float into the UI static memory
165 * @note Its not a dynamic memory allocation. Please only use it at the loading time
166 * @param[in] count number of element need to allocate
167 * @todo Assert out when we are not in parsing/loading stage
168 */
169float* UI_AllocStaticFloat (int count)
170{
171 float *result;
172 assert(count > 0)(__builtin_expect(!(count > 0), 0) ? __assert_rtn(__func__
, "src/client/ui/ui_parse.cpp", 172, "count > 0") : (void)
0)
;
173 result = (float*) UI_AllocHunkMemory(sizeof(float) * count, sizeof(float), false);
174 if (result == NULL__null)
175 Com_Error(ERR_FATAL0, "UI_AllocFloat: UI memory hunk exceeded - increase the size");
176 return result;
177}
178
179/**
180 * @brief Allocate a color into the UI static memory
181 * @note Its not a dynamic memory allocation. Please only use it at the loading time
182 * @param[in] count number of element need to allocate
183 * @todo Assert out when we are not in parsing/loading stage
184 */
185vec4_t* UI_AllocStaticColor (int count)
186{
187 vec4_t *result;
188 assert(count > 0)(__builtin_expect(!(count > 0), 0) ? __assert_rtn(__func__
, "src/client/ui/ui_parse.cpp", 188, "count > 0") : (void)
0)
;
189 result = (vec4_t*) UI_AllocHunkMemory(sizeof(vec_t) * 4 * count, sizeof(vec_t), false);
190 if (result == NULL__null)
191 Com_Error(ERR_FATAL0, "UI_AllocColor: UI memory hunk exceeded - increase the size");
192 return result;
193}
194
195/**
196 * @brief Allocate a string into the UI static memory
197 * @note Its not a dynamic memory allocation. Please only use it at the loading time
198 * @param[in] string Use to initialize the string
199 * @param[in] size request a fixed memory size, if 0 the string size is used
200 * @todo Assert out when we are not in parsing/loading stage
201 */
202char* UI_AllocStaticString (const char* string, int size)
203{
204 char* result;
205 if (size == 0) {
206 size = strlen(string) + 1;
207 }
208 result = (char*) UI_AllocHunkMemory(size, sizeof(char), false);
209 if (result == NULL__null)
210 Com_Error(ERR_FATAL0, "UI_AllocString: UI memory hunk exceeded - increase the size");
211 Q_strncpyz(result, string, size)Q_strncpyzDebug( result, string, size, "src/client/ui/ui_parse.cpp"
, 211 )
;
212 return result;
213}
214
215/**
216 * @brief Allocate an action
217 * @return An action
218 */
219uiAction_t *UI_AllocStaticAction (void)
220{
221 if (ui_global.numActions >= UI_MAX_ACTIONS2*8192)
222 Com_Error(ERR_FATAL0, "UI_AllocAction: Too many UI actions");
223 return &ui_global.actions[ui_global.numActions++];
224}
225
226/**
227 * Parse a string according to a property type, and allocate a raw value to the static memory
228 *
229 * @param action Action to initialize
230 * @param node Current node we are parsing, only used for error message
231 * @param property Type of the value to parse, if NULL the string is not stored as string
232 * @param string String value to parse
233 * @return True if the action is initialized
234 * @todo remove node param and catch error where we call that function
235 */
236bool UI_InitRawActionValue (uiAction_t* action, uiNode_t *node, const value_t *property, const char *string)
237{
238 if (property == NULL__null) {
239 action->type = EA_VALUE_STRING;
240 action->d.terminal.d1.data = UI_AllocStaticString(string, 0);
241 action->d.terminal.d2.integer = 0;
242 return true;
243 }
244
245 if (property->type == V_UI_SPRITEREF(0x8000 + 3)) {
246 uiSprite_t* sprite = UI_GetSpriteByName(string);
247 if (sprite == NULL__null) {
248 Com_Printf("UI_ParseSetAction: sprite '%s' not found (%s)\n", string, UI_GetPath(node));
249 return false;
250 }
251 action->type = EA_VALUE_RAW;
252 action->d.terminal.d1.data = sprite;
253 action->d.terminal.d2.integer = property->type;
254 return true;
255 } else {
256 const int baseType = property->type & V_UI_MASK0x8F00;
257 if (baseType != 0 && baseType != V_UI_CVAR(0x8000 + 0x0100)) {
258 Com_Printf("UI_ParseRawValue: setter for property '%s' (type %d, 0x%X) is not supported (%s)\n", property->string, property->type, property->type, UI_GetPath(node));
259 return false;
260 }
261 ui_global.curadata = (byte*) Com_AlignPtr(ui_global.curadata, (valueTypes_t) (property->type & V_BASETYPEMASK0x3F));
262 action->type = EA_VALUE_RAW;
263 action->d.terminal.d1.data = ui_global.curadata;
264 action->d.terminal.d2.integer = property->type;
265 /** @todo we should hide use of ui_global.curadata */
266 ui_global.curadata += Com_EParseValue(ui_global.curadata, string, (valueTypes_t) (property->type & V_BASETYPEMASK), 0, property->size)Com_EParseValueDebug(ui_global.curadata, string, (valueTypes_t
) (property->type & 0x3F), 0, property->size, "src/client/ui/ui_parse.cpp"
, 266)
;
267 return true;
268 }
269}
270
271/**
272 * @brief Parser for setter command
273 */
274static bool UI_ParseSetAction (uiNode_t *node, uiAction_t *action, const char **text, const char **token, const char *errhead)
275{
276 const value_t *property;
277 int type;
278 uiAction_t* localAction;
279
280 assert((*token)[0] == '*')(__builtin_expect(!((*token)[0] == '*'), 0) ? __assert_rtn(__func__
, "src/client/ui/ui_parse.cpp", 280, "(*token)[0] == '*'") : (
void)0)
;
281
282 Com_UnParseLastToken();
283 action->d.nonTerminal.left = UI_ParseExpression(text);
284
285 type = action->d.nonTerminal.left->type;
286 if (type != EA_VALUE_CVARNAME && type != EA_VALUE_CVARNAME_WITHINJECTION
287 && type != EA_VALUE_PATHPROPERTY && type != EA_VALUE_PATHPROPERTY_WITHINJECTION) {
288 Com_Printf("UI_ParseSetAction: Cvar or Node property expected. Type '%i' found\n", type);
289 return false;
290 }
291
292 /* must use "equal" char between name and value */
293 *token = Com_EParse(text, errhead, NULL__null);
294 if (!*text)
295 return false;
296 if (!Q_streq(*token, "=")(strcmp(*token, "=") == 0)) {
297 Com_Printf("UI_ParseSetAction: Assign sign '=' expected between variable and value. '%s' found in node %s.\n", *token, UI_GetPath(node));
298 return false;
299 }
300
301 /* get the value */
302 if (type == EA_VALUE_CVARNAME || type == EA_VALUE_CVARNAME_WITHINJECTION) {
303 action->d.nonTerminal.right = UI_ParseExpression(text);
304 return true;
305 }
306
307 property = (const value_t *) action->d.nonTerminal.left->d.terminal.d2.data;
308
309 *token = Com_EParse(text, errhead, NULL__null);
310 if (!*text)
311 return false;
312
313 if (Q_streq(*token, "{")(strcmp(*token, "{") == 0)) {
314 uiAction_t* actionList;
315
316 if (property != NULL__null && property->type != V_UI_ACTION(0x8000 + 0)) {
317 Com_Printf("UI_ParseSetAction: Property %s@%s do not expect code block.\n", UI_GetPath(node), property->string);
318 return false;
319 }
320
321 actionList = UI_ParseActionList(node, text, token);
322 if (actionList == NULL__null)
323 return false;
324
325 localAction = UI_AllocStaticAction();
326 localAction->type = EA_VALUE_RAW;
327 localAction->d.terminal.d1.data = actionList;
328 localAction->d.terminal.d2.integer = V_UI_ACTION(0x8000 + 0);
329 action->d.nonTerminal.right = localAction;
330
331 return true;
332 }
333
334 if (Q_streq(*token, "(")(strcmp(*token, "(") == 0)) {
335 Com_UnParseLastToken();
336 action->d.nonTerminal.right = UI_ParseExpression(text);
337 return true;
338 }
339
340 /* @todo everything should come from UI_ParseExpression */
341
342 if (UI_IsInjectedString(*token)) {
343 localAction = UI_AllocStaticAction();
344 localAction->type = EA_VALUE_STRING_WITHINJECTION;
345 localAction->d.terminal.d1.data = UI_AllocStaticString(*token, 0);
346 action->d.nonTerminal.right = localAction;
347 return true;
348 }
349
350 localAction = UI_AllocStaticAction();
351 UI_InitRawActionValue(localAction, node, property, *token);
352 action->d.nonTerminal.right = localAction;
353 return true;
354}
355
356/**
357 * @brief Parser for c command
358 */
359static bool UI_ParseCallAction (uiNode_t *node, uiAction_t *action, const char **text, const char **token, const char *errhead)
360{
361 uiAction_t *expression;
362 uiAction_t *lastParam = NULL__null;
363 int paramID = 0;
364 expression = UI_ParseExpression(text);
365 if (expression == NULL__null)
366 return false;
367
368 if (expression->type != EA_VALUE_PATHNODE_WITHINJECTION && expression->type != EA_VALUE_PATHNODE && expression->type != EA_VALUE_PATHPROPERTY && expression->type != EA_VALUE_PATHPROPERTY_WITHINJECTION) {
369 Com_Printf("UI_ParseCallAction: \"call\" keyword only support pathnode and pathproperty (node: %s)\n", UI_GetPath(node));
370 return false;
371 }
372
373 action->d.nonTerminal.left = expression;
374
375 /* check parameters */
376 *token = Com_EParse(text, errhead, NULL__null);
377 if ((*token)[0] == '\0')
378 return false;
379
380 /* there is no parameters */
381 if (!Q_streq(*token, "(")(strcmp(*token, "(") == 0)) {
382 Com_UnParseLastToken();
383 return true;
384 }
385
386 /* read parameters */
387 do {
388 uiAction_t *param;
389 paramID++;
390
391 /* parameter */
392 param = UI_ParseExpression(text);
393 if (param == NULL__null) {
394 Com_Printf("UI_ParseCallAction: problem with the %i parameter\n", paramID);
395 return false;
396 }
397 if (lastParam == NULL__null)
398 action->d.nonTerminal.right = param;
399 else
400 lastParam->next = param;
401 lastParam = param;
402
403 /* separator */
404 *token = Com_EParse(text, errhead, NULL__null);
405 if (!*token)
406 return false;
407 if (!Q_streq(*token, ",")(strcmp(*token, ",") == 0)) {
408 if (Q_streq(*token, ")")(strcmp(*token, ")") == 0))
409 break;
410 Com_UnParseLastToken();
411 Com_Printf("UI_ParseCallAction: Invalidate end of 'call' after param %i\n", paramID);
412 return false;
413 }
414 } while(true);
415
416 return true;
417}
418
419/**
420 * @brief Parse actions and return action list
421 * @return The first element from a list of action
422 * @sa ea_t
423 * @todo need cleanup, compute action out of the final memory; reduce number of var
424 */
425static uiAction_t *UI_ParseActionList (uiNode_t *node, const char **text, const char **token)
426{
427 const char *errhead = "UI_ParseActionList: unexpected end of file (in event)";
428 uiAction_t *firstAction;
429 uiAction_t *lastAction;
430 uiAction_t *action;
431
432 lastAction = NULL__null;
433 firstAction = NULL__null;
434
435 /* prevent bad position */
436 if ((*token)[0] != '{') {
437 Com_Printf("UI_ParseActionList: token \"{\" expected, but \"%s\" found (in event) (node: %s)\n", *token, UI_GetPath(node));
438 return NULL__null;
439 }
440
441 while (true) {
442 bool result;
443 int type = EA_NULL;
444
445 /* get new token */
446 *token = Com_EParse(text, errhead, NULL__null);
447 if (!*token)
448 return NULL__null;
449
450 if ((*token)[0] == '}')
451 break;
452
453 type = UI_GetActionTokenType(*token, EA_ACTION);
454 /* setter form */
455 if (type == EA_NULL && (*token)[0] == '*')
456 type = EA_ASSIGN;
457
458 /* unknown, we break the parsing */
459 if (type == EA_NULL) {
460 Com_Printf("UI_ParseActionList: unknown token \"%s\" ignored (in event) (node: %s)\n", *token, UI_GetPath(node));
461 return NULL__null;
462 }
463
464 /* add the action */
465 action = UI_AllocStaticAction();
466 /** @todo better to append the action after initialization */
467 if (lastAction)
468 lastAction->next = action;
469 if (!firstAction)
470 firstAction = action;
471 action->type = type;
472
473 /* decode action */
474 switch (action->type) {
475 case EA_CMD:
476 /* get parameter values */
477 *token = Com_EParse(text, errhead, NULL__null);
478 if (!*text)
479 return NULL__null;
480
481 /* get the value */
482 action->d.terminal.d1.constString = UI_AllocStaticString(*token, 0);
483 break;
484
485 case EA_ASSIGN:
486 result = UI_ParseSetAction(node, action, text, token, errhead);
487 if (!result)
488 return NULL__null;
489 break;
490
491 case EA_CALL:
492 result = UI_ParseCallAction(node, action, text, token, errhead);
493 if (!result)
494 return NULL__null;
495 break;
496
497 case EA_DELETE:
498 {
499 uiAction_t *expression;
500 expression = UI_ParseExpression(text);
501 if (expression == NULL__null)
502 return NULL__null;
503
504 if (expression->type != EA_VALUE_CVARNAME) {
505 Com_Printf("UI_ParseActionList: \"delete\" keyword only support cvarname (node: %s)\n", UI_GetPath(node));
506 return NULL__null;
507 }
508
509 action->d.nonTerminal.left = expression;
510 break;
511 }
512
513 case EA_ELIF:
514 /* check previous action */
515 if (!lastAction || (lastAction->type != EA_IF && lastAction->type != EA_ELIF)) {
516 Com_Printf("UI_ParseActionList: 'elif' must be set after an 'if' or an 'elif' (node: %s)\n", UI_GetPath(node));
517 return NULL__null;
518 }
519 /* then it execute EA_IF, no break */
520 case EA_WHILE:
521 case EA_IF:
522 {
523 uiAction_t *expression;
524
525 /* get the condition */
526 expression = UI_ParseExpression(text);
527 if (expression == NULL__null)
528 return NULL__null;
529 action->d.nonTerminal.left = expression;
530
531 /* get the action block */
532 *token = Com_EParse(text, errhead, NULL__null);
533 if (!*text)
534 return NULL__null;
535 action->d.nonTerminal.right = UI_ParseActionList(node, text, token);
536 if (action->d.nonTerminal.right == NULL__null) {
537 if (action->type == EA_IF)
538 Com_Printf("UI_ParseActionList: block expected after \"if\" (node: %s)\n", UI_GetPath(node));
539 else if (action->type == EA_ELIF)
540 Com_Printf("UI_ParseActionList: block expected after \"elif\" (node: %s)\n", UI_GetPath(node));
541 else
542 Com_Printf("UI_ParseActionList: block expected after \"while\" (node: %s)\n", UI_GetPath(node));
543 return NULL__null;
544 }
545 break;
546 }
547
548 case EA_ELSE:
549 /* check previous action */
550 if (!lastAction || (lastAction->type != EA_IF && lastAction->type != EA_ELIF)) {
551 Com_Printf("UI_ParseActionList: 'else' must be set after an 'if' or an 'elif' (node: %s)\n", UI_GetPath(node));
552 return NULL__null;
553 }
554
555 /* get the action block */
556 *token = Com_EParse(text, errhead, NULL__null);
557 if (!*text)
558 return NULL__null;
559 action->d.nonTerminal.left = NULL__null;
560 action->d.nonTerminal.right = UI_ParseActionList(node, text, token);
561 if (action->d.nonTerminal.right == NULL__null) {
562 Com_Printf("UI_ParseActionList: block expected after \"else\" (node: %s)\n", UI_GetPath(node));
563 return NULL__null;
564 }
565 break;
566
567 default:
568 assert(false)(__builtin_expect(!(false), 0) ? __assert_rtn(__func__, "src/client/ui/ui_parse.cpp"
, 568, "false") : (void)0)
;
569 }
570
571 /* step */
572 lastAction = action;
573 }
574
575 assert((*token)[0] == '}')(__builtin_expect(!((*token)[0] == '}'), 0) ? __assert_rtn(__func__
, "src/client/ui/ui_parse.cpp", 575, "(*token)[0] == '}'") : (
void)0)
;
576
577 /* return non NULL value */
578 if (firstAction == NULL__null) {
579 firstAction = UI_AllocStaticAction();
580 }
581
582 return firstAction;
583}
584
585static bool UI_ParseExcludeRect (uiNode_t * node, const char **text, const char **token, const char *errhead)
586{
587 uiExcludeRect_t rect;
588 uiExcludeRect_t *newRect;
589
590 /* get parameters */
591 *token = Com_EParse(text, errhead, node->name);
592 if (!*text)
593 return false;
594 if ((*token)[0] != '{') {
595 Com_Printf("UI_ParseExcludeRect: node with bad excluderect ignored (node \"%s\")\n", UI_GetPath(node));
596 return true;
597 }
598
599 do {
600 *token = Com_EParse(text, errhead, node->name);
601 if (!*text)
602 return false;
603 /** @todo move it into a property array */
604 if (Q_streq(*token, "pos")(strcmp(*token, "pos") == 0)) {
605 *token = Com_EParse(text, errhead, node->name);
606 if (!*text)
607 return false;
608 Com_EParseValue(&rect, *token, V_POS, offsetof(uiExcludeRect_t, pos), sizeof(vec2_t))Com_EParseValueDebug(&rect, *token, V_POS, __builtin_offsetof
(uiExcludeRect_t, pos), sizeof(vec2_t), "src/client/ui/ui_parse.cpp"
, 608)
;
609 } else if (Q_streq(*token, "size")(strcmp(*token, "size") == 0)) {
610 *token = Com_EParse(text, errhead, node->name);
611 if (!*text)
612 return false;
613 Com_EParseValue(&rect, *token, V_POS, offsetof(uiExcludeRect_t, size), sizeof(vec2_t))Com_EParseValueDebug(&rect, *token, V_POS, __builtin_offsetof
(uiExcludeRect_t, size), sizeof(vec2_t), "src/client/ui/ui_parse.cpp"
, 613)
;
614 }
615 } while ((*token)[0] != '}');
616
617 newRect = (uiExcludeRect_t*) UI_AllocHunkMemory(sizeof(*newRect), STRUCT_MEMORY_ALIGN8, false);
618 if (newRect == NULL__null) {
619 Com_Printf("UI_ParseExcludeRect: ui hunk memory exceeded.");
620 return false;
621 }
622
623 /* move data to final memory and link to node */
624 *newRect = rect;
625 newRect->next = node->firstExcludeRect;
626 node->firstExcludeRect = newRect;
627 return true;
628}
629
630static bool UI_ParseEventProperty (uiNode_t * node, const value_t *event, const char **text, const char **token, const char *errhead)
631{
632 /* add new actions to end of list */
633 uiAction_t** action = &Com_GetValue<uiAction_t*>(node, event);
634 for (; *action; action = &(*action)->next) {}
635
636 /* get the action body */
637 *token = Com_EParse(text, errhead, node->name);
638 if (!*text)
639 return false;
640
641 if ((*token)[0] != '{') {
642 Com_Printf("UI_ParseEventProperty: Event '%s' without body (%s)\n", event->string, UI_GetPath(node));
643 return false;
644 }
645
646 *action = UI_ParseActionList(node, text, token);
647 if (*action == NULL__null)
648 return false;
649
650 /* block terminal already read */
651 assert((*token)[0] == '}')(__builtin_expect(!((*token)[0] == '}'), 0) ? __assert_rtn(__func__
, "src/client/ui/ui_parse.cpp", 651, "(*token)[0] == '}'") : (
void)0)
;
652
653 return true;
654}
655
656/**
657 * @brief Parse a property value
658 * @todo don't read the next token (need to change the script language)
659 */
660static bool UI_ParseProperty (void* object, const value_t *property, const char* objectName, const char **text, const char **token)
661{
662 const char *errhead = "UI_ParseProperty: unexpected end of file (object";
663 static const char *notWellFormedValue = "UI_ParseProperty: \"%s\" is not a well formed node name (it must be quoted, uppercase const, a number, or prefixed with '*')\n";
664 size_t bytes;
665 int result;
666 const int specialType = property->type & V_UI_MASK0x8F00;
667
668 if (property->type == V_NULL) {
669 return false;
670 }
671
672 switch (specialType) {
673 case V_NOT_UI0: /* common type */
674
675 *token = Com_EParse(text, errhead, objectName);
676 if (!*text)
677 return false;
678 if (!UI_TokenIsValue(*token, Com_GetType(text) == TT_QUOTED_WORD)) {
679 Com_Printf(notWellFormedValue, *token);
680 return false;
681 }
682
683 if (property->type == V_TRANSLATION_STRING) {
684 /* selectbox values are static arrays */
685 char* const target = Com_GetValue<char[]>(object, property);
686 const char *translatableToken = *token;
687 assert(property->size)(__builtin_expect(!(property->size), 0) ? __assert_rtn(__func__
, "src/client/ui/ui_parse.cpp", 687, "property->size") : (
void)0)
;
688 if (translatableToken[0] == '_')
689 translatableToken++;
690 Q_strncpyz(target, translatableToken, property->size)Q_strncpyzDebug( target, translatableToken, property->size
, "src/client/ui/ui_parse.cpp", 690 )
;
691 } else {
692 result = Com_ParseValue(object, *token, property->type, property->ofs, property->size, &bytes);
693 if (result != RESULT_OK) {
694 Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
695 return false;
696 }
697 }
698 break;
699
700 case V_UI_REF(0x8000 + 0x0200):
701 *token = Com_EParse(text, errhead, objectName);
702 if (!*text)
703 return false;
704 if (!UI_TokenIsValue(*token, Com_GetType(text) == TT_QUOTED_WORD)) {
705 Com_Printf(notWellFormedValue, *token);
706 return false;
707 }
708
709 /* a reference to data is handled like this */
710 ui_global.curadata = (byte*) Com_AlignPtr(ui_global.curadata, (valueTypes_t) (property->type & V_BASETYPEMASK0x3F));
711 Com_GetValue<byte*>(object, property) = ui_global.curadata;
712
713 /** @todo check for the moment its not a cvar */
714 assert((*token)[0] != '*')(__builtin_expect(!((*token)[0] != '*'), 0) ? __assert_rtn(__func__
, "src/client/ui/ui_parse.cpp", 714, "(*token)[0] != '*'") : (
void)0)
;
715
716 /* sanity check */
717 if ((property->type & V_BASETYPEMASK0x3F) == V_STRING && strlen(*token) > MAX_VAR64 - 1) {
718 Com_Printf("UI_ParseProperty: Value '%s' is too long (key %s)\n", *token, property->string);
719 return false;
720 }
721
722 result = Com_ParseValue(ui_global.curadata, *token, (valueTypes_t) (property->type & V_BASETYPEMASK0x3F), 0, property->size, &bytes);
723 if (result != RESULT_OK) {
724 Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
725 return false;
726 }
727 ui_global.curadata += bytes;
728
729 break;
730
731 case V_UI_CVAR(0x8000 + 0x0100): /* common type */
732 *token = Com_EParse(text, errhead, objectName);
733 if (!*text)
734 return false;
735 if (!UI_TokenIsValue(*token, Com_GetType(text) == TT_QUOTED_WORD)) {
736 Com_Printf(notWellFormedValue, *token);
737 return false;
738 }
739
740 /* references are parsed as string */
741 if ((*token)[0] == '*') {
742 /* a reference to data */
743 ui_global.curadata = (byte*) Com_AlignPtr(ui_global.curadata, V_STRING);
744 Com_GetValue<byte*>(object, property) = ui_global.curadata;
745
746 /* sanity check */
747 if (strlen(*token) > MAX_VAR64 - 1) {
748 Com_Printf("UI_ParseProperty: Value '%s' is too long (key %s)\n", *token, property->string);
749 return false;
750 }
751
752 result = Com_ParseValue(ui_global.curadata, *token, V_STRING, 0, 0, &bytes);
753 if (result != RESULT_OK) {
754 Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
755 return false;
756 }
757 ui_global.curadata += bytes;
758 } else {
759 /* a reference to data */
760 ui_global.curadata = (byte*) Com_AlignPtr(ui_global.curadata, (valueTypes_t)(property->type & V_BASETYPEMASK0x3F));
761 Com_GetValue<byte*>(object, property) = ui_global.curadata;
762
763 /* sanity check */
764 if ((property->type & V_BASETYPEMASK0x3F) == V_STRING && strlen(*token) > MAX_VAR64 - 1) {
765 Com_Printf("UI_ParseProperty: Value '%s' is too long (key %s)\n", *token, property->string);
766 return false;
767 }
768
769 result = Com_ParseValue(ui_global.curadata, *token, (valueTypes_t)(property->type & V_BASETYPEMASK0x3F), 0, property->size, &bytes);
770 if (result != RESULT_OK) {
771 Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
772 return false;
773 }
774 ui_global.curadata += bytes;
775 }
776 break;
777
778 case V_UI0x8000:
779
780 switch ((int)property->type) {
781 case V_UI_ACTION(0x8000 + 0):
782 result = UI_ParseEventProperty(static_cast<uiNode_t*>(object), property, text, token, errhead);
783 if (!result)
784 return false;
785 break;
786
787 case V_UI_EXCLUDERECT(0x8000 + 1):
788 result = UI_ParseExcludeRect(static_cast<uiNode_t*>(object), text, token, errhead);
789 if (!result)
790 return false;
791 break;
792
793 case V_UI_SPRITEREF(0x8000 + 3):
794 {
795 *token = Com_EParse(text, errhead, objectName);
796 if (!*text)
797 return false;
798
799 uiSprite_t const*& sprite = Com_GetValue<uiSprite_t const*>(object, property);
800 sprite = UI_GetSpriteByName(*token);
801 if (!sprite) {
802 Com_Printf("UI_ParseProperty: sprite '%s' not found (object %s)\n", *token, objectName);
803 }
804 }
805 break;
806
807 case V_UI_IF(0x8000 + 4):
808 {
809 *token = Com_EParse(text, errhead, objectName);
810 if (!*text)
811 return false;
812
813 uiAction_t*& expression = Com_GetValue<uiAction_t*>(object, property);
814 expression = UI_AllocStaticStringCondition(*token);
815 if (!expression)
816 return false;
817 }
818 break;
819
820 case V_UI_DATAID(0x8000 + 5):
821 {
822 *token = Com_EParse(text, errhead, objectName);
823 if (!*text)
824 return false;
825
826 int& dataId = Com_GetValue<int>(object, property);
827 dataId = UI_GetDataIDByName(*token);
828 if (dataId < 0) {
829 Com_Printf("UI_ParseProperty: Could not find shared data ID '%s' (%s@%s)\n",
830 *token, objectName, property->string);
831 return false;
832 }
833 }
834 break;
835
836 default:
837 Com_Printf("UI_ParseProperty: unknown property type '%d' (0x%X) (%s@%s)\n",
838 property->type, property->type, objectName, property->string);
839 return false;
840 }
841 break;
842
843 default:
844 Com_Printf("UI_ParseProperties: unknown property type '%d' (0x%X) (%s@%s)\n",
845 property->type, property->type, objectName, property->string);
846 return false;
847 }
848
849 return true;
850}
851
852static bool UI_ParseFunction (uiNode_t * node, const char **text, const char **token)
853{
854 uiAction_t **action;
855 assert(UI_Node_IsFunction(node))(__builtin_expect(!(UI_Node_IsFunction(node)), 0) ? __assert_rtn
(__func__, "src/client/ui/ui_parse.cpp", 855, "UI_Node_IsFunction(node)"
) : (void)0)
;
856
857 action = &node->onClick;
858 *action = UI_ParseActionList(node, text, token);
859 if (*action == NULL__null)
860 return false;
861
862 return (*token)[0] == '}';
863}
864
865/**
866 * @sa UI_ParseNodeProperties
867 * @brief parse all sequencial properties into a block
868 * @note allow to use an extra block
869 * @code
870 * foobehaviour foonode {
871 * { properties }
872 * // the function stop reading here
873 * nodes
874 * }
875 * foobehaviour foonode {
876 * properties
877 * // the function stop reading here
878 * nodes
879 * }
880 * @endcode
881 */
882static bool UI_ParseNodeProperties (uiNode_t * node, const char **text, const char **token)
883{
884 const char *errhead = "UI_ParseNodeProperties: unexpected end of file (node";
885 bool nextTokenAlreadyRead = false;
886
887 if ((*token)[0] != '{')
888 nextTokenAlreadyRead = true;
889
890 do {
891 const value_t *val;
892 int result;
893
894 /* get new token */
895 if (!nextTokenAlreadyRead) {
896 *token = Com_EParse(text, errhead, node->name);
897 if (!*text)
898 return false;
899 } else {
900 nextTokenAlreadyRead = false;
901 }
902
903 /* is finished */
904 if ((*token)[0] == '}')
905 break;
906
907 /* find the property */
908 val = UI_GetPropertyFromBehaviour(node->behaviour, *token);
909 if (!val) {
910 /* unknown token, print message and continue */
911 Com_Printf("UI_ParseNodeProperties: unknown property \"%s\", node ignored (node %s)\n",
912 *token, UI_GetPath(node));
913 return false;
914 }
915
916 /* get parameter values */
917 result = UI_ParseProperty(node, val, node->name, text, token);
918 if (!result) {
919 Com_Printf("UI_ParseNodeProperties: Problem with parsing of node property '%s@%s'. See upper\n",
920 UI_GetPath(node), val->string);
921 return false;
922 }
923 } while (*text);
924
925 return true;
926}
927
928/**
929 * @brief Read a node body
930 * @note Node header already read, we are over the node name, or '{'
931 * @code
932 * Allowed syntax
933 * { properties }
934 * OR
935 * { nodes }
936 * OR
937 * { { properties } nodes }
938 * @endcode
939 */
940static bool UI_ParseNodeBody (uiNode_t * node, const char **text, const char **token, const char *errhead)
941{
942 bool result = true;
943
944 if ((*token)[0] != '{') {
945 /* read the body block start */
946 *token = Com_EParse(text, errhead, node->name);
947 if (!*text)
948 return false;
949 if ((*token)[0] != '{') {
950 Com_Printf("UI_ParseNodeBody: node doesn't have body, token '%s' read (node \"%s\")\n", *token, UI_GetPath(node));
951 ui_global.numNodes--;
952 return false;
953 }
954 }
955
956 /* functions are a special case */
957 if (UI_Node_IsFunction(node)) {
958 result = UI_ParseFunction(node, text, token);
959 } else {
960
961 /* check the content */
962 *token = Com_EParse(text, errhead, node->name);
963 if (!*text)
964 return false;
965
966 if ((*token)[0] == '{') {
967 /* we have a special block for properties */
968 result = UI_ParseNodeProperties(node, text, token);
969 if (!result)
970 return false;
971
972 /* move token over the next node behaviour */
973 *token = Com_EParse(text, errhead, node->name);
974 if (!*text)
975 return false;
976
977 /* and then read all nodes */
978 while ((*token)[0] != '}') {
979 uiNode_t *newNode = UI_ParseNode(node, text, token, errhead);
980 if (!newNode)
981 return false;
982
983 *token = Com_EParse(text, errhead, node->name);
984 if (*text == NULL__null)
985 return false;
986 }
987 } else if (UI_GetPropertyFromBehaviour(node->behaviour, *token)) {
988 /* we should have a block with properties only */
989 result = UI_ParseNodeProperties(node, text, token);
990 } else {
991 /* we should have a block with nodes only */
992 while ((*token)[0] != '}') {
993 uiNode_t *newNode = UI_ParseNode(node, text, token, errhead);
994 if (!newNode)
995 return false;
996
997 *token = Com_EParse(text, errhead, node->name);
998 if (*text == NULL__null)
999 return false;
1000 }
1001 }
1002 }
1003 if (!result) {
1004 Com_Printf("UI_ParseNodeBody: node with bad body ignored (node \"%s\")\n", UI_GetPath(node));
1005 ui_global.numNodes--;
1006 return false;
1007 }
1008
1009 /* already check on UI_ParseNodeProperties */
1010 assert((*token)[0] == '}')(__builtin_expect(!((*token)[0] == '}'), 0) ? __assert_rtn(__func__
, "src/client/ui/ui_parse.cpp", 1010, "(*token)[0] == '}'") :
(void)0)
;
1011 return true;
1012}
1013
1014/**
1015 * @brief parse a node
1016 * @sa UI_ParseNodeProperties
1017 * @todo we can think about merging UI_ParseNodeProperties here
1018 * @note first token already read
1019 * @note dont read more than the need token (last right token is '}' of end of node)
1020 */
1021static uiNode_t *UI_ParseNode (uiNode_t * parent, const char **text, const char **token, const char *errhead)
1022{
1023 uiNode_t *node = NULL__null;
1024 uiBehaviour_t *behaviour;
1025 uiNode_t *component = NULL__null;
1026
1027 /* get the behaviour */
1028 behaviour = UI_GetNodeBehaviour(*token);
1029 if (!behaviour) {
1030 component = UI_GetComponent(*token);
1031 }
1032 if (behaviour == NULL__null && component == NULL__null) {
1033 Com_Printf("UI_ParseNode: node behaviour/component '%s' doesn't exists (%s)\n", *token, UI_GetPath(parent));
1034 return NULL__null;
1035 }
1036
1037 /* get the name */
1038 *token = Com_EParse(text, errhead, "");
1039 if (!*text)
1040 return NULL__null;
1041 if (!UI_TokenIsName(*token, Com_GetType(text) == TT_QUOTED_WORD)) {
1042 Com_Printf("UI_ParseNode: \"%s\" is not a well formed node name ([a-zA-Z_][a-zA-Z0-9_]*)\n", *token);
1043 return NULL__null;
1044 }
1045 if (UI_TokenIsReserved(*token)) {
1046 Com_Printf("UI_ParseNode: \"%s\" is a reserved token, we can't call a node with it\n", *token);
1047 return NULL__null;
1048 }
1049
1050 /* test if node already exists */
1051 /* Already existing node should only come from inherited node,we should not have 2 definitions of the same node into the same window. */
1052 if (parent)
1053 node = UI_GetNode(parent, *token);
1054
1055 /* reuse a node */
1056 if (node) {
1057 if (node->behaviour != behaviour) {
1058 Com_Printf("UI_ParseNode: we can't change node type (node \"%s\")\n", UI_GetPath(node));
1059 return NULL__null;
1060 }
1061 Com_DPrintf(DEBUG_CLIENT0x20, "... over-riding node %s\n", UI_GetPath(node));
1062
1063 /* else initialize a component */
1064 } else if (component) {
1065 node = UI_CloneNode(component, NULL__null, true, *token, false);
1066 if (parent) {
1067 if (parent->root)
1068 UI_UpdateRoot(node, parent->root);
1069 UI_AppendNode(parent, node);
1070 }
1071
1072 /* else initialize a new node */
1073 } else {
1074 node = UI_AllocNode(*token, behaviour->name, false);
1075 node->parent = parent;
1076 if (parent)
1077 node->root = parent->root;
1078 /** @todo move it into caller */
1079 if (parent)
1080 UI_AppendNode(parent, node);
1081 }
1082
1083 /* get body */
1084 const bool result = UI_ParseNodeBody(node, text, token, errhead);
1085 if (!result)
1086 return NULL__null;
1087
1088 /* validate properties */
1089 UI_Node_Loaded(node);
1090
1091 return node;
1092}
1093
1094/**
1095 * @brief parses the models.ufo and all files where UI models (menu_model) are defined
1096 * @sa CL_ParseClientData
1097 */
1098bool UI_ParseUIModel (const char *name, const char **text)
1099{
1100 uiModel_t *model;
1101 const char *token;
1102 int i;
1103 const value_t *v = NULL__null;
1104 const char *errhead = "UI_ParseUIModel: unexpected end of file (names ";
1105
1106 /* search for a UI models with same name */
1107 for (i = 0; i < ui_global.numModels; i++)
1
Loop condition is false. Execution continues on line 1113
1108 if (Q_streq(ui_global.models[i].id, name)(strcmp(ui_global.models[i].id, name) == 0)) {
1109 Com_Printf("UI_ParseUIModel: menu_model \"%s\" with same name found, second ignored\n", name);
1110 return false;
1111 }
1112
1113 if (ui_global.numModels >= UI_MAX_MODELS128) {
2
Taking false branch
1114 Com_Printf("UI_ParseUIModel: Max UI models reached\n");
1115 return false;
1116 }
1117
1118 /* initialize the model */
1119 model = &ui_global.models[ui_global.numModels];
1120 OBJZERO(*model)(memset(&((*model)), (0), sizeof((*model))));
1121
1122 Vector4Set(model->color, 1, 1, 1, 1)((model->color)[0]=(1), (model->color)[1]=(1), (model->
color)[2]=(1), (model->color)[3]=(1))
;
1123
1124 model->id = Mem_PoolStrDup(name, ui_sysPool, 0)_Mem_PoolStrDup((name),(ui_sysPool),(0),"src/client/ui/ui_parse.cpp"
,1124)
;
1125 Com_DPrintf(DEBUG_CLIENT0x20, "Found UI model %s (%i)\n", model->id, ui_global.numModels);
1126
1127 /* get it's body */
1128 token = Com_Parse(text);
1129
1130 if (!*text || token[0] != '{') {
3
Taking false branch
1131 Com_Printf("UI_ParseUIModel: Model \"%s\" without body ignored\n", model->id);
1132 return false;
1133 }
1134
1135 ui_global.numModels++;
1136
1137 do {
1138 /* get the name type */
1139 token = Com_EParse(text, errhead, name);
1140 if (!*text)
4
Taking false branch
1141 return false;
1142 if (token[0] == '}')
5
Taking false branch
1143 break;
1144
1145 v = UI_FindPropertyByName(uiModelProperties, token);
1146 if (!v)
6
Taking true branch
1147 Com_Printf("UI_ParseUIModel: unknown token \"%s\" ignored (UI model %s)\n", token, name);
1148
1149 if (v->type == V_NULL) {
7
Access to field 'type' results in a dereference of a null pointer (loaded from variable 'v')
1150 if (Q_streq(v->string, "need")(strcmp(v->string, "need") == 0)) {
1151 token = Com_EParse(text, errhead, name);
1152 if (!*text)
1153 return false;
1154 if (model->next != NULL__null)
1155 Sys_Error("UI_ParseUIModel: second 'need' token found in model %s", name);
1156 model->next = UI_GetUIModel(token);
1157 if (!model->next)
1158 Com_Printf("Could not find UI model %s", token);
1159 }
1160 } else {
1161 token = Com_EParse(text, errhead, name);
1162 if (!*text)
1163 return false;
1164 switch (v->type) {
1165 case V_HUNK_STRING:
1166 Mem_PoolStrDupTo(token, &Com_GetValue<char*>(model, v), ui_sysPool, 0)_Mem_PoolStrDupTo((token),(&Com_GetValue<char*>(model
, v)),(ui_sysPool),(0),"src/client/ui/ui_parse.cpp",1166)
;
1167 break;
1168 default:
1169 Com_EParseValue(model, token, v->type, v->ofs, v->size)Com_EParseValueDebug(model, token, v->type, v->ofs, v->
size, "src/client/ui/ui_parse.cpp", 1169)
;
1170 break;
1171 }
1172 }
1173 } while (*text);
1174
1175 return true;
1176}
1177
1178bool UI_ParseSprite (const char *name, const char **text)
1179{
1180 uiSprite_t *icon;
1181 const char *token;
1182
1183 /* search for icons with same name */
1184 icon = UI_AllocStaticSprite(name);
1185
1186 /* get it's body */
1187 token = Com_Parse(text);
1188 assert(token[0] == '{')(__builtin_expect(!(token[0] == '{'), 0) ? __assert_rtn(__func__
, "src/client/ui/ui_parse.cpp", 1188, "token[0] == '{'") : (void
)0)
;
1189
1190 /* read properties */
1191 while (true) {
1192 const value_t *property;
1193
1194 token = Com_Parse(text);
1195 if (*text == NULL__null)
1196 return false;
1197
1198 if (token[0] == '}')
1199 break;
1200
1201 property = UI_FindPropertyByName(ui_spriteProperties, token);
1202 if (!property) {
1203 Com_Printf("UI_ParseIcon: unknown options property: '%s' - ignore it\n", token);
1204 return false;
1205 }
1206
1207 /* get parameter values */
1208 const bool result = UI_ParseProperty(icon, property, icon->name, text, &token);
1209 if (!result) {
1210 Com_Printf("UI_ParseIcon: Parsing for sprite '%s'. See upper\n", icon->name);
1211 return false;
1212 }
1213 }
1214
1215 return true;
1216}
1217
1218/**
1219 * @brief Parse a component
1220 * @sa CL_ParseClientData
1221 * @code
1222 * component panel componentName {
1223 * }
1224 * @endcode
1225 */
1226bool UI_ParseComponent (const char *type, const char *name, const char **text)
1227{
1228 const char *errhead = "UI_ParseComponent: unexpected end of file (component";
1229 const char *token;
1230
1231 if (!Q_streq(type, "component")(strcmp(type, "component") == 0)) {
1232 Com_Error(ERR_FATAL0, "UI_ParseComponent: \"component\" expected but \"%s\" found.\n", type);
1233 return false; /* never reached */
1234 }
1235
1236 /* check the name */
1237 if (!UI_TokenIsName(name, false)) {
1238 Com_Printf("UI_ParseNode: \"%s\" is not a well formed node name ([a-zA-Z_][a-zA-Z0-9_]*)\n", name);
1239 return false;
1240 }
1241 if (UI_TokenIsReserved(name)) {
1242 Com_Printf("UI_ParseNode: \"%s\" is a reserved token, we can't call a node with it\n", name);
1243 return false;
1244 }
1245
1246 token = Com_EParse(text, errhead, "");
1247 if (text == NULL__null)
1248 return false;
1249
1250 /* get keyword */
1251 if (!Q_streq(token, "extends")(strcmp(token, "extends") == 0)) {
1252 Com_Printf("UI_ParseComponent: \"extends\" expected but \"%s\" found (component %s)\n", token, name);
1253 return false;
1254 }
1255 token = Com_EParse(text, errhead, "");
1256 if (text == NULL__null)
1257 return false;
1258
1259 /* initialize component */
1260 uiNode_t *component = NULL__null;
1261 const uiBehaviour_t *behaviour = UI_GetNodeBehaviour(token);
1262 if (behaviour) {
1263 /* initialize a new node from behaviour */
1264 component = UI_AllocNode(name, behaviour->name, false);
1265 } else {
1266 const uiNode_t *inheritedComponent = UI_GetComponent(token);
1267 if (inheritedComponent) {
1268 /* initialize from a component */
1269 component = UI_CloneNode(inheritedComponent, NULL__null, true, name, false);
1270 } else {
1271 Com_Printf("UI_ParseComponent: node behaviour/component '%s' doesn't exists (component %s)\n", token, name);
1272 return false;
1273 }
1274 }
1275
1276 /* get body */
1277 token = Com_EParse(text, errhead, "");
1278 if (!*text)
1279 return false;
1280 bool result = UI_ParseNodeBody(component, text, &token, errhead);
1281 if (!result)
1282 return false;
1283
1284 /* validate properties */
1285 UI_Node_Loaded(component);
1286
1287 UI_InsertComponent(component);
1288 return true;
1289}
1290
1291
1292/**
1293 * @brief Parse a window
1294 * @sa CL_ParseClientData
1295 * @code
1296 * window windowName {
1297 * }
1298 * @endcode
1299 */
1300bool UI_ParseWindow (const char *type, const char *name, const char **text)
1301{
1302 const char *errhead = "UI_ParseWindow: unexpected end of file (window";
1303 uiNode_t *window;
1304 const char *token;
1305 int i;
1306
1307 if (!Q_streq(type, "window")(strcmp(type, "window") == 0)) {
1308 Com_Error(ERR_FATAL0, "UI_ParseWindow: '%s %s' is not a window node\n", type, name);
1309 return false; /* never reached */
1310 }
1311
1312 if (!UI_TokenIsName(name, Com_GetType(text) == TT_QUOTED_WORD)) {
1313 Com_Printf("UI_ParseWindow: \"%s\" is not a well formed node name ([a-zA-Z_][a-zA-Z0-9_]*)\n", name);
1314 return false;
1315 }
1316 if (UI_TokenIsReserved(name)) {
1317 Com_Printf("UI_ParseWindow: \"%s\" is a reserved token, we can't call a node with it (node \"%s\")\n", name, name);
1318 return false;
1319 }
1320
1321 /* search for windows with same name */
1322 for (i = 0; i < ui_global.numWindows; i++)
1323 if (!strncmp(name, ui_global.windows[i]->name, sizeof(ui_global.windows[i]->name)))
1324 break;
1325
1326 if (i < ui_global.numWindows) {
1327 Com_Printf("UI_ParseWindow: %s \"%s\" with same name found, second ignored\n", type, name);
1328 }
1329
1330 if (ui_global.numWindows >= UI_MAX_WINDOWS128) {
1331 Com_Error(ERR_FATAL0, "UI_ParseWindow: max windows exceeded (%i) - ignore '%s'\n", UI_MAX_WINDOWS128, name);
1332 return false; /* never reached */
1333 }
1334
1335 /* get window body */
1336 token = Com_Parse(text);
1337
1338 /* does this window inherit data from another window? */
1339 if (Q_streq(token, "extends")(strcmp(token, "extends") == 0)) {
1340 uiNode_t *superWindow;
1341 token = Com_Parse(text);
1342 superWindow = UI_GetWindow(token);
1343 if (superWindow == NULL__null)
1344 Sys_Error("Could not get the super window \"%s\"", token);
1345 window = UI_CloneNode(superWindow, NULL__null, true, name, false);
1346 token = Com_Parse(text);
1347 } else {
1348 window = UI_AllocNode(name, type, false);
1349 window->root = window;
1350 }
1351
1352 UI_InsertWindow(window);
1353
1354 /* parse it's body */
1355 bool result = UI_ParseNodeBody(window, text, &token, errhead);
1356 if (!result) {
1357 Com_Error(ERR_FATAL0, "UI_ParseWindow: window \"%s\" has a bad body\n", window->name);
1358 }
1359
1360 UI_Node_Loaded(window);
1361 return true;
1362}
1363
1364/**
1365 * @sa Com_MacroExpandString
1366 * @todo we should review this code, '*' doesn't work very well for all the needed things
1367 */
1368const char *UI_GetReferenceString (const uiNode_t* const node, const char *ref)
1369{
1370 if (!ref)
1371 return NULL__null;
1372
1373 /* its a cvar */
1374 if (ref[0] == '*') {
1375 const char *token;
1376
1377 /* get the reference and the name */
1378 token = Com_MacroExpandString(ref);
1379 if (token) {
1380 if (token[0] == '_') {
1381 token++;
1382 return _(token)gettext(token);
1383 }
1384 return token;
1385 }
1386
1387 /* skip the star */
1388 token = ref + 1;
1389 if (token[0] == '\0')
1390 return NULL__null;
1391
1392 if (char const* const binding = Q_strstart(token, "binding:")) {
1393 return Key_GetBinding(binding, cls.state != ca_active ? KEYSPACE_UI : KEYSPACE_GAME);
1394 }
1395
1396 Sys_Error("UI_GetReferenceString: unknown reference %s", token);
1397 /* translatable string */
1398 } else if (ref[0] == '_') {
1399 ref++;
1400 return _(ref)gettext(ref);
1401 }
1402
1403 /* just a string */
1404 return ref;
1405}
1406
1407float UI_GetReferenceFloat (const uiNode_t* const node, const void *ref)
1408{
1409 if (!ref)
1410 return 0.0;
1411 if (char const* const token = Q_strstart((char const*)ref, "*")) {
1412 if (token[0] == '\0')
1413 return 0.0;
1414
1415 if (char const* const cvar = Q_strstart(token, "cvar:")) {
1416 return Cvar_GetValue(cvar);
1417 }
1418
1419 Sys_Error("UI_GetReferenceFloat: unknown reference '%s' from node '%s'", token, node->name);
1420 }
1421
1422 /* just get the data */
1423 return *(const float *) ref;
1424}