]> git.draconx.ca Git - upkg.git/blob - src/uobject/uobject.c
upkg: Print out object file offsets at high verbosity levels.
[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 <assert.h>
23 #include <glib-object.h>
24
25 #include <uobject/uobject.h>
26 #include <uobject/module.h>
27 #include "upkg.h"
28 #include "pack.h"
29
30 #define U_OBJECT_GET_PRIV(o) \
31         G_TYPE_INSTANCE_GET_PRIVATE(o, U_TYPE_OBJECT, struct u_object_priv)
32
33 enum {
34         PROPERTY_BYTE = 1,
35         PROPERTY_INTEGER,
36         PROPERTY_BOOLEAN,
37         PROPERTY_FLOAT,
38         PROPERTY_OBJECT,
39         PROPERTY_NAME,
40         PROPERTY_STRING,
41         PROPERTY_CLASS,
42         PROPERTY_ARRAY,
43         PROPERTY_STRUCT,
44         PROPERTY_VECTOR,
45         PROPERTY_ROTATOR,
46         PROPERTY_STR,
47         PROPERTY_MAP,
48         PROPERTY_FIXEDARRAY,
49 };
50
51 struct u_object_priv {
52         struct upkg_file *f;
53         size_t base, len;
54
55         unsigned char buf[2048];
56         unsigned long nbuf;
57 };
58
59 G_DEFINE_TYPE(UObject, u_object, G_TYPE_OBJECT);
60
61 static unsigned long
62 get_real_size(unsigned long *real, unsigned size, unsigned char *buf, size_t n)
63 {
64         assert(size < 8);
65
66         *real = 0;
67         switch (size) {
68         case 0: *real =  1; return 0;
69         case 1: *real =  2; return 0;
70         case 2: *real =  4; return 0;
71         case 3: *real = 12; return 0;
72         case 4: *real = 16; return 0;
73         case 5:
74                 if (n < 1) return 0;
75                 *real = *buf;
76                 return 1;
77         case 6:
78                 if (n < 2) return 0;
79                 *real = unpack_16_le(buf);
80                 return 2;
81         case 7:
82                 if (n < 4) return 0;
83                 *real = unpack_32_le(buf);
84                 return 4;
85         }
86
87         return 0;
88 }
89
90 static int decode_object_property(UObject *uo, GValue *val, unsigned long len)
91 {
92         struct u_object_priv *priv = U_OBJECT_GET_PRIV(uo);
93         GObject *obj = NULL;
94         long index;
95         int rc;
96
97         rc = upkg_decode_index(&index, priv->buf+len, priv->nbuf-len);
98         if (rc == 0 || index == 0)
99                 return -1;
100
101         if (index < 0) {
102                 fprintf(stderr, "Imports not supported yet.\n");
103         } else {
104                 obj = u_object_new_from_package(uo->pkg, index-1);
105         }
106
107         g_value_init(val, U_TYPE_OBJECT);
108         g_value_take_object(val, obj);
109         return 0;
110 }
111
112 /*
113  * XXX: I must have been smoking the happy plant when I started property
114  * decoding.  The tracking of various sizes in these functions makes no
115  * sense at all.
116  */
117
118 static unsigned long
119 decode_property(UObject *o, const char *name, struct upkg_file *f, unsigned long len)
120 {
121         struct u_object_priv *priv = U_OBJECT_GET_PRIV(o);
122         unsigned long real_size, rc;
123         int type, size, top;
124         GValue val = {0};
125
126         if (priv->nbuf-len < 1)
127                 return 0;
128         
129         type = (priv->buf[len] >> 0) & 0x0f;
130         size = (priv->buf[len] >> 4) & 0x07;
131         top  = (priv->buf[len] >> 7) & 0x01;
132         len += 1;
133
134         rc = get_real_size(&real_size, size, priv->buf, priv->nbuf-len);
135         if (real_size == 0)
136                 return 0;
137         len += rc;
138
139         switch (type) {
140         case PROPERTY_BYTE:
141                 if (priv->nbuf-len < 1)
142                         return 0;
143                 g_value_init(&val, G_TYPE_UCHAR);
144                 g_value_set_uchar(&val, priv->buf[len]);
145                 g_object_set_property(G_OBJECT(o), name, &val);
146                 break;
147         case PROPERTY_INTEGER:
148                 if (priv->nbuf-len < 4)
149                         return 0;
150                 g_value_init(&val, G_TYPE_ULONG);
151                 g_value_set_ulong(&val, unpack_32_le(priv->buf+len));
152                 g_object_set_property(G_OBJECT(o), name, &val);
153                 break;
154         case PROPERTY_OBJECT:
155                 rc = decode_object_property(o, &val, len);
156                 if (rc != 0)
157                         return 0;
158                 g_object_set_property(G_OBJECT(o), name, &val);
159                 break;
160         default:
161                 fprintf(stderr, "Unhandled property type %x\n", (unsigned)type);
162         }
163
164         real_size += len;
165         if (real_size + len <= priv->nbuf) {
166                 priv->nbuf -= real_size;
167                 memmove(priv->buf, priv->buf+real_size, priv->nbuf);
168         } else {
169                 long skip = real_size - priv->nbuf;
170                 if (upkg_export_seek(f, skip, SEEK_CUR) != 0)
171                         return 0;
172                 priv->nbuf = 0;
173         }
174
175         return real_size;
176 }
177
178 /* Deserialize properties from an Unreal package. */
179 static int deserialize(UObject *uo)
180 {
181         struct u_object_priv *priv = U_OBJECT_GET_PRIV(uo);
182         struct upkg_file *f = uo->pkg_file;
183         unsigned long rc, tot_len = 0;
184
185         while (1) {
186                 unsigned long len = 0;
187                 const char *name;
188                 long tmp;
189
190                 /* Read some data into buffer. */
191                 if (!f->eof) {
192                         void  *buf = priv->buf + priv->nbuf;
193                         size_t amt = sizeof priv->buf - priv->nbuf;
194                         rc = upkg_export_read(f, buf, amt);
195                         if (rc == 0 && priv->nbuf == 0)
196                                 return -1;
197                         priv->nbuf += rc;
198                 }
199
200                 /* Get the property name. */
201                 rc = upkg_decode_index(&tmp, priv->buf+len, priv->nbuf-len);
202                 if (rc == 0)
203                         return -1;
204                 len = rc;
205
206                 name = upkg_get_name(uo->pkg, tmp);
207                 if (!name) {
208                         return -1;
209                 } else if (strcmp(name, "None") == 0) {
210                         tot_len += len;
211                         break;
212                 }
213
214                 rc = decode_property(uo, name, f, len);
215                 if (rc == 0)
216                         return -1;
217                 len = rc;
218
219                 tot_len += len;
220         }
221
222         f->base += tot_len;
223         f->len  -= tot_len;
224         upkg_export_seek(f, 0, SEEK_SET);
225
226         return 0;
227 }
228
229 int u_object_deserialize(GObject *obj, struct upkg *pkg, unsigned long idx)
230 {
231         g_return_val_if_fail(IS_U_OBJECT(obj), -1);
232         UObject *uo = U_OBJECT(obj);
233         struct upkg_file *f;
234         int rc;
235
236         g_return_val_if_fail(uo->pkg_file == NULL, -1);
237         f = upkg_export_open(pkg, idx);
238         if (!f) {
239                 return -1;
240         }
241
242         uo->pkg      = pkg;
243         uo->pkg_file = f;
244
245         rc = U_OBJECT_GET_CLASS(obj)->deserialize(uo);
246         if (rc != 0) {
247                 upkg_export_close(f);
248                 uo->pkg_file = NULL;
249         }
250
251         return rc;
252 }
253
254 GObject *u_object_new_from_package(struct upkg *upkg, unsigned long idx)
255 {
256         const struct upkg_export *export;
257         const char *class, *package;
258         GObject *obj = NULL;
259         GType type;
260
261         class = upkg_export_class(upkg, idx, &package);
262
263         type = u_object_module_get_class(package, class);
264         if (type) {
265                 obj = g_object_new(type, NULL);
266                 if (u_object_deserialize(obj, upkg, idx) != 0) {
267                         g_object_unref(obj);
268                         return NULL;
269                 }
270         }
271
272         return obj;
273 }
274
275 static void u_object_init(UObject *o)
276 {
277         o->pkg      = NULL;
278         o->pkg_file = NULL;
279 }
280
281 static void u_object_finalize(GObject *o)
282 {
283         UObject *uo = U_OBJECT(o);
284
285         if (uo->pkg_file) {
286                 upkg_export_close(uo->pkg_file);
287         }
288
289         G_OBJECT_CLASS(u_object_parent_class)->finalize(o);
290 }
291
292 static void u_object_class_init(UObjectClass *class)
293 {
294         g_type_class_add_private(class, sizeof (struct u_object_priv));
295         GObjectClass *go = G_OBJECT_CLASS(class);
296
297         class->deserialize = deserialize;
298         go->finalize       = u_object_finalize;
299 }