]> git.draconx.ca Git - upkg.git/blob - src/uobject.c
Fix another mistake in uobject's property parsing.
[upkg.git] / src / uobject.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <glib-object.h>
6
7 #include "uobject.h"
8 #include "upkg.h"
9 #include "pack.h"
10 #include "avl.h"
11
12 #define U_OBJECT_GET_PRIV(o) \
13         G_TYPE_INSTANCE_GET_PRIVATE(o, U_OBJECT_TYPE, struct uobject_priv)
14
15 enum {
16         PROPERTY_BYTE = 1,
17         PROPERTY_INTEGER,
18         PROPERTY_BOOLEAN,
19         PROPERTY_FLOAT,
20         PROPERTY_OBJECT,
21         PROPERTY_NAME,
22         PROPERTY_STRING,
23         PROPERTY_CLASS,
24         PROPERTY_ARRAY,
25         PROPERTY_STRUCT,
26         PROPERTY_VECTOR,
27         PROPERTY_ROTATOR,
28         PROPERTY_STR,
29         PROPERTY_MAP,
30         PROPERTY_FIXEDARRAY,
31 };
32
33 struct uobject_property {
34         const char *name;
35         GValue val;
36 };
37
38 struct uobject_priv {
39         struct upkg_file *f;
40         size_t base, len;
41
42         struct avl_table *properties;
43
44         unsigned char buf[2048];
45         unsigned long nbuf;
46 };
47
48 /* AVL tree functions. */
49 static int propcmp(const void *_a, const void *_b, void *_data)
50 {
51         const struct uobject_property *a = _a, *b = _b;
52         return strcmp(a->name, b->name);
53 }
54
55 static void propfree(void *item, void *data)
56 {
57         free(item);
58 }
59
60 static unsigned long
61 get_real_size(unsigned long *real, unsigned size, unsigned char *buf, size_t n)
62 {
63         assert(size < 8);
64
65         *real = 0;
66         switch (size) {
67         case 0: *real =  1; return 0;
68         case 1: *real =  2; return 0;
69         case 2: *real =  4; return 0;
70         case 3: *real = 12; return 0;
71         case 4: *real = 16; return 0;
72         case 5:
73                 if (n < 1) return 0;
74                 *real = *buf;
75                 return 1;
76         case 6:
77                 if (n < 2) return 0;
78                 *real = unpack_16_le(buf);
79                 return 2;
80         case 7:
81                 if (n < 4) return 0;
82                 *real = unpack_32_le(buf);
83                 return 4;
84         }
85
86         return 0;
87 }
88
89 static unsigned long
90 decode_property(UObject *o, const char *name, struct upkg_file *f, unsigned long len)
91 {
92         struct uobject_priv *priv = U_OBJECT_GET_PRIV(o);
93         unsigned long real_size, rc;
94         int type, size, top;
95         GValue val = {0};
96
97         if (priv->nbuf-len < 1)
98                 return 0;
99         
100         type = (priv->buf[len] >> 0) & 0x0f;
101         size = (priv->buf[len] >> 4) & 0x07;
102         top  = (priv->buf[len] >> 7) & 0x01;
103         len += 1;
104
105         rc = get_real_size(&real_size, size, priv->buf, priv->nbuf-len);
106         if (real_size == 0)
107                 return 0;
108         len += rc;
109
110         switch (type) {
111         case PROPERTY_BYTE:
112                 if (priv->nbuf-len < 1)
113                         return 0;
114                 g_value_init(&val, G_TYPE_UCHAR);
115                 g_value_set_uchar(&val, priv->buf[len]);
116                 u_object_set_property(o, name, &val);
117                 break;
118         case PROPERTY_INTEGER:
119                 if (priv->nbuf-len < 4)
120                         return 0;
121                 g_value_init(&val, G_TYPE_ULONG);
122                 g_value_set_ulong(&val, unpack_32_le(priv->buf+len));
123                 u_object_set_property(o, name, &val);
124                 break;
125         default:
126                 fprintf(stderr, "Unhandled property type %x\n", (unsigned)type);
127         }
128
129         real_size += len;
130         if (real_size + len <= priv->nbuf) {
131                 priv->nbuf -= real_size;
132                 memmove(priv->buf, priv->buf+real_size, priv->nbuf);
133         } else {
134                 long skip = real_size - priv->nbuf;
135                 if (upkg_export_seek(f, skip, SEEK_CUR) != 0)
136                         return 0;
137                 priv->nbuf = 0;
138         }
139
140         return real_size;
141 }
142
143 /* Deserialize properties from an Unreal package. */
144 static int deserialize(UObject *o, struct upkg_file *f)
145 {
146         struct uobject_priv *priv = U_OBJECT_GET_PRIV(o);
147         unsigned long rc, tot_len = 0;
148
149         while (1) {
150                 unsigned long len = 0;
151                 const char *name;
152                 long tmp;
153
154                 /* Read some data into buffer. */
155                 if (!f->eof) {
156                         void  *buf = priv->buf + priv->nbuf;
157                         size_t amt = sizeof priv->buf - priv->nbuf;
158                         rc = upkg_export_read(f, buf, amt);
159                         if (rc == 0)
160                                 return -1;
161                         priv->nbuf += rc;
162                 }
163
164                 /* Get the property name. */
165                 rc = upkg_decode_index(&tmp, priv->buf+len, priv->nbuf-len);
166                 if (rc == 0)
167                         return -1;
168                 len = rc;
169
170                 name = upkg_get_name(f->pkg, tmp);
171                 if (!name) {
172                         return -1;
173                 } else if (strcmp(name, "None") == 0) {
174                         tot_len += len;
175                         break;
176                 }
177
178                 rc = decode_property(U_OBJECT(o), name, f, len);
179                 if (rc == 0)
180                         return -1;
181                 len = rc;
182
183                 tot_len += len;
184         }
185
186         f->base += tot_len;
187         f->len  -= tot_len;
188         upkg_export_seek(f, 0, SEEK_SET);
189
190         return 0;
191 }
192
193 int u_object_deserialize(GObject *obj, struct upkg_file *f)
194 {
195         g_return_val_if_fail(IS_U_OBJECT(obj), -1);
196         U_OBJECT_GET_CLASS(obj)->deserialize(U_OBJECT(obj), f);
197 }
198
199 static void u_object_init(UObject *o)
200 {
201         struct uobject_priv *priv = U_OBJECT_GET_PRIV(o);
202         priv->properties = NULL;
203 }
204
205 void u_object_set_property(UObject *o, const char *name, const GValue *val)
206 {
207         struct uobject_priv *priv = U_OBJECT_GET_PRIV(o);
208         struct uobject_property *prop, search = { .name = name };
209         void **p;
210
211         if (!priv->properties) {
212                 priv->properties = avl_create(propcmp, NULL, NULL);
213                 g_return_if_fail(priv->properties != NULL);
214         }
215
216         prop = avl_find(priv->properties, &search);
217         if (prop) {
218                 g_value_unset(&prop->val);
219                 g_value_init(&prop->val, G_VALUE_TYPE(val));
220                 g_value_copy(val, &prop->val);
221                 return;
222         }
223
224         prop = malloc(sizeof *prop);
225         g_return_if_fail(prop != NULL);
226
227         *prop = (struct uobject_property) { .name = name };
228         g_value_init(&prop->val, G_VALUE_TYPE(val));
229         g_value_copy(val, &prop->val);
230
231         g_return_if_fail(avl_probe(priv->properties, prop) != NULL);
232 }
233
234 const GValue *u_object_get_property(UObject *o, const char *name)
235 {
236         struct uobject_priv *priv = U_OBJECT_GET_PRIV(o);
237         struct uobject_property *prop, search = { .name = name };
238
239         if (!priv->properties)
240                 return NULL;
241
242         prop = avl_find(priv->properties, &search);
243         if (!prop)
244                 return NULL;
245         return &prop->val;
246 }
247
248 static void u_object_finalize(GObject *o)
249 {
250         struct uobject_priv *priv = U_OBJECT_GET_PRIV(o);
251
252         if (priv->properties) {
253                 avl_destroy(priv->properties, propfree);
254                 priv->properties = NULL;
255         }
256 }
257
258 static void u_object_class_init(UObjectClass *class)
259 {
260         GObjectClass *go = G_OBJECT_CLASS(class);
261
262         class->deserialize = deserialize;
263
264         g_type_class_add_private(class, sizeof (struct uobject_priv));
265         go->finalize = u_object_finalize;
266 }
267
268 G_DEFINE_TYPE(UObject, u_object, G_TYPE_OBJECT);