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