1 /*
2 	bi_plist.c
3 
4 	QuakeC plist api
5 
6 	Copyright (C) 2003 Bill Currie
7 
8 	Author: Bill Currie <bill@taniwha.org>
9 	Date: 2003/4/7
10 
11 	This program is free software; you can redistribute it and/or
12 	modify it under the terms of the GNU General Public License
13 	as published by the Free Software Foundation; either version 2
14 	of the License, or (at your option) any later version.
15 
16 	This program is distributed in the hope that it will be useful,
17 	but WITHOUT ANY WARRANTY; without even the implied warranty of
18 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 
20 	See the GNU General Public License for more details.
21 
22 	You should have received a copy of the GNU General Public License
23 	along with this program; if not, write to:
24 
25 		Free Software Foundation, Inc.
26 		59 Temple Place - Suite 330
27 		Boston, MA  02111-1307, USA
28 
29 */
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33 
34 #ifdef HAVE_STRING_H
35 # include <string.h>
36 #endif
37 #ifdef HAVE_STRINGS_H
38 # include <strings.h>
39 #endif
40 #include <stdlib.h>
41 
42 #include "QF/hash.h"
43 #include "QF/progs.h"
44 #include "QF/qfplist.h"
45 
46 #include "rua_internal.h"
47 
48 typedef struct bi_plist_s {
49 	struct bi_plist_s *next;
50 	struct bi_plist_s **prev;
51 	plitem_t   *plitem;
52 	int         own;
53 } bi_plist_t;
54 
55 typedef struct {
56 	PR_RESMAP (bi_plist_t) plist_map;
57 	bi_plist_t *plists;
58 	hashtab_t  *plist_tab;
59 } plist_resources_t;
60 
61 static bi_plist_t *
plist_new(plist_resources_t * res)62 plist_new (plist_resources_t *res)
63 {
64 	PR_RESNEW (bi_plist_t, res->plist_map);
65 }
66 
67 static void
plist_free(plist_resources_t * res,bi_plist_t * plist)68 plist_free (plist_resources_t *res, bi_plist_t *plist)
69 {
70 	PR_RESFREE (bi_plist_t, res->plist_map, plist);
71 }
72 
73 static void
plist_reset(plist_resources_t * res)74 plist_reset (plist_resources_t *res)
75 {
76 	PR_RESRESET (bi_plist_t, res->plist_map);
77 }
78 
79 static inline bi_plist_t *
plist_get(plist_resources_t * res,unsigned index)80 plist_get (plist_resources_t *res, unsigned index)
81 {
82 	PR_RESGET(res->plist_map, index);
83 }
84 
85 static inline int
plist_index(plist_resources_t * res,bi_plist_t * plist)86 plist_index (plist_resources_t *res, bi_plist_t *plist)
87 {
88 	PR_RESINDEX(res->plist_map, plist);
89 }
90 
91 static void
bi_plist_clear(progs_t * pr,void * data)92 bi_plist_clear (progs_t *pr, void *data)
93 {
94 	plist_resources_t *res = (plist_resources_t *) data;
95 	bi_plist_t *plist;
96 
97 	for (plist = res->plists; plist; plist = plist->next) {
98 		if (plist->own)
99 			PL_Free (plist->plitem);
100 	}
101 	res->plists = 0;
102 
103 	Hash_FlushTable (res->plist_tab);
104 	plist_reset (res);
105 }
106 
107 static inline int
plist_handle(plist_resources_t * res,plitem_t * plitem)108 plist_handle (plist_resources_t *res, plitem_t *plitem)
109 {
110 	bi_plist_t  dummy = {0, 0, plitem, 0};
111 	bi_plist_t *plist = Hash_FindElement (res->plist_tab, &dummy);
112 
113 	if (plist)
114 		return plist_index (res, plist);
115 
116 	plist = plist_new (res);
117 
118 	if (!plist)
119 		return 0;
120 
121 	plist->next = res->plists;
122 	plist->prev = &res->plists;
123 	if (res->plists)
124 		res->plists->prev = &plist->next;
125 	res->plists = plist;
126 
127 	plist->plitem = plitem;
128 
129 	Hash_AddElement (res->plist_tab, plist);
130 	return plist_index (res, plist);
131 }
132 
133 static inline void
plist_free_handle(plist_resources_t * res,bi_plist_t * plist)134 plist_free_handle (plist_resources_t *res, bi_plist_t *plist)
135 {
136 	Hash_DelElement (res->plist_tab, plist);
137 	*plist->prev = plist->next;
138 	if (plist->next)
139 		plist->next->prev = plist->prev;
140 	plist_free (res, plist);
141 }
142 
143 static inline bi_plist_t *
get_plist(progs_t * pr,const char * name,int handle)144 get_plist (progs_t *pr, const char *name, int handle)
145 {
146 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
147 	bi_plist_t *plist = plist_get (res, handle);
148 
149 	// plist->prev will be null if the handle is unallocated
150 	if (!plist || !plist->prev)
151 		PR_RunError (pr, "invalid plist passed to %s", name + 3);
152 	return plist;
153 }
154 
155 static inline int
plist_retain(plist_resources_t * res,plitem_t * plitem)156 plist_retain (plist_resources_t *res, plitem_t *plitem)
157 {
158 	int         handle;
159 	bi_plist_t *plist;
160 
161 	if (!plitem)
162 		return 0;
163 
164 	handle = plist_handle (res, plitem);
165 	if (!handle) {
166 		// we're taking ownership of the plitem, but we have nowhere to store
167 		// it, so we have to lose it. However, in this situation, we have worse
168 		// things to worry about.
169 		PL_Free (plitem);
170 		return 0;
171 	}
172 
173 	plist = plist_get (res, handle);
174 	plist->own = 1;
175 	return handle;
176 }
177 
178 static void
bi_PL_GetFromFile(progs_t * pr)179 bi_PL_GetFromFile (progs_t *pr)
180 {
181 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
182 	QFile      *file = QFile_GetFile (pr, P_INT (pr, 0));
183 	plitem_t   *plitem;
184 	long        offset;
185 	long        size;
186 	long		len;
187 	char       *buf;
188 
189 	offset = Qtell (file);
190 	size = Qfilesize (file);
191 	len = size - offset;
192 	buf = malloc (len + 1);
193 	Qread (file, buf, len);
194 	buf[len] = 0;
195 
196 	plitem = PL_GetPropertyList (buf);
197 
198 	R_INT (pr) = plist_retain (res, plitem);
199 }
200 
201 static void
bi_PL_GetPropertyList(progs_t * pr)202 bi_PL_GetPropertyList (progs_t *pr)
203 {
204 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
205 	plitem_t   *plitem = PL_GetPropertyList (P_GSTRING (pr, 0));
206 
207 	R_INT (pr) = plist_retain (res, plitem);
208 }
209 
210 static void
bi_PL_WritePropertyList(progs_t * pr)211 bi_PL_WritePropertyList (progs_t *pr)
212 {
213 	int         handle = P_INT (pr, 0);
214 	bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle);
215 	char       *pl = PL_WritePropertyList (plist->plitem);
216 
217 	R_STRING (pr) = PR_SetDynamicString (pr, pl);
218 	free (pl);
219 }
220 
221 static void
bi_PL_Type(progs_t * pr)222 bi_PL_Type (progs_t *pr)
223 {
224 	int         handle = P_INT (pr, 0);
225 	bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle);
226 
227 	R_INT (pr) = PL_Type (plist->plitem);
228 }
229 
230 static void
bi_PL_String(progs_t * pr)231 bi_PL_String (progs_t *pr)
232 {
233 	int         handle = P_INT (pr, 0);
234 	bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle);
235 	const char *str = PL_String (plist->plitem);
236 
237 	RETURN_STRING (pr, str);
238 }
239 
240 static void
bi_PL_ObjectForKey(progs_t * pr)241 bi_PL_ObjectForKey (progs_t *pr)
242 {
243 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
244 	int         handle = P_INT (pr, 0);
245 	bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle);
246 	const char *key = P_GSTRING (pr, 1);
247 	plitem_t   *plitem = PL_ObjectForKey (plist->plitem, key);
248 
249 	R_INT (pr) = 0;
250 	if (!plitem)
251 		return;
252 	R_INT (pr) = plist_handle (res, plitem);
253 }
254 
255 static void
bi_PL_RemoveObjectForKey(progs_t * pr)256 bi_PL_RemoveObjectForKey (progs_t *pr)
257 {
258 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
259 	int         handle = P_INT (pr, 0);
260 	bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle);
261 	const char *key = P_GSTRING (pr, 1);
262 	plitem_t   *plitem = PL_RemoveObjectForKey (plist->plitem, key);
263 
264 	R_INT (pr) = plist_retain (res, plitem);
265 }
266 
267 static void
bi_PL_ObjectAtIndex(progs_t * pr)268 bi_PL_ObjectAtIndex (progs_t *pr)
269 {
270 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
271 	int         handle = P_INT (pr, 0);
272 	bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle);
273 	int         ind = P_INT (pr, 1);
274 	plitem_t   *plitem = PL_ObjectAtIndex (plist->plitem, ind);
275 
276 	R_INT (pr) = 0;
277 	if (!plitem)
278 		return;
279 	R_INT (pr) = plist_handle (res, plitem);
280 }
281 
282 static void
bi_PL_D_AllKeys(progs_t * pr)283 bi_PL_D_AllKeys (progs_t *pr)
284 {
285 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
286 	int         handle = P_INT (pr, 0);
287 	bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle);
288 	plitem_t   *plitem = PL_D_AllKeys (plist->plitem);
289 
290 	R_INT (pr) = plist_retain (res, plitem);
291 }
292 
293 static void
bi_PL_D_NumKeys(progs_t * pr)294 bi_PL_D_NumKeys (progs_t *pr)
295 {
296 	int         handle = P_INT (pr, 0);
297 	bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle);
298 
299 	R_INT (pr) = PL_D_NumKeys (plist->plitem);
300 }
301 
302 static void
bi_PL_D_AddObject(progs_t * pr)303 bi_PL_D_AddObject (progs_t *pr)
304 {
305 	int         dict_handle = P_INT (pr, 0);
306 	int         obj_handle = P_INT (pr, 2);
307 	bi_plist_t *dict = get_plist (pr, __FUNCTION__, dict_handle);
308 	const char *key = P_GSTRING (pr, 1);
309 	bi_plist_t *obj = get_plist (pr, __FUNCTION__, obj_handle);
310 
311 	obj->own = 0;
312 	R_INT (pr) = PL_D_AddObject (dict->plitem, key, obj->plitem);
313 }
314 
315 static void
bi_PL_A_AddObject(progs_t * pr)316 bi_PL_A_AddObject (progs_t *pr)
317 {
318 	int         arr_handle = P_INT (pr, 0);
319 	int         obj_handle = P_INT (pr, 1);
320 	bi_plist_t *arr = get_plist (pr, __FUNCTION__, arr_handle);
321 	bi_plist_t *obj = get_plist (pr, __FUNCTION__, obj_handle);
322 
323 	obj->own = 0;
324 	R_INT (pr) = PL_A_AddObject (arr->plitem, obj->plitem);
325 }
326 
327 static void
bi_PL_A_NumObjects(progs_t * pr)328 bi_PL_A_NumObjects (progs_t *pr)
329 {
330 	int         handle = P_INT (pr, 0);
331 	bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle);
332 
333 	R_INT (pr) = PL_A_NumObjects (plist->plitem);
334 }
335 
336 static void
bi_PL_A_InsertObjectAtIndex(progs_t * pr)337 bi_PL_A_InsertObjectAtIndex (progs_t *pr)
338 {
339 	int         dict_handle = P_INT (pr, 0);
340 	int         obj_handle = P_INT (pr, 1);
341 	bi_plist_t *arr = get_plist (pr, __FUNCTION__, dict_handle);
342 	bi_plist_t *obj = get_plist (pr, __FUNCTION__, obj_handle);
343 	int         ind = P_INT (pr, 2);
344 
345 	obj->own = 0;
346 	R_INT (pr) = PL_A_InsertObjectAtIndex (arr->plitem, obj->plitem, ind);
347 }
348 
349 static void
bi_PL_RemoveObjectAtIndex(progs_t * pr)350 bi_PL_RemoveObjectAtIndex (progs_t *pr)
351 {
352 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
353 	int         handle = P_INT (pr, 0);
354 	bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle);
355 	int         ind = P_INT (pr, 1);
356 	plitem_t   *plitem = PL_RemoveObjectAtIndex (plist->plitem, ind);
357 
358 	R_INT (pr) = plist_retain (res, plitem);
359 }
360 
361 static void
bi_PL_NewDictionary(progs_t * pr)362 bi_PL_NewDictionary (progs_t *pr)
363 {
364 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
365 	plitem_t   *plitem = PL_NewDictionary ();
366 
367 	R_INT (pr) = plist_retain (res, plitem);
368 }
369 
370 static void
bi_PL_NewArray(progs_t * pr)371 bi_PL_NewArray (progs_t *pr)
372 {
373 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
374 	plitem_t   *plitem = PL_NewArray ();
375 
376 	R_INT (pr) = plist_retain (res, plitem);
377 }
378 
379 static void
bi_PL_NewData(progs_t * pr)380 bi_PL_NewData (progs_t *pr)
381 {
382 	//FIXME not safe
383 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
384 	plitem_t   *plitem = PL_NewData (P_GPOINTER (pr, 0), P_INT (pr, 1));
385 
386 	R_INT (pr) = plist_retain (res, plitem);
387 }
388 
389 static void
bi_PL_NewString(progs_t * pr)390 bi_PL_NewString (progs_t *pr)
391 {
392 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
393 	plitem_t   *plitem = PL_NewString (P_GSTRING (pr, 0));
394 
395 	R_INT (pr) = plist_retain (res, plitem);
396 }
397 
398 static void
bi_PL_Free(progs_t * pr)399 bi_PL_Free (progs_t *pr)
400 {
401 	plist_resources_t *res = PR_Resources_Find (pr, "plist");
402 	int         handle = P_INT (pr, 0);
403 	bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle);
404 
405 	if (!plist->own)
406 		PR_RunError (pr, "attempt to free unowned plist");
407 
408 	PL_Free (plist->plitem);
409 
410 	plist_free_handle (res, plist);
411 }
412 
413 static uintptr_t
plist_get_hash(const void * key,void * unused)414 plist_get_hash (const void *key, void *unused)
415 {
416 	bi_plist_t *plist = (bi_plist_t *) key;
417 	return (uintptr_t) plist->plitem;
418 }
419 
420 static int
plist_compare(const void * k1,const void * k2,void * unused)421 plist_compare (const void *k1, const void *k2, void *unused)
422 {
423 	bi_plist_t *pl1 = (bi_plist_t *) k1;
424 	bi_plist_t *pl2 = (bi_plist_t *) k2;
425 	return pl1->plitem == pl2->plitem;
426 }
427 
428 static builtin_t builtins[] = {
429 	{"PL_GetFromFile",				bi_PL_GetFromFile,				-1},
430 	{"PL_GetPropertyList",			bi_PL_GetPropertyList,			-1},
431 	{"PL_WritePropertyList",		bi_PL_WritePropertyList,		-1},
432 	{"PL_Type",						bi_PL_Type,						-1},
433 	{"PL_String",					bi_PL_String,					-1},
434 	{"PL_ObjectForKey",				bi_PL_ObjectForKey,				-1},
435 	{"PL_RemoveObjectForKey",		bi_PL_RemoveObjectForKey,		-1},
436 	{"PL_ObjectAtIndex",			bi_PL_ObjectAtIndex,			-1},
437 	{"PL_D_AllKeys",				bi_PL_D_AllKeys,				-1},
438 	{"PL_D_NumKeys",				bi_PL_D_NumKeys,				-1},
439 	{"PL_D_AddObject",				bi_PL_D_AddObject,				-1},
440 	{"PL_A_AddObject",				bi_PL_A_AddObject,				-1},
441 	{"PL_A_NumObjects",				bi_PL_A_NumObjects,				-1},
442 	{"PL_A_InsertObjectAtIndex",	bi_PL_A_InsertObjectAtIndex,	-1},
443 	{"PL_RemoveObjectAtIndex",		bi_PL_RemoveObjectAtIndex,		-1},
444 	{"PL_NewDictionary",			bi_PL_NewDictionary,			-1},
445 	{"PL_NewArray",					bi_PL_NewArray,					-1},
446 	{"PL_NewData",					bi_PL_NewData,					-1},
447 	{"PL_NewString",				bi_PL_NewString,				-1},
448 	{"PL_Free",						bi_PL_Free,						-1},
449 	{0}
450 };
451 
452 void
RUA_Plist_Init(progs_t * pr,int secure)453 RUA_Plist_Init (progs_t *pr, int secure)
454 {
455 	plist_resources_t *res = calloc (1, sizeof (plist_resources_t));
456 	res->plist_tab = Hash_NewTable (1021, 0, 0, 0);
457 	Hash_SetHashCompare (res->plist_tab, plist_get_hash, plist_compare);
458 
459 	PR_Resources_Register (pr, "plist", res, bi_plist_clear);
460 	PR_RegisterBuiltins (pr, builtins);
461 }
462