1 /**
2 * @file
3 */
4
5 /*
6 Copyright (C) 2002-2013 UFO: Alien Invasion.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 */
24
25 #include "ui_main.h"
26 #include "ui_internal.h"
27 #include "ui_behaviour.h"
28 #include "ui_parse.h"
29
30 /**
31 * Size of the temporary property-list allocation (per behaviour)
32 */
33 #define LOCAL_PROPERTY_SIZE 128
34
35 /**
36 * @brief Register a property to a behaviour.
37 * It should not be used in the code
38 * @param behaviour Target behaviour
39 * @param name Name of the property
40 * @param type Type of the property
41 * @param pos position of the attribute (which store property memory) into the node structure
42 * @param size size of the attribute (which store property memory) into the node structure
43 * @see UI_RegisterNodeProperty
44 * @see UI_RegisterExtradataNodeProperty
45 * @return A link to the node property
46 */
UI_RegisterNodePropertyPosSize_(uiBehaviour_t * behaviour,const char * name,int type,size_t pos,size_t size)47 const struct value_s* UI_RegisterNodePropertyPosSize_ (uiBehaviour_t* behaviour, const char* name, int type, size_t pos, size_t size)
48 {
49 value_t* property = (value_t*) UI_AllocHunkMemory(sizeof(value_t), STRUCT_MEMORY_ALIGN, false);
50 if (property == nullptr)
51 Com_Error(ERR_FATAL, "UI_RegisterNodePropertyPosSize_: UI memory hunk exceeded - increase the size");
52
53 if (type == V_STRING || type == V_LONGSTRING || type == V_CVAR_OR_LONGSTRING || V_CVAR_OR_STRING) {
54 size = 0;
55 }
56
57 property->string = name;
58 property->type = (valueTypes_t) type;
59 property->ofs = pos;
60 property->size = size;
61
62 if (behaviour->localProperties == nullptr) {
63 /* temporary memory allocation */
64 behaviour->localProperties = Mem_PoolAllocTypeN(value_t const*, LOCAL_PROPERTY_SIZE, ui_sysPool);
65 }
66 if (behaviour->propertyCount >= LOCAL_PROPERTY_SIZE - 1) {
67 Com_Error(ERR_FATAL, "UI_RegisterNodePropertyPosSize_: Property memory of behaviour %s is full.", behaviour->name);
68 }
69 behaviour->localProperties[behaviour->propertyCount++] = property;
70 behaviour->localProperties[behaviour->propertyCount] = nullptr;
71
72 return property;
73 }
74
75 /**
76 * @brief Register a node method to a behaviour.
77 * @param behaviour Target behaviour
78 * @param name Name of the property
79 * @param function function to execute the node method
80 * @return A link to the node property
81 */
UI_RegisterNodeMethod(uiBehaviour_t * behaviour,const char * name,uiNodeMethod_t function)82 const struct value_s* UI_RegisterNodeMethod (uiBehaviour_t* behaviour, const char* name, uiNodeMethod_t function)
83 {
84 return UI_RegisterNodePropertyPosSize_(behaviour, name, V_UI_NODEMETHOD, (size_t)function, 0);
85 }
86
87 /**
88 * @brief Get a property from a behaviour or his inheritance
89 * It use a dichotomic search.
90 * @param[in] behaviour Context behaviour
91 * @param[in] name Property name we search
92 * @return A value_t with the requested name, else nullptr
93 */
UI_GetPropertyFromBehaviour(const uiBehaviour_t * behaviour,const char * name)94 const value_t* UI_GetPropertyFromBehaviour (const uiBehaviour_t* behaviour, const char* name)
95 {
96 for (; behaviour; behaviour = behaviour->super) {
97 unsigned char min = 0;
98 unsigned char max = behaviour->propertyCount;
99
100 while (min != max) {
101 const int mid = (min + max) >> 1;
102 const int diff = Q_strcasecmp(behaviour->localProperties[mid]->string, name);
103 assert(mid < max);
104 assert(mid >= min);
105
106 if (diff == 0)
107 return behaviour->localProperties[mid];
108
109 if (diff > 0)
110 max = mid;
111 else
112 min = mid + 1;
113 }
114 }
115 return nullptr;
116 }
117
118 /**
119 * @brief Initialize a node behaviour memory, after registration, and before unsing it.
120 * @param behaviour Behaviour to initialize
121 */
UI_InitializeNodeBehaviour(uiBehaviour_t * behaviour)122 void UI_InitializeNodeBehaviour (uiBehaviour_t* behaviour)
123 {
124 if (behaviour->isInitialized)
125 return;
126
127 /* everything inherits 'abstractnode' */
128 if (behaviour->extends == nullptr && !Q_streq(behaviour->name, "abstractnode")) {
129 behaviour->extends = "abstractnode";
130 }
131
132 if (!behaviour->manager && Q_strvalid(behaviour->name)) {
133 Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: Behaviour '%s' expect a manager class", behaviour->name);
134 }
135
136 if (behaviour->extends) {
137 /** @todo Find a way to remove that, if possible */
138 behaviour->super = UI_GetNodeBehaviour(behaviour->extends);
139 UI_InitializeNodeBehaviour(behaviour->super);
140
141 /* cache super function if we don't overwrite it */
142 if (behaviour->extraDataSize == 0)
143 behaviour->extraDataSize = behaviour->super->extraDataSize;
144 }
145
146 /* sort properties by alphabet */
147 if (behaviour->localProperties) {
148 const value_t** oldmemory = behaviour->localProperties;
149 behaviour->localProperties = (const value_t**) UI_AllocHunkMemory(sizeof(value_t*) * (behaviour->propertyCount+1), STRUCT_MEMORY_ALIGN, false);
150 if (behaviour->localProperties == nullptr) {
151 Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: UI memory hunk exceeded - increase the size");
152 }
153
154 const value_t* previous = nullptr;
155 for (int i = 0; i < behaviour->propertyCount; i++) {
156 const value_t* better = nullptr;
157 /* search the next element after previous */
158 for (const value_t** current = oldmemory; *current != nullptr; current++) {
159 if (previous != nullptr && Q_strcasecmp(previous->string, (*current)->string) >= 0) {
160 continue;
161 }
162 if (better == nullptr || Q_strcasecmp(better->string, (*current)->string) >= 0) {
163 better = *current;
164 }
165 }
166 previous = better;
167 behaviour->localProperties[i] = better;
168 }
169 behaviour->localProperties[behaviour->propertyCount] = nullptr;
170 Mem_Free(oldmemory);
171 }
172
173 /* property must not overwrite another property */
174 if (behaviour->super && behaviour->localProperties) {
175 const value_t** property = behaviour->localProperties;
176 while (*property) {
177 const value_t* p = UI_GetPropertyFromBehaviour(behaviour->super, (*property)->string);
178 #if 0 /**< @todo not possible at the moment, not sure its the right way */
179 const uiBehaviour_t* b = UI_GetNodeBehaviour(current->string);
180 #endif
181 if (p != nullptr)
182 Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' overwrite another property", (*property)->string, behaviour->name);
183 #if 0 /**< @todo not possible at the moment, not sure its the right way */
184 if (b != nullptr)
185 Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' use the name of an existing node behaviour", (*property)->string, behaviour->name);
186 #endif
187 property++;
188 }
189 }
190
191 /* Sanity: A property must not be outside the node memory */
192 if (behaviour->localProperties) {
193 const int size = sizeof(uiNode_t) + behaviour->extraDataSize;
194 const value_t** property = behaviour->localProperties;
195 while (*property) {
196 if ((*property)->type != V_UI_NODEMETHOD && (*property)->ofs + (*property)->size > size)
197 Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' is outside the node memory. The C code need a fix.", (*property)->string, behaviour->name);
198 property++;
199 }
200 }
201
202 behaviour->isInitialized = true;
203 }
204