]> git.draconx.ca Git - upkg.git/blob - src/uobject/uobject.c
627abe68e32ac3dc2405a1887fd3ad85178e4d10
[upkg.git] / src / uobject / uobject.c
1 /*
2  *  upkg: tool for manipulating Unreal Tournament packages.
3  *  Copyright © 2009-2011 Nick Bowler
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <math.h>
23 #include <stdarg.h>
24 #include <stdbool.h>
25 #include <inttypes.h>
26 #include <assert.h>
27 #include <glib-object.h>
28
29 #include <uobject/uobject.h>
30 #include <uobject/module.h>
31 #include <uobject/package.h>
32 #include "upkg.h"
33 #include "pack.h"
34
35 #define U_OBJECT_GET_PRIV(o) \
36         G_TYPE_INSTANCE_GET_PRIVATE(o, U_TYPE_OBJECT, struct u_object_priv)
37
38
39 struct prop_head {
40         const char *prop_name, *struct_name;
41         unsigned long size, array_idx;
42         bool tag_msb;
43
44         enum {
45                 PROPERTY_END,
46                 PROPERTY_BYTE,
47                 PROPERTY_INTEGER,
48                 PROPERTY_BOOLEAN,
49                 PROPERTY_FLOAT,
50                 PROPERTY_OBJECT,
51                 PROPERTY_NAME,
52                 PROPERTY_STRING,
53                 PROPERTY_CLASS,
54                 PROPERTY_ARRAY,
55                 PROPERTY_STRUCT,
56                 PROPERTY_VECTOR,
57                 PROPERTY_ROTATOR,
58                 PROPERTY_STR,
59                 PROPERTY_MAP,
60                 PROPERTY_FIXEDARRAY,
61         } type;
62 };
63
64 struct u_object_priv {
65         struct upkg_file *f;
66         size_t base, len;
67
68         unsigned char buf[2048];
69         unsigned long nbuf;
70 };
71
72 G_DEFINE_TYPE(UObject, u_object, G_TYPE_OBJECT);
73
74 /*
75  * Determine the actual size (in bytes) of a property, given the 3-bit encoded
76  * size from the tag byte.  Depending on the tag size, there will be 0, 1, 2
77  * or 4 additional size bytes, which are to be found in the provided buffer
78  * which is at least n bytes long.  The result is stored in *size.
79  *
80  * Returns the number of bytes that were read from the buffer, which may be 0.
81  * On failure, (i.e., there was not enough material in the buffer) 0 is stored
82  * in *size.
83  */
84 static unsigned long
85 decode_tag_size(unsigned long *size, unsigned tag_size,
86                 const unsigned char *buf, unsigned long n)
87 {
88         *size = 0;
89
90         switch (tag_size) {
91         case 0: *size =  1; return 0;
92         case 1: *size =  2; return 0;
93         case 2: *size =  4; return 0;
94         case 3: *size = 12; return 0;
95         case 4: *size = 16; return 0;
96         case 5:
97                 if (n < 1)
98                         return 0;
99                 *size = buf[0];
100                 return 1;
101         case 6:
102                 if (n < 2)
103                         return 0;
104                 *size = unpack_16_le(buf);
105                 return 2;
106         case 7:
107                 if (n < 4)
108                         return 0;
109                 *size = unpack_32_le(buf);
110                 return 4;
111         }
112
113         assert(0);
114 }
115
116 static unsigned long
117 decode_array_index(unsigned long *index, const unsigned char *buf,
118                                          unsigned long n)
119 {
120         if (n < 1)
121                 return 0;
122
123         /* TODO: Actually implement this instead of aborting. */
124         assert("FIXME" && !(buf[0] & 0x80));
125
126         *index = buf[0];
127         return 1;
128 }
129
130 /*
131  * Decode the (mostly) generic property header, filling out the struct pointed
132  * to by head.  Returns the number of bytes read from the buffer, or 0 on
133  * failure.
134  */
135 static unsigned long
136 decode_prop_header(struct upkg *upkg, struct prop_head *head,
137                    const unsigned char *buf, unsigned long n)
138 {
139         unsigned long rc, len = 0;
140         unsigned char tag_size;
141         long index;
142
143         rc = upkg_decode_index(&index, buf+len, n-len);
144         if (rc == 0)
145                 return 0;
146         if (!(head->prop_name = upkg_get_name(upkg, index)))
147                 return 0;
148         len += rc;
149
150         /* A property called "None" terminates the list, and does not have
151          * the usual header. */
152         if (!strcmp(head->prop_name, "None")) {
153                 head->type = PROPERTY_END;
154                 head->size = 0;
155                 return len;
156         }
157
158         if (n-len < 1)
159                 return 0;
160         head->tag_msb = (buf[len] >> 7) & 0x01;
161         tag_size      = (buf[len] >> 4) & 0x07;
162         head->type    = (buf[len] >> 0) & 0x0f;
163         len++;
164
165         /*
166          * TODO: Confirm the correct relative ordering of the next three
167          * fields.
168          */
169         if (head->type == PROPERTY_STRUCT) {
170                 rc = upkg_decode_index(&index, buf+len, n-len);
171                 if (rc == 0)
172                         return 0;
173                 if (!(head->struct_name = upkg_get_name(upkg, index)))
174                         return 0;
175                 len += rc;
176         }
177
178         rc = decode_tag_size(&head->size, tag_size, buf+len, n-len);
179         if (rc == 0 && head->size == 0)
180                 return 0;
181         len += rc;
182
183         head->array_idx = 0;
184         if (head->tag_msb && head->type != PROPERTY_BOOLEAN) {
185                 rc = decode_array_index(&head->array_idx, buf+len, n-len);
186                 if (rc == 0)
187                         return 0;
188                 len += rc;
189         }
190
191         return len;
192 }
193
194 /*
195  * TODO: Make this use the uobject_module stuff so packages are not loaded
196  * more than once.
197  */
198 static GTypeModule *
199 open_import_package(UObject *uo, const struct upkg_import *i)
200 {
201         GTypeModule *pkg;
202
203         assert(i->parent == NULL);
204
205         if (strcmp(i->class_package, "Core") != 0
206             || strcmp(i->class_name, "Package") != 0) {
207                 u_err(uo, "import root must be Core.Package");
208                 return NULL;
209         }
210
211         pkg = u_pkg_open(i->name);
212         if (!pkg || !g_type_module_use(pkg)) {
213                 u_err(uo, "failed to open package: %s", i->name);
214                 return NULL;
215         }
216
217         if (!U_PKG(pkg)->pkg) {
218                 g_type_module_unuse(pkg);
219                 u_err(uo, "failed to open package: %s", pkg->name);
220                 return NULL;
221         }
222
223         return pkg;
224 }
225
226 /* Find the export index of a package based on the import chain from another
227  * package.  Returns the (non-negative) offset on success, or a negative value
228  * on failure. */
229 static long resolve_import(struct upkg *pkg, const struct upkg_import *import)
230 {
231         long current, index;
232
233         if (!import->parent)
234                 return -1;
235
236         current = resolve_import(pkg, import->parent);
237         if (current != -1 && current >= 0)
238                 return -42;
239
240         index = upkg_export_find(pkg, current, import->name);
241         if (index < 0)
242                 return -42;
243
244         return index;
245 }
246
247 static GObject *get_import_object(UObject *uo, unsigned long index)
248 {
249         const struct upkg_import *import;
250         GObject *obj = NULL;
251         GTypeModule *pkg;
252         long obj_index;
253
254         import = upkg_get_import(uo->pkg_file->pkg, index);
255         if (!import) {
256                 u_err(uo, "invalid package import: %ld", index);
257                 return NULL;
258         }
259
260         /* Get the package name at the top of the hierarchy. */
261         for (const struct upkg_import *i = import; i; i = i->parent) {
262                 if (i->parent == NULL) {
263                         pkg = open_import_package(uo, i);
264                         if (!pkg)
265                                 return NULL;
266                 }
267         }
268
269         obj_index = resolve_import(U_PKG(pkg)->pkg, import);
270         if (obj_index < 0) {
271                 u_err(uo, "could not find import in package: %s", pkg->name);
272                 goto out;
273         }
274
275         obj = u_object_new_from_package(pkg, obj_index);
276 out:
277         g_type_module_unuse(pkg);
278         return obj;
279 }
280
281 static int decode_object_property(UObject *uo, GValue *val, unsigned long len)
282 {
283         struct u_object_priv *priv = U_OBJECT_GET_PRIV(uo);
284         GObject *obj = NULL;
285         long index;
286         int rc;
287
288         rc = upkg_decode_index(&index, priv->buf+len, priv->nbuf-len);
289         if (rc == 0 || index == 0)
290                 return -1;
291
292         if (index < 0) {
293                 obj = get_import_object(uo, -(index+1));
294         } else {
295                 obj = u_object_new_from_package(uo->pkg, index-1);
296         }
297
298         g_value_init(val, U_TYPE_OBJECT);
299         g_value_take_object(val, obj);
300         return 0;
301 }
302
303 /*
304  * Deserialize an IEEE 754 binary32 value in "little endian" (for whatever
305  * that term is worth in this context).  That is, when interpreted as a little
306  * endian 32-bit unsigned integer: bit 31 is the sign, bits 30-23 are the
307  * (biased) exponent, and bits 22-0 are the encoded part of the significand.
308  *
309  * The implementation is designed to be agnostic of the platform's actual
310  * float type, but the conversion may be lossy if "float" is not itself a
311  * binary32 format.  NaN payloads are not preserved.
312  */
313 static float unpack_binary32_le(const unsigned char *buf)
314 {
315         unsigned long raw;
316         long significand;
317         int exponent;
318         float result;
319
320         raw = unpack_32_le(buf);
321         exponent = (raw & 0x7f800000) >> 23;
322         significand = (raw & 0x007fffff) >> 0;
323
324         switch (exponent) {
325         case 255:
326                 result = significand ? NAN : INFINITY;
327                 break;
328         default:
329                 significand |= 0x00800000;
330                 /* fall through */
331         case 0:
332                 result = ldexpf(significand, exponent-126-24);
333         }
334
335         return copysignf(result, raw & 0x80000000 ? -1 : 1);
336 }
337
338 static unsigned long deserialize_property(UObject *uo, struct prop_head *head)
339 {
340         struct u_object_priv *priv = U_OBJECT_GET_PRIV(uo);
341         unsigned long rc, len = 0;
342         GValue val = {0};
343
344         rc = decode_prop_header(uo->pkg_file->pkg, head, priv->buf, priv->nbuf);
345         if (rc == 0)
346                 return 0;
347         len += rc;
348
349         switch (head->type) {
350         case PROPERTY_END:
351                 break;
352         case PROPERTY_BYTE:
353                 if (head->size != 1 || priv->nbuf-len < head->size)
354                         return 0;
355                 g_value_init(&val, G_TYPE_UCHAR);
356                 g_value_set_uchar(&val, priv->buf[len]);
357                 g_object_set_property(G_OBJECT(uo), head->prop_name, &val);
358                 break;
359         case PROPERTY_INTEGER:
360                 if (head->size != 4 || priv->nbuf-len < head->size)
361                         return 0;
362                 g_value_init(&val, G_TYPE_ULONG);
363                 g_value_set_ulong(&val, unpack_32_le(priv->buf+len));
364                 g_object_set_property(G_OBJECT(uo), head->prop_name, &val);
365                 break;
366         case PROPERTY_BOOLEAN:
367                 if (head->size != 0)
368                         return 0;
369                 g_value_init(&val, G_TYPE_BOOLEAN);
370                 g_value_set_boolean(&val, head->tag_msb);
371                 g_object_set_property(G_OBJECT(uo), head->prop_name, &val);
372                 break;
373         case PROPERTY_OBJECT:
374                 rc = decode_object_property(uo, &val, len);
375                 if (rc != 0)
376                         return 0;
377                 g_object_set_property(G_OBJECT(uo), head->prop_name, &val);
378                 break;
379         case PROPERTY_FLOAT:
380                 if (head->size != 4 || priv->nbuf-len < head->size)
381                         return 0;
382                 g_value_init(&val, G_TYPE_FLOAT);
383                 g_value_set_float(&val, unpack_binary32_le(priv->buf+len));
384                 g_object_set_property(G_OBJECT(uo), head->prop_name, &val);
385                 break;
386         default:
387                 u_warn(uo, "%s: unsupported property type %u",
388                            head->prop_name, (unsigned)head->type);
389         }
390         len += head->size;
391
392         if (len > priv->nbuf) {
393                 long skip = len - priv->nbuf;
394
395                 /* XXX: Long properties are not supported yet, so just seek
396                  * past them for now. */
397                 if (upkg_export_seek(uo->pkg_file, skip, SEEK_CUR) != 0)
398                         return 0;
399
400                 priv->nbuf = 0;
401         } else {
402                 priv->nbuf -= len;
403                 memmove(priv->buf, priv->buf+len, priv->nbuf-len);
404         }
405
406         return len;
407 }
408
409 /* Deserialize properties from an Unreal package. */
410 static int deserialize(UObject *uo)
411 {
412         struct u_object_priv *priv = U_OBJECT_GET_PRIV(uo);
413         struct upkg_file *f = uo->pkg_file;
414         unsigned long rc, tot_len = 0;
415
416         while (1) {
417                 struct prop_head head;
418
419                 /* Prime the buffer; deserialize_property assumes that there's
420                  * enough data for "small" properties already available. */
421                 if (!f->eof) {
422                         void  *buf = priv->buf + priv->nbuf;
423                         size_t amt = sizeof priv->buf - priv->nbuf;
424                         rc = upkg_export_read(f, buf, amt);
425                         if (rc == 0 && priv->nbuf == 0)
426                                 return -1;
427                         priv->nbuf += rc;
428                 }
429
430                 rc = deserialize_property(uo, &head);
431                 if (rc == 0) {
432                         return -1;
433                 }
434                 tot_len += rc;
435
436                 if (head.type == PROPERTY_END)
437                         break;
438         }
439
440         f->base += tot_len;
441         f->len  -= tot_len;
442         upkg_export_seek(f, 0, SEEK_SET);
443
444         return 0;
445 }
446
447 /*
448  * Get the full hierarchical object name for an export, used for diagnostics.
449  * The package name is passed in pname.
450  *
451  * Returns a buffer allocated by malloc on success, or NULL on failure.
452  */
453 static char *get_obj_fullname(const char *pname, const struct upkg_export *e)
454 {
455         size_t total_len = strlen(pname) + 1, len;
456         char *fullname;
457
458         assert(e != NULL);
459
460         for (const struct upkg_export *c = e; c; c = c->parent) {
461                 len = strlen(c->name) + 1;
462                 if (total_len > SIZE_MAX - len)
463                         return NULL;
464                 total_len += len;
465         }
466
467         fullname = malloc(total_len);
468         if (!fullname)
469                 return NULL;
470
471         for (const struct upkg_export *c = e; c; c = c->parent) {
472                 len = strlen(c->name);
473                 assert(total_len > len);
474
475                 total_len -= len + 1;
476                 memcpy(fullname + total_len, c->name, len);
477                 fullname[total_len + len] = c == e ? '\0' : '.';
478         }
479
480         assert(total_len == strlen(pname)+1);
481         memcpy(fullname, pname, total_len-1);
482         fullname[total_len-1] = '.';
483
484         return fullname;
485 }
486
487 int u_object_deserialize(GObject *obj, GTypeModule *pkg, unsigned long idx)
488 {
489         g_return_val_if_fail(IS_U_OBJECT(obj), -1);
490         g_return_val_if_fail(IS_U_PKG(pkg), -1);
491
492         UObject *uo = U_OBJECT(obj);
493         struct upkg *upkg = U_PKG(pkg)->pkg;
494         const struct upkg_export *e;
495         struct upkg_file *f;
496         int rc;
497
498         g_return_val_if_fail(uo->pkg_file == NULL, -1);
499         f = upkg_export_open(upkg, idx);
500         if (!f) {
501                 return -1;
502         }
503
504         uo->pkg = pkg;
505         uo->pkg_file = f;
506
507         e = upkg_get_export(upkg, idx);
508         if (!e)
509                 return -1;
510         uo->pkg_name = get_obj_fullname(pkg->name, e);
511         if (!uo->pkg_name)
512                 return -1;
513
514         rc = U_OBJECT_GET_CLASS(obj)->deserialize(uo);
515         if (rc != 0) {
516                 upkg_export_close(f);
517                 uo->pkg_file = NULL;
518         }
519
520         return rc;
521 }
522
523 GObject *u_object_new_from_package(GTypeModule *pkg, unsigned long idx)
524 {
525         g_return_val_if_fail(IS_U_PKG(pkg), NULL);
526
527         const char *class, *package;
528         GObject *obj = NULL;
529         GType type;
530
531         class = upkg_export_class(U_PKG(pkg)->pkg, idx, &package);
532
533         type = u_object_module_get_class(package, class);
534         if (type) {
535                 obj = g_object_new(type, NULL);
536                 if (u_object_deserialize(obj, pkg, idx) != 0) {
537                         g_object_unref(obj);
538                         return NULL;
539                 }
540         }
541
542         return obj;
543 }
544
545 static void u_object_init(UObject *o)
546 {
547         o->pkg      = NULL;
548         o->pkg_file = NULL;
549 }
550
551 static void u_object_finalize(GObject *o)
552 {
553         UObject *uo = U_OBJECT(o);
554
555         if (uo->pkg_file) {
556                 upkg_export_close(uo->pkg_file);
557         }
558
559         free(uo->pkg_name);
560
561         G_OBJECT_CLASS(u_object_parent_class)->finalize(o);
562 }
563
564 static void u_object_class_init(UObjectClass *class)
565 {
566         g_type_class_add_private(class, sizeof (struct u_object_priv));
567         GObjectClass *go = G_OBJECT_CLASS(class);
568
569         class->deserialize = deserialize;
570         go->finalize       = u_object_finalize;
571 }
572
573 /*
574  * Prepend a prefix to a printf-style format string.  Unfortunately, there's
575  * no way to construct va_list values on the fly in C, so we cannot simply
576  * prepend %s and pass the prefix as an argument.  Any % characters in the
577  * prefix will be escaped.
578  *
579  * Returns a pointer to a newly allocated string, which should be freed by the
580  * caller, or NULL otherwise.
581  */
582 static char *prepend_fmt(const char *prefix, const char *fmt)
583 {
584         size_t prefix_len = strlen(prefix), fmt_len = strlen(fmt);
585         char *new_fmt;
586
587         if (prefix_len > SIZE_MAX/2 - sizeof ": ")
588                 return NULL;
589         if (2*prefix_len > SIZE_MAX - fmt_len)
590                 return NULL;
591
592         new_fmt = malloc(2*prefix_len + fmt_len + 1);
593         if (new_fmt) {
594                 size_t offset = 0;
595
596                 for (size_t i = 0; i < prefix_len; i++) {
597                         if (prefix[i] == '%')
598                                 new_fmt[offset++] = '%';
599                         new_fmt[offset++] = prefix[i];
600                 }
601
602                 new_fmt[offset++] = ':';
603                 new_fmt[offset++] = ' ';
604                 strcpy(new_fmt+offset, fmt);
605         }
606
607         return new_fmt;
608 }
609
610 /* Logging helpers that automatically prepend the UObject class information. */
611 void u_vlog_full(GObject *o, GLogLevelFlags level, const char *fmt, va_list ap)
612 {
613         g_return_if_fail(IS_U_OBJECT(o));
614         UObject *uo = U_OBJECT(o);
615         char *new_fmt = NULL;
616
617         if (uo->pkg_name) {
618                 new_fmt = prepend_fmt(uo->pkg_name, fmt);
619                 if (!new_fmt) {
620                         g_log(G_OBJECT_TYPE_NAME(o), level, "%s",
621                               uo->pkg_file->name);
622                 } else {
623                         fmt = new_fmt;
624                 }
625         }
626
627         g_logv(G_OBJECT_TYPE_NAME(o), level, fmt, ap);
628         free(new_fmt);
629 }
630
631 void u_log_full(GObject *o, GLogLevelFlags level, const char *fmt, ...)
632 {
633         va_list ap;
634
635         va_start(ap, fmt);
636         u_vlog_full(o, level, fmt, ap);
637         va_end(ap);
638 }