00001
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "../client.h"
00028 #include "ui_parse.h"
00029 #include "ui_main.h"
00030 #include "ui_data.h"
00031 #include "ui_internal.h"
00032 #include "ui_actions.h"
00033 #include "ui_icon.h"
00034 #include "ui_components.h"
00035 #include "node/ui_node_window.h"
00036 #include "node/ui_node_selectbox.h"
00037 #include "node/ui_node_abstractnode.h"
00038 #include "node/ui_node_abstractoption.h"
00039
00040 #include "../../shared/parse.h"
00041
00043 static qboolean UI_ParseProperty(void* object, const value_t *property, const char* objectName, const char **text, const char **token);
00044 static uiAction_t *UI_ParseActionList(uiNode_t *node, const char **text, const const char **token);
00045 static uiNode_t *UI_ParseNode(uiNode_t * parent, const char **text, const char **token, const char *errhead);
00046
00048 static const value_t uiModelProperties[] = {
00049 {"model", V_CLIENT_HUNK_STRING, offsetof(uiModel_t, model), 0},
00050 {"need", V_NULL, 0, 0},
00051 {"anim", V_CLIENT_HUNK_STRING, offsetof(uiModel_t, anim), 0},
00052 {"skin", V_INT, offsetof(uiModel_t, skin), sizeof(int)},
00053 {"color", V_COLOR, offsetof(uiModel_t, color), sizeof(vec4_t)},
00054 {"tag", V_CLIENT_HUNK_STRING, offsetof(uiModel_t, tag), 0},
00055 {"parent", V_CLIENT_HUNK_STRING, offsetof(uiModel_t, parent), 0},
00056
00057 {NULL, V_NULL, 0, 0},
00058 };
00059
00063 static const char *reserved_tokens[] = {
00064 "this",
00065 "parent",
00066 "root",
00067 "null",
00068 "super",
00069 "node",
00070 "cvar",
00071 "int",
00072 "float",
00073 "string",
00074 "var",
00075 NULL
00076 };
00077
00078 static qboolean UI_TokenIsReserved (const char *name)
00079 {
00080 const char **token = reserved_tokens;
00081 while (*token) {
00082 if (!strcmp(*token, name))
00083 return qtrue;
00084 token++;
00085 }
00086 return qfalse;
00087 }
00088
00089 static qboolean UI_TokenIsValue (const char *name, qboolean isQuoted)
00090 {
00091 assert(name);
00092 if (isQuoted)
00093 return qtrue;
00094
00095 if ((name[0] >= '0' && name[0] <= '9') || name[0] == '-' || name[0] == '.')
00096 return qtrue;
00097
00098 if (name[0] == '*')
00099 return qtrue;
00100 if (!strcmp(name, "true"))
00101 return qtrue;
00102 if (!strcmp(name, "false"))
00103 return qtrue;
00104
00105
00106 if ((name[0] >= 'A' && name[0] <= 'Z') || name[0] == '_') {
00107 qboolean onlyUpperCase = qtrue;
00108 while (*name != '\0') {
00109 if ((name[0] >= 'A' && name[0] <= 'Z') || name[0] == '_' || (name[0] >= '0' && name[0] <= '9')) {
00110
00111 } else {
00112 return qfalse;
00113 }
00114 name++;
00115 }
00116 return onlyUpperCase;
00117 }
00118
00119 return qfalse;
00120 }
00121
00122 static qboolean UI_TokenIsName (const char *name, qboolean isQuoted)
00123 {
00124 assert(name);
00125 if (isQuoted)
00126 return qfalse;
00127 if ((name[0] >= 'a' && name[0] <= 'z') || (name[0] >= 'A' && name[0] <= 'Z') || name[0] == '_') {
00128 qboolean onlyUpperCase = qtrue;
00129 while (*name != '\0') {
00130 if (name[0] >= 'a' && name[0] <= 'z') {
00131 onlyUpperCase = qfalse;
00132 } else if ((name[0] >= '0' && name[0] <= '9') || (name[0] >= 'A' && name[0] <= 'Z') || name[0] == '_') {
00133
00134 } else {
00135 return qfalse;
00136 }
00137 name++;
00138 }
00139 return !onlyUpperCase;
00140 }
00141 return qfalse;
00142 }
00143
00150 const value_t* UI_FindPropertyByName (const value_t* propertyList, const char* name)
00151 {
00152 const value_t* current = propertyList;
00153 while (current->string != NULL) {
00154 if (!Q_strcasecmp(name, current->string))
00155 return current;
00156 current++;
00157 }
00158 return NULL;
00159 }
00160
00167 float* UI_AllocStaticFloat (int count)
00168 {
00169 float *result;
00170 assert(count > 0);
00171 ui_global.curadata = ALIGN_PTR(ui_global.curadata, sizeof(float));
00172 result = (float*) ui_global.curadata;
00173 ui_global.curadata += sizeof(float) * count;
00174 if (ui_global.curadata - ui_global.adata > ui_global.adataize)
00175 Com_Error(ERR_FATAL, "UI_AllocFloat: UI memory hunk exceeded - increase the size");
00176 return result;
00177 }
00178
00185 vec4_t* UI_AllocStaticColor (int count)
00186 {
00187 vec4_t *result;
00188 assert(count > 0);
00189 ui_global.curadata = ALIGN_PTR(ui_global.curadata, sizeof(vec_t));
00190 result = (vec4_t*) ui_global.curadata;
00191 ui_global.curadata += sizeof(vec_t) * 4 * count;
00192 if (ui_global.curadata - ui_global.adata > ui_global.adataize)
00193 Com_Error(ERR_FATAL, "UI_AllocColor: UI memory hunk exceeded - increase the size");
00194 return result;
00195 }
00196
00204 char* UI_AllocStaticString (const char* string, int size)
00205 {
00206 char* result = (char *)ui_global.curadata;
00207 ui_global.curadata = ALIGN_PTR(ui_global.curadata, sizeof(char));
00208 if (size != 0) {
00209 if (ui_global.curadata - ui_global.adata + size > ui_global.adataize)
00210 Com_Error(ERR_FATAL, "UI_AllocString: UI memory hunk exceeded - increase the size");
00211 strncpy((char *)ui_global.curadata, string, size);
00212 ui_global.curadata += size;
00213 } else {
00214 if (ui_global.curadata - ui_global.adata + strlen(string) + 1 > ui_global.adataize)
00215 Com_Error(ERR_FATAL, "UI_AllocString: UI memory hunk exceeded - increase the size");
00216 ui_global.curadata += sprintf((char *)ui_global.curadata, "%s", string) + 1;
00217 }
00218 return result;
00219 }
00220
00225 uiAction_t *UI_AllocStaticAction (void)
00226 {
00227 if (ui_global.numActions >= UI_MAX_ACTIONS)
00228 Com_Error(ERR_FATAL, "UI_AllocAction: Too many UI actions");
00229 return &ui_global.actions[ui_global.numActions++];
00230 }
00231
00242 qboolean UI_InitRawActionValue (uiAction_t* action, uiNode_t *node, const value_t *property, const char *string)
00243 {
00244 if (property == NULL) {
00245 action->type = EA_VALUE_STRING;
00246 action->d.terminal.d1.data = UI_AllocStaticString(string, 0);
00247 action->d.terminal.d2.integer = 0;
00248 return qtrue;
00249 }
00250
00251 if (property->type == V_UI_ICONREF) {
00252 uiIcon_t* icon = UI_GetIconByName(string);
00253 if (icon == NULL) {
00254 Com_Printf("UI_ParseSetAction: icon '%s' not found (%s)\n", string, UI_GetPath(node));
00255 return qfalse;
00256 }
00257 action->type = EA_VALUE_RAW;
00258 action->d.terminal.d1.data = icon;
00259 action->d.terminal.d2.integer = property->type;
00260 return qtrue;
00261 } else {
00262 const int baseType = property->type & V_UI_MASK;
00263 if (baseType != 0 && baseType != V_UI_CVAR) {
00264 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));
00265 return qfalse;
00266 }
00267 ui_global.curadata = Com_AlignPtr(ui_global.curadata, property->type & V_BASETYPEMASK);
00268 action->type = EA_VALUE_RAW;
00269 action->d.terminal.d1.data = ui_global.curadata;
00270 action->d.terminal.d2.integer = property->type;
00272 ui_global.curadata += Com_EParseValue(ui_global.curadata, string, property->type & V_BASETYPEMASK, 0, property->size);
00273 return qtrue;
00274 }
00275 }
00276
00280 static qboolean UI_ParseSetAction (uiNode_t *node, uiAction_t *action, const char **text, const char **token, const char *errhead)
00281 {
00282 const value_t *property;
00283 int type;
00284 uiAction_t* localAction;
00285
00286 assert((*token)[0] == '*');
00287
00288 Com_UnParseLastToken();
00289 action->d.nonTerminal.left = UI_ParseExpression(text);
00290
00291 type = action->d.nonTerminal.left->type;
00292 if (type != EA_VALUE_CVARNAME && type != EA_VALUE_CVARNAME_WITHINJECTION
00293 && type != EA_VALUE_PATHPROPERTY && type != EA_VALUE_PATHPROPERTY_WITHINJECTION) {
00294 Com_Printf("UI_ParseSetAction: Cvar or Node property expected. Type '%i' found\n", type);
00295 return qfalse;
00296 }
00297
00298
00299 *token = Com_EParse(text, errhead, NULL);
00300 if (!*text)
00301 return qfalse;
00302 if (strcmp(*token, "=") != 0) {
00303 Com_Printf("UI_ParseSetAction: Assign sign '=' expected between variable and value. '%s' found in node %s.\n", *token, UI_GetPath(node));
00304 return qfalse;
00305 }
00306
00307
00308 if (type == EA_VALUE_CVARNAME || type == EA_VALUE_CVARNAME_WITHINJECTION) {
00309 action->d.nonTerminal.right = UI_ParseExpression(text);
00310 return qtrue;
00311 }
00312
00313 property = (const value_t *) action->d.nonTerminal.left->d.terminal.d2.data;
00314
00315 *token = Com_EParse(text, errhead, NULL);
00316 if (!*text)
00317 return qfalse;
00318
00319 if (!strcmp(*token, "{")) {
00320 uiAction_t* actionList;
00321
00322 if (property != NULL && property->type != V_UI_ACTION) {
00323 Com_Printf("UI_ParseSetAction: Property %s@%s do not expect code block.\n", UI_GetPath(node), property->string);
00324 return qfalse;
00325 }
00326
00327 actionList = UI_ParseActionList(node, text, token);
00328 if (actionList == NULL)
00329 return qfalse;
00330
00331 localAction = UI_AllocStaticAction();
00332 localAction->type = EA_VALUE_RAW;
00333 localAction->d.terminal.d1.data = actionList;
00334 localAction->d.terminal.d2.integer = V_UI_ACTION;
00335 action->d.nonTerminal.right = localAction;
00336
00337 return qtrue;
00338 }
00339
00340 if (!strcmp(*token, "(")) {
00341 Com_UnParseLastToken();
00342 action->d.nonTerminal.right = UI_ParseExpression(text);
00343 return qtrue;
00344 }
00345
00346
00347
00348 if (UI_IsInjectedString(*token)) {
00349 localAction = UI_AllocStaticAction();
00350 localAction->type = EA_VALUE_STRING_WITHINJECTION;
00351 localAction->d.terminal.d1.data = UI_AllocStaticString(*token, 0);
00352 action->d.nonTerminal.right = localAction;
00353 return qtrue;
00354 }
00355
00356 localAction = UI_AllocStaticAction();
00357 UI_InitRawActionValue(localAction, node, property, *token);
00358 action->d.nonTerminal.right = localAction;
00359 return qtrue;
00360 }
00361
00365 static qboolean UI_ParseCallAction (uiNode_t *node, uiAction_t *action, const char **text, const char **token, const char *errhead)
00366 {
00367 uiAction_t *expression;
00368 uiAction_t *lastParam = NULL;
00369 int paramID = 0;
00370 expression = UI_ParseExpression(text);
00371 if (expression == NULL)
00372 return qfalse;
00373
00374 if (expression->type != EA_VALUE_PATHNODE_WITHINJECTION && expression->type != EA_VALUE_PATHNODE && expression->type != EA_VALUE_PATHPROPERTY && expression->type != EA_VALUE_PATHPROPERTY_WITHINJECTION) {
00375 Com_Printf("UI_ParseCallAction: \"call\" keyword only support pathnode and pathproperty (node: %s)\n", UI_GetPath(node));
00376 return qfalse;
00377 }
00378
00379 action->d.nonTerminal.left = expression;
00380
00381
00382 *token = Com_EParse(text, errhead, NULL);
00383 if ((*token)[0] == '\0')
00384 return qfalse;
00385
00386
00387 if (strcmp(*token, "(") != 0) {
00388 Com_UnParseLastToken();
00389 return qtrue;
00390 }
00391
00392
00393 do {
00394 uiAction_t *param;
00395 paramID++;
00396
00397
00398 param = UI_ParseExpression(text);
00399 if (param == NULL) {
00400 Com_Printf("UI_ParseCallAction: problem with the %i parameter\n", paramID);
00401 return qfalse;
00402 }
00403 if (lastParam == NULL)
00404 action->d.nonTerminal.right = param;
00405 else
00406 lastParam->next = param;
00407 lastParam = param;
00408
00409
00410 *token = Com_EParse(text, errhead, NULL);
00411 if (!*token)
00412 return qfalse;
00413 if (strcmp(*token, ",") != 0) {
00414 if (strcmp(*token, ")") == 0)
00415 break;
00416 Com_UnParseLastToken();
00417 Com_Printf("UI_ParseCallAction: Invalidate end of 'call' after param %i\n", paramID);
00418 return qfalse;
00419 }
00420 } while(qtrue);
00421
00422 return qtrue;
00423 }
00424
00431 static uiAction_t *UI_ParseActionList (uiNode_t *node, const char **text, const const char **token)
00432 {
00433 const char *errhead = "UI_ParseActionList: unexpected end of file (in event)";
00434 uiAction_t *firstAction;
00435 uiAction_t *lastAction;
00436 uiAction_t *action;
00437 qboolean result;
00438
00439 lastAction = NULL;
00440 firstAction = NULL;
00441
00442
00443 if ((*token)[0] != '{') {
00444 Com_Printf("UI_ParseActionList: token \"{\" expected, but \"%s\" found (in event) (node: %s)\n", *token, UI_GetPath(node));
00445 return NULL;
00446 }
00447
00448 while (qtrue) {
00449 int type = EA_NULL;
00450
00451
00452 *token = Com_EParse(text, errhead, NULL);
00453 if (!*token)
00454 return NULL;
00455
00456 if ((*token)[0] == '}')
00457 break;
00458
00459 type = UI_GetActionTokenType(*token, EA_ACTION);
00460
00461 if (type == EA_NULL && (*token)[0] == '*')
00462 type = EA_ASSIGN;
00463
00464
00465 if (type == EA_NULL) {
00466 Com_Printf("UI_ParseActionList: unknown token \"%s\" ignored (in event) (node: %s)\n", *token, UI_GetPath(node));
00467 return NULL;
00468 }
00469
00470
00471 action = UI_AllocStaticAction();
00473 if (lastAction)
00474 lastAction->next = action;
00475 if (!firstAction)
00476 firstAction = action;
00477 action->type = type;
00478
00479
00480 switch (action->type) {
00481 case EA_CMD:
00482
00483 *token = Com_EParse(text, errhead, NULL);
00484 if (!*text)
00485 return NULL;
00486
00487
00488 action->d.terminal.d1.string = UI_AllocStaticString(*token, 0);
00489 break;
00490
00491 case EA_ASSIGN:
00492 result = UI_ParseSetAction(node, action, text, token, errhead);
00493 if (!result)
00494 return NULL;
00495 break;
00496
00497 case EA_CALL:
00498 result = UI_ParseCallAction(node, action, text, token, errhead);
00499 if (!result)
00500 return NULL;
00501 break;
00502
00503 case EA_DELETE:
00504 {
00505 uiAction_t *expression;
00506 expression = UI_ParseExpression(text);
00507 if (expression == NULL)
00508 return NULL;
00509
00510 if (expression->type != EA_VALUE_CVARNAME) {
00511 Com_Printf("UI_ParseActionList: \"delete\" keyword only support cvarname (node: %s)\n", UI_GetPath(node));
00512 return NULL;
00513 }
00514
00515 action->d.nonTerminal.left = expression;
00516 break;
00517 }
00518
00519 case EA_ELIF:
00520
00521 if (!lastAction || (lastAction->type != EA_IF && lastAction->type != EA_ELIF)) {
00522 Com_Printf("UI_ParseActionList: 'elif' must be set after an 'if' or an 'elif' (node: %s)\n", UI_GetPath(node));
00523 return NULL;
00524 }
00525
00526 case EA_WHILE:
00527 case EA_IF:
00528 {
00529 uiAction_t *expression;
00530
00531
00532 expression = UI_ParseExpression(text);
00533 if (expression == NULL)
00534 return NULL;
00535 action->d.nonTerminal.left = expression;
00536
00537
00538 *token = Com_EParse(text, errhead, NULL);
00539 if (!*text)
00540 return NULL;
00541 action->d.nonTerminal.right = UI_ParseActionList(node, text, token);
00542 if (action->d.nonTerminal.right == NULL) {
00543 if (action->type == EA_IF)
00544 Com_Printf("UI_ParseActionList: block expected after \"if\" (node: %s)\n", UI_GetPath(node));
00545 else if (action->type == EA_ELIF)
00546 Com_Printf("UI_ParseActionList: block expected after \"elif\" (node: %s)\n", UI_GetPath(node));
00547 else
00548 Com_Printf("UI_ParseActionList: block expected after \"while\" (node: %s)\n", UI_GetPath(node));
00549 return NULL;
00550 }
00551 break;
00552 }
00553
00554 case EA_ELSE:
00555
00556 if (!lastAction || (lastAction->type != EA_IF && lastAction->type != EA_ELIF)) {
00557 Com_Printf("UI_ParseActionList: 'else' must be set after an 'if' or an 'elif' (node: %s)\n", UI_GetPath(node));
00558 return NULL;
00559 }
00560
00561
00562 *token = Com_EParse(text, errhead, NULL);
00563 if (!*text)
00564 return NULL;
00565 action->d.nonTerminal.left = NULL;
00566 action->d.nonTerminal.right = UI_ParseActionList(node, text, token);
00567 if (action->d.nonTerminal.right == NULL) {
00568 Com_Printf("UI_ParseActionList: block expected after \"else\" (node: %s)\n", UI_GetPath(node));
00569 return NULL;
00570 }
00571 break;
00572
00573 default:
00574 assert(qfalse);
00575 }
00576
00577
00578 lastAction = action;
00579 }
00580
00581 assert((*token)[0] == '}');
00582
00583
00584 if (firstAction == NULL) {
00585 firstAction = UI_AllocStaticAction();
00586 }
00587
00588 return firstAction;
00589 }
00590
00591 static qboolean UI_ParseExcludeRect (uiNode_t * node, const char **text, const char **token, const char *errhead)
00592 {
00593 uiExcludeRect_t rect;
00594
00595
00596 *token = Com_EParse(text, errhead, node->name);
00597 if (!*text)
00598 return qfalse;
00599 if ((*token)[0] != '{') {
00600 Com_Printf("UI_ParseExcludeRect: node with bad excluderect ignored (node \"%s\")\n", UI_GetPath(node));
00601 return qtrue;
00602 }
00603
00604 do {
00605 *token = Com_EParse(text, errhead, node->name);
00606 if (!*text)
00607 return qfalse;
00609 if (!strcmp(*token, "pos")) {
00610 *token = Com_EParse(text, errhead, node->name);
00611 if (!*text)
00612 return qfalse;
00613 Com_EParseValue(&rect, *token, V_POS, offsetof(uiExcludeRect_t, pos), sizeof(vec2_t));
00614 } else if (!strcmp(*token, "size")) {
00615 *token = Com_EParse(text, errhead, node->name);
00616 if (!*text)
00617 return qfalse;
00618 Com_EParseValue(&rect, *token, V_POS, offsetof(uiExcludeRect_t, size), sizeof(vec2_t));
00619 }
00620 } while ((*token)[0] != '}');
00621
00622 if (ui_global.numExcludeRect >= UI_MAX_EXLUDERECTS) {
00623 Com_Printf("UI_ParseExcludeRect: exluderect limit exceeded (max: %i)\n", UI_MAX_EXLUDERECTS);
00624 return qfalse;
00625 }
00626
00627
00628 ui_global.excludeRect[ui_global.numExcludeRect] = rect;
00629
00630
00631 if (node->excludeRect == NULL) {
00632 node->excludeRect = &ui_global.excludeRect[ui_global.numExcludeRect];
00633 }
00634
00635 ui_global.numExcludeRect++;
00636 node->excludeRectNum++;
00637
00638 return qtrue;
00639 }
00640
00641 static qboolean UI_ParseEventProperty (uiNode_t * node, const value_t *event, const char **text, const char **token, const char *errhead)
00642 {
00643 uiAction_t **action;
00644
00645
00646 action = (uiAction_t **) ((byte *) node + event->ofs);
00647 for (; *action; action = &(*action)->next) {}
00648
00649
00650 *token = Com_EParse(text, errhead, node->name);
00651 if (!*text)
00652 return qfalse;
00653
00654 if ((*token)[0] != '{') {
00655 Com_Printf("UI_ParseEventProperty: Event '%s' without body (%s)\n", event->string, UI_GetPath(node));
00656 return qfalse;
00657 }
00658
00659 Com_EnableFunctionScriptToken(qtrue);
00660
00661 *action = UI_ParseActionList(node, text, token);
00662 if (*action == NULL)
00663 return qfalse;
00664
00665 Com_EnableFunctionScriptToken(qfalse);
00666
00667
00668 assert((*token)[0] == '}');
00669
00670 return qtrue;
00671 }
00672
00677 static qboolean UI_ParseProperty (void* object, const value_t *property, const char* objectName, const char **text, const char **token)
00678 {
00679 const char *errhead = "UI_ParseProperty: unexpected end of file (object";
00680 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";
00681 size_t bytes;
00682 void *valuePtr = (void*) ((uintptr_t)object + property->ofs);
00683 int result;
00684 const int specialType = property->type & V_UI_MASK;
00685
00686 if (property->type == V_NULL) {
00687 return qfalse;
00688 }
00689
00690 switch (specialType) {
00691 case V_NOT_UI:
00692
00693 *token = Com_EParse(text, errhead, objectName);
00694 if (!*text)
00695 return qfalse;
00696 if (!UI_TokenIsValue(*token, Com_ParsedTokenIsQuoted())) {
00697 Com_Printf(notWellFormedValue, *token);
00698 return qfalse;
00699 }
00700
00701 if (property->type == V_TRANSLATION_STRING) {
00702
00703 char *target = (char*) valuePtr;
00704 const char *translatableToken = *token;
00705 assert(property->size);
00706 if (translatableToken[0] == '_')
00707 translatableToken++;
00708 Q_strncpyz(target, translatableToken, property->size);
00709 } else {
00710 result = Com_ParseValue(object, *token, property->type, property->ofs, property->size, &bytes);
00711 if (result != RESULT_OK) {
00712 Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
00713 return qfalse;
00714 }
00715 }
00716 break;
00717
00718 case V_UI_REF:
00719 *token = Com_EParse(text, errhead, objectName);
00720 if (!*text)
00721 return qfalse;
00722 if (!UI_TokenIsValue(*token, Com_ParsedTokenIsQuoted())) {
00723 Com_Printf(notWellFormedValue, *token);
00724 return qfalse;
00725 }
00726
00727
00728 ui_global.curadata = Com_AlignPtr(ui_global.curadata, property->type & V_BASETYPEMASK);
00729 *(byte **) ((byte *) object + property->ofs) = ui_global.curadata;
00730
00732 assert((*token)[0] != '*');
00733
00734
00735 if ((property->type & V_BASETYPEMASK) == V_STRING && strlen(*token) > MAX_VAR - 1) {
00736 Com_Printf("UI_ParseProperty: Value '%s' is too long (key %s)\n", *token, property->string);
00737 return qfalse;
00738 }
00739
00740 result = Com_ParseValue(ui_global.curadata, *token, property->type & V_BASETYPEMASK, 0, property->size, &bytes);
00741 if (result != RESULT_OK) {
00742 Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
00743 return qfalse;
00744 }
00745 ui_global.curadata += bytes;
00746
00747 break;
00748
00749 case V_UI_CVAR:
00750 *token = Com_EParse(text, errhead, objectName);
00751 if (!*text)
00752 return qfalse;
00753 if (!UI_TokenIsValue(*token, Com_ParsedTokenIsQuoted())) {
00754 Com_Printf(notWellFormedValue, *token);
00755 return qfalse;
00756 }
00757
00758
00759 if ((*token)[0] == '*') {
00760
00761 ui_global.curadata = Com_AlignPtr(ui_global.curadata, V_STRING);
00762 *(byte **) valuePtr = ui_global.curadata;
00763
00764
00765 if (strlen(*token) > MAX_VAR - 1) {
00766 Com_Printf("UI_ParseProperty: Value '%s' is too long (key %s)\n", *token, property->string);
00767 return qfalse;
00768 }
00769
00770 result = Com_ParseValue(ui_global.curadata, *token, V_STRING, 0, 0, &bytes);
00771 if (result != RESULT_OK) {
00772 Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
00773 return qfalse;
00774 }
00775 ui_global.curadata += bytes;
00776 } else {
00777
00778 ui_global.curadata = Com_AlignPtr(ui_global.curadata, property->type & V_BASETYPEMASK);
00779 *(byte **) valuePtr = ui_global.curadata;
00780
00781
00782 if ((property->type & V_BASETYPEMASK) == V_STRING && strlen(*token) > MAX_VAR - 1) {
00783 Com_Printf("UI_ParseProperty: Value '%s' is too long (key %s)\n", *token, property->string);
00784 return qfalse;
00785 }
00786
00787 result = Com_ParseValue(ui_global.curadata, *token, property->type & V_BASETYPEMASK, 0, property->size, &bytes);
00788 if (result != RESULT_OK) {
00789 Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
00790 return qfalse;
00791 }
00792 ui_global.curadata += bytes;
00793 }
00794 break;
00795
00796 case V_UI:
00797
00798 switch (property->type) {
00799 case V_UI_ACTION:
00800 result = UI_ParseEventProperty(object, property, text, token, errhead);
00801 if (!result)
00802 return qfalse;
00803 break;
00804
00805 case V_UI_EXCLUDERECT:
00806 result = UI_ParseExcludeRect(object, text, token, errhead);
00807 if (!result)
00808 return qfalse;
00809 break;
00810
00811 case V_UI_ICONREF:
00812 {
00813 uiIcon_t** icon = (uiIcon_t**) valuePtr;
00814 *token = Com_EParse(text, errhead, objectName);
00815 if (!*text)
00816 return qfalse;
00817
00818 *icon = UI_GetIconByName(*token);
00819 if (*icon == NULL) {
00820 Com_Printf("UI_ParseProperty: icon '%s' not found (object %s)\n", *token, objectName);
00821 }
00822 }
00823 break;
00824
00825 case V_UI_IF:
00826 {
00827 uiAction_t **expression = (uiAction_t **) valuePtr;
00828
00829 *token = Com_EParse(text, errhead, objectName);
00830 if (!*text)
00831 return qfalse;
00832
00833 *expression = UI_AllocStaticStringCondition(*token);
00834 if (*expression == NULL)
00835 return qfalse;
00836 }
00837 break;
00838
00839 case V_UI_DATAID:
00840 {
00841 int *dataId = (int*) valuePtr;
00842 *token = Com_EParse(text, errhead, objectName);
00843 if (!*text)
00844 return qfalse;
00845
00846 *dataId = UI_GetDataIDByName(*token);
00847 if (*dataId < 0) {
00848 Com_Printf("UI_ParseProperty: Could not find shared data ID '%s' (%s@%s)\n",
00849 *token, objectName, property->string);
00850 return qfalse;
00851 }
00852 }
00853 break;
00854
00855 default:
00856 Com_Printf("UI_ParseProperty: unknown property type '%d' (0x%X) (%s@%s)\n",
00857 property->type, property->type, objectName, property->string);
00858 return qfalse;
00859 }
00860 break;
00861
00862 default:
00863 Com_Printf("UI_ParseProperties: unknown property type '%d' (0x%X) (%s@%s)\n",
00864 property->type, property->type, objectName, property->string);
00865 return qfalse;
00866 }
00867
00868 return qtrue;
00869 }
00870
00871 static qboolean UI_ParseFunction (uiNode_t * node, const char **text, const char **token)
00872 {
00873 uiAction_t **action;
00874 assert(node->behaviour->isFunction);
00875
00876 Com_EnableFunctionScriptToken(qtrue);
00877
00878 action = &node->onClick;
00879 *action = UI_ParseActionList(node, text, token);
00880 if (*action == NULL)
00881 return qfalse;
00882
00883 Com_EnableFunctionScriptToken(qfalse);
00884
00885 return (*token)[0] == '}';
00886 }
00887
00905 static qboolean UI_ParseNodeProperties (uiNode_t * node, const char **text, const char **token)
00906 {
00907 const char *errhead = "UI_ParseNodeProperties: unexpected end of file (node";
00908 qboolean nextTokenAlreadyRead = qfalse;
00909
00910 if ((*token)[0] != '{')
00911 nextTokenAlreadyRead = qtrue;
00912
00913 do {
00914 const value_t *val;
00915 int result;
00916
00917
00918 if (!nextTokenAlreadyRead) {
00919 *token = Com_EParse(text, errhead, node->name);
00920 if (!*text)
00921 return qfalse;
00922 } else {
00923 nextTokenAlreadyRead = qfalse;
00924 }
00925
00926
00927 if ((*token)[0] == '}')
00928 break;
00929
00930
00931 val = UI_GetPropertyFromBehaviour(node->behaviour, *token);
00932 if (!val) {
00933
00934 Com_Printf("UI_ParseNodeProperties: unknown property \"%s\", node ignored (node %s)\n",
00935 *token, UI_GetPath(node));
00936 return qfalse;
00937 }
00938
00939
00940 result = UI_ParseProperty(node, val, node->name, text, token);
00941 if (!result) {
00942 Com_Printf("UI_ParseNodeProperties: Problem with parsing of node property '%s@%s'. See upper\n",
00943 UI_GetPath(node), val->string);
00944 return qfalse;
00945 }
00946
00947 } while (*text);
00948
00949 return qtrue;
00950 }
00951
00964 static qboolean UI_ParseNodeBody (uiNode_t * node, const char **text, const char **token, const char *errhead)
00965 {
00966 qboolean result = qtrue;
00967
00968 if ((*token)[0] != '{') {
00969
00970 *token = Com_EParse(text, errhead, node->name);
00971 if (!*text)
00972 return qfalse;
00973 if ((*token)[0] != '{') {
00974 Com_Printf("UI_ParseNodeBody: node doesn't have body, token '%s' read (node \"%s\")\n", *token, UI_GetPath(node));
00975 ui_global.numNodes--;
00976 return qfalse;
00977 }
00978 }
00979
00980
00981 if (node->behaviour->isFunction) {
00982 result = UI_ParseFunction(node, text, token);
00983 } else {
00984
00985
00986 *token = Com_EParse(text, errhead, node->name);
00987 if (!*text)
00988 return qfalse;
00989
00990 if ((*token)[0] == '{') {
00991
00992 result = UI_ParseNodeProperties(node, text, token);
00993 if (!result)
00994 return qfalse;
00995
00996
00997 *token = Com_EParse(text, errhead, node->name);
00998 if (!*text)
00999 return qfalse;
01000
01001
01002 while ((*token)[0] != '}') {
01003 uiNode_t *new = UI_ParseNode(node, text, token, errhead);
01004 if (!new)
01005 return qfalse;
01006
01007 *token = Com_EParse(text, errhead, node->name);
01008 if (*text == NULL)
01009 return qfalse;
01010 }
01011 } else if (UI_GetPropertyFromBehaviour(node->behaviour, *token)) {
01012
01013 result = UI_ParseNodeProperties(node, text, token);
01014 } else {
01015
01016 while ((*token)[0] != '}') {
01017 uiNode_t *new = UI_ParseNode(node, text, token, errhead);
01018 if (!new)
01019 return qfalse;
01020
01021 *token = Com_EParse(text, errhead, node->name);
01022 if (*text == NULL)
01023 return qfalse;
01024 }
01025 }
01026 }
01027 if (!result) {
01028 Com_Printf("UI_ParseNodeBody: node with bad body ignored (node \"%s\")\n", UI_GetPath(node));
01029 ui_global.numNodes--;
01030 return qfalse;
01031 }
01032
01033
01034 assert((*token)[0] == '}');
01035 return qtrue;
01036 }
01037
01045 static uiNode_t *UI_ParseNode (uiNode_t * parent, const char **text, const char **token, const char *errhead)
01046 {
01047 uiNode_t *node = NULL;
01048 uiBehaviour_t *behaviour;
01049 uiNode_t *component = NULL;
01050 qboolean result;
01051
01052
01053 if (!Q_strcasecmp(*token, "node")) {
01054 *token = Com_EParse(text, errhead, "");
01055 if (!*text)
01056 return NULL;
01057 }
01058
01059
01060 behaviour = UI_GetNodeBehaviour(*token);
01061 if (!behaviour) {
01062 component = UI_GetComponent(*token);
01063 }
01064 if (behaviour == NULL && component == NULL) {
01065 Com_Printf("UI_ParseNode: node behaviour/component '%s' doesn't exists\n", *token);
01066 return NULL;
01067 }
01068
01069
01070 *token = Com_EParse(text, errhead, "");
01071 if (!*text)
01072 return NULL;
01073 if (!UI_TokenIsName(*token, Com_ParsedTokenIsQuoted())) {
01074 Com_Printf("UI_ParseNode: \"%s\" is not a well formed node name ([a-zA-Z_][a-zA-Z0-9_]*)\n", *token);
01075 return NULL;
01076 }
01077 if (UI_TokenIsReserved(*token)) {
01078 Com_Printf("UI_ParseNode: \"%s\" is a reserved token, we can't call a node with it\n", *token);
01079 return NULL;
01080 }
01081
01082
01083
01084 if (parent)
01085 node = UI_GetNode(parent, *token);
01086
01087
01088 if (node) {
01089 if (node->behaviour != behaviour) {
01090 Com_Printf("UI_ParseNode: we can't change node type (node \"%s\")\n", UI_GetPath(node));
01091 return NULL;
01092 }
01093 Com_DPrintf(DEBUG_CLIENT, "... over-riding node %s\n", UI_GetPath(node));
01094
01095
01096 } else if (component) {
01097 node = UI_CloneNode(component, NULL, qtrue, *token, qfalse);
01098 if (parent) {
01099 if (parent->root)
01100 UI_UpdateRoot(node, parent->root);
01101 UI_AppendNode(parent, node);
01102 }
01103
01104
01105 } else {
01106 node = UI_AllocNode(*token, behaviour->name, qfalse);
01107 node->parent = parent;
01108 if (parent)
01109 node->root = parent->root;
01111 if (parent)
01112 UI_AppendNode(parent, node);
01113 }
01114
01115
01116 result = UI_ParseNodeBody(node, text, token, errhead);
01117 if (!result)
01118 return NULL;
01119
01120
01121 if (node->behaviour->loaded)
01122 node->behaviour->loaded(node);
01123
01124 return node;
01125 }
01126
01131 void UI_ParseUIModel (const char *name, const char **text)
01132 {
01133 uiModel_t *model;
01134 const char *token;
01135 int i;
01136 const value_t *v = NULL;
01137 const char *errhead = "UI_ParseUIModel: unexpected end of file (names ";
01138
01139
01140 for (i = 0; i < ui_global.numModels; i++)
01141 if (!strcmp(ui_global.models[i].id, name)) {
01142 Com_Printf("UI_ParseUIModel: menu_model \"%s\" with same name found, second ignored\n", name);
01143 return;
01144 }
01145
01146 if (ui_global.numModels >= UI_MAX_MODELS) {
01147 Com_Printf("UI_ParseUIModel: Max UI models reached\n");
01148 return;
01149 }
01150
01151
01152 model = &ui_global.models[ui_global.numModels];
01153 memset(model, 0, sizeof(*model));
01154
01155 Vector4Set(model->color, 1, 1, 1, 1);
01156
01157 model->id = Mem_PoolStrDup(name, ui_sysPool, 0);
01158 Com_DPrintf(DEBUG_CLIENT, "Found UI model %s (%i)\n", model->id, ui_global.numModels);
01159
01160
01161 token = Com_Parse(text);
01162
01163 if (!*text || token[0] != '{') {
01164 Com_Printf("UI_ParseUIModel: Model \"%s\" without body ignored\n", model->id);
01165 return;
01166 }
01167
01168 ui_global.numModels++;
01169
01170 do {
01171
01172 token = Com_EParse(text, errhead, name);
01173 if (!*text)
01174 break;
01175 if (token[0] == '}')
01176 break;
01177
01178 v = UI_FindPropertyByName(uiModelProperties, token);
01179 if (!v)
01180 Com_Printf("UI_ParseUIModel: unknown token \"%s\" ignored (UI model %s)\n", token, name);
01181
01182 if (v->type == V_NULL) {
01183 if (!strcmp(v->string, "need")) {
01184 token = Com_EParse(text, errhead, name);
01185 if (!*text)
01186 return;
01187 model->next = UI_GetUIModel(token);
01188 if (!model->next)
01189 Com_Printf("Could not find UI model %s", token);
01190 model->need = Mem_PoolStrDup(token, ui_sysPool, 0);
01191 }
01192 } else {
01193 token = Com_EParse(text, errhead, name);
01194 if (!*text)
01195 return;
01196 switch (v->type) {
01197 case V_CLIENT_HUNK_STRING:
01198 Mem_PoolStrDupTo(token, (char**) ((char*)model + (int)v->ofs), ui_sysPool, 0);
01199 break;
01200 default:
01201 Com_EParseValue(model, token, v->type, v->ofs, v->size);
01202 }
01203 }
01204 } while (*text);
01205 }
01206
01207 void UI_ParseIcon (const char *name, const char **text)
01208 {
01209 uiIcon_t *icon;
01210 const char *token;
01211
01212
01213 icon = UI_AllocStaticIcon(name);
01214
01215
01216 token = Com_Parse(text);
01217 assert(token[0] == '{');
01218
01219
01220 while (qtrue) {
01221 const value_t *property;
01222 qboolean result;
01223
01224 token = Com_Parse(text);
01225 if (*text == NULL)
01226 return;
01227
01228 if (token[0] == '}')
01229 break;
01230
01231 property = UI_FindPropertyByName(mn_iconProperties, token);
01232 if (!property) {
01233 Com_Printf("UI_ParseIcon: unknown options property: '%s' - ignore it\n", token);
01234 return;
01235 }
01236
01237
01238 result = UI_ParseProperty(icon, property, icon->name, text, &token);
01239 if (!result) {
01240 Com_Printf("UI_ParseIcon: Parsing for icon '%s'. See upper\n", icon->name);
01241 return;
01242 }
01243 }
01244
01245 return;
01246 }
01247
01256 void UI_ParseComponent (const char *type, const char **text)
01257 {
01258 const char *errhead = "UI_ParseComponent: unexpected end of file (component";
01259 uiNode_t *component;
01260 const char *token;
01261
01262 if (strcmp(type, "component") != 0) {
01263 Com_Error(ERR_FATAL, "UI_ParseComponent: \"component\" expected but \"%s\" found.\n", type);
01264 return;
01265 }
01266
01267
01268 Com_UnParseLastToken();
01269 token = Com_Parse(text);
01270
01271 component = UI_ParseNode(NULL, text, &token, errhead);
01272 if (component)
01273 UI_InsertComponent(component);
01274 }
01275
01276
01285 void UI_ParseWindow (const char *type, const char *name, const char **text)
01286 {
01287 const char *errhead = "UI_ParseWindow: unexpected end of file (window";
01288 uiNode_t *window;
01289 const char *token;
01290 qboolean result;
01291 int i;
01292
01293 if (strcmp(type, "window") != 0) {
01294 Com_Error(ERR_FATAL, "UI_ParseWindow: '%s %s' is not a window node\n", type, name);
01295 return;
01296 }
01297
01298 if (!UI_TokenIsName(name, Com_ParsedTokenIsQuoted())) {
01299 Com_Printf("UI_ParseWindow: \"%s\" is not a well formed node name ([a-zA-Z_][a-zA-Z0-9_]*)\n", name);
01300 return;
01301 }
01302 if (UI_TokenIsReserved(name)) {
01303 Com_Printf("UI_ParseWindow: \"%s\" is a reserved token, we can't call a node with it (node \"%s\")\n", name, name);
01304 return;
01305 }
01306
01307
01308 for (i = 0; i < ui_global.numWindows; i++)
01309 if (!strncmp(name, ui_global.windows[i]->name, sizeof(ui_global.windows[i]->name)))
01310 break;
01311
01312 if (i < ui_global.numWindows) {
01313 Com_Printf("UI_ParseWindow: %s \"%s\" with same name found, second ignored\n", type, name);
01314 }
01315
01316 if (ui_global.numWindows >= UI_MAX_WINDOWS) {
01317 Com_Error(ERR_FATAL, "UI_ParseWindow: max windows exceeded (%i) - ignore '%s'\n", UI_MAX_WINDOWS, name);
01318 return;
01319 }
01320
01321
01322 token = Com_Parse(text);
01323
01324
01325 if (!strcmp(token, "extends")) {
01326 uiNode_t *superWindow;
01327 token = Com_Parse(text);
01328 superWindow = UI_GetWindow(token);
01329 if (superWindow == NULL)
01330 Sys_Error("Could not get the super window \"%s\"", token);
01331 window = UI_CloneNode(superWindow, NULL, qtrue, name, qfalse);
01332 token = Com_Parse(text);
01333 } else {
01334 window = UI_AllocNode(name, type, qfalse);
01335 window->root = window;
01336 }
01337
01338 UI_InsertWindow(window);
01339
01340
01341 result = UI_ParseNodeBody(window, text, &token, errhead);
01342 if (!result) {
01343 Com_Error(ERR_FATAL, "UI_ParseWindow: window \"%s\" has a bad body\n", window->name);
01344 return;
01345 }
01346
01347 window->behaviour->loaded(window);
01348 }
01349
01354 const char *UI_GetReferenceString (const uiNode_t* const node, const char *ref)
01355 {
01356 if (!ref)
01357 return NULL;
01358
01359
01360 if (ref[0] == '*') {
01361 const char *token;
01362
01363
01364 token = Com_MacroExpandString(ref);
01365 if (token)
01366 return token;
01367
01368
01369 token = ref + 1;
01370 if (token[0] == '\0')
01371 return NULL;
01372
01373 if (!strncmp(token, "binding:", 8)) {
01374
01375 token = token + 8;
01376 return Key_GetBinding(token, (cls.state != ca_active ? KEYSPACE_UI : KEYSPACE_GAME));
01377 } else {
01378 Sys_Error("UI_GetReferenceString: unknown reference");
01379 #if 0
01380 uiNode_t *refNode;
01381 const value_t *val;
01382
01383 token = Com_Parse(&text);
01384 if (!text)
01385 return NULL;
01386
01387
01388 refNode = UI_GetNode(node->root, ident);
01389 if (!refNode)
01390 return NULL;
01391
01392
01393 val = UI_GetPropertyFromBehaviour(refNode->behaviour, token);
01394 if (!val)
01395 return NULL;
01396
01397
01398 return Com_ValueToStr(refNode, val->type & V_BASETYPEMASK, val->ofs);
01399 #endif
01400 }
01401
01402
01403 } else if (ref[0] == '_') {
01404 ref++;
01405 return _(ref);
01406
01407
01408 } else {
01409 return ref;
01410 }
01411 }
01412
01413 float UI_GetReferenceFloat (const uiNode_t* const node, const void *ref)
01414 {
01415 if (!ref)
01416 return 0.0;
01417 if (((const char *) ref)[0] == '*') {
01418 const char *token;
01419 token = (const char *) ref + 1;
01420
01421 if (token[0] == '\0')
01422 return 0.0;
01423
01424 if (!strncmp(token, "cvar:", 5)) {
01425
01426 return Cvar_GetValue(token + 5);
01427 } else {
01429 Sys_Error("UI_GetReferenceFloat: unknown reference '%s' from node '%s'",
01430 token, node->name);
01431 #if 0
01432 uiNode_t *refNode;
01433 const value_t *val;
01434
01435
01436 refNode = UI_GetNode(node->root, ident);
01437 if (!refNode)
01438 return 0.0;
01439
01440
01441 val = UI_GetPropertyFromBehaviour(refNode->behaviour, token);
01442 if (!val || val->type != V_FLOAT)
01443 return 0.0;
01444
01445
01446 return *(float *) ((byte *) refNode + val->ofs);
01447 #endif
01448 }
01449 } else {
01450
01451 return *(const float *) ref;
01452 }
01453 }