]> git.draconx.ca Git - upkg.git/blob - src/uobject.c
e271a678bb83f2f3b44ab568f4d6a041805f50d2
[upkg.git] / src / uobject.c
1 /*
2  *  upkg: tool for manipulating Unreal Tournament packages.
3  *  Copyright (C) 2009 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 2 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, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24 #include <glib-object.h>
25
26 #include <uobject/uobject.h>
27 #include "upkg.h"
28 #include "pack.h"
29
30 #define UOBJECT_GET_PRIV(o) \
31         G_TYPE_INSTANCE_GET_PRIVATE(o, UOBJECT_TYPE, struct uobject_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 uobject_property {
52         const char *name;
53         GValue val;
54 };
55
56 struct uobject_priv {
57         struct upkg_file *f;
58         size_t base, len;
59
60         unsigned char buf[2048];
61         unsigned long nbuf;
62 };
63
64 G_DEFINE_TYPE(UObject, uobject, G_TYPE_OBJECT);
65
66 static unsigned long
67 get_real_size(unsigned long *real, unsigned size, unsigned char *buf, size_t n)
68 {
69         assert(size < 8);
70
71         *real = 0;
72         switch (size) {
73         case 0: *real =  1; return 0;
74         case 1: *real =  2; return 0;
75         case 2: *real =  4; return 0;
76         case 3: *real = 12; return 0;
77         case 4: *real = 16; return 0;
78         case 5:
79                 if (n < 1) return 0;
80                 *real = *buf;
81                 return 1;
82         case 6:
83                 if (n < 2) return 0;
84                 *real = unpack_16_le(buf);
85                 return 2;
86         case 7:
87                 if (n < 4) return 0;
88                 *real = unpack_32_le(buf);
89                 return 4;
90         }
91
92         return 0;
93 }
94
95 static unsigned long
96 decode_property(UObject *o, const char *name, struct upkg_file *f, unsigned long len)
97 {
98         struct uobject_priv *priv = UOBJECT_GET_PRIV(o);
99         unsigned long real_size, rc;
100         int type, size, top;
101         GValue val = {0};
102
103         if (priv->nbuf-len < 1)
104                 return 0;
105         
106         type = (priv->buf[len] >> 0) & 0x0f;
107         size = (priv->buf[len] >> 4) & 0x07;
108         top  = (priv->buf[len] >> 7) & 0x01;
109         len += 1;
110
111         rc = get_real_size(&real_size, size, priv->buf, priv->nbuf-len);
112         if (real_size == 0)
113                 return 0;
114         len += rc;
115
116         switch (type) {
117         case PROPERTY_BYTE:
118                 if (priv->nbuf-len < 1)
119                         return 0;
120                 g_value_init(&val, G_TYPE_UCHAR);
121                 g_value_set_uchar(&val, priv->buf[len]);
122                 g_object_set_property(G_OBJECT(o), name, &val);
123                 break;
124         case PROPERTY_INTEGER:
125                 if (priv->nbuf-len < 4)
126                         return 0;
127                 g_value_init(&val, G_TYPE_ULONG);
128                 g_value_set_ulong(&val, unpack_32_le(priv->buf+len));
129                 g_object_set_property(G_OBJECT(o), name, &val);
130                 break;
131         default:
132                 fprintf(stderr, "Unhandled property type %x\n", (unsigned)type);
133         }
134
135         real_size += len;
136         if (real_size + len <= priv->nbuf) {
137                 priv->nbuf -= real_size;
138                 memmove(priv->buf, priv->buf+real_size, priv->nbuf);
139         } else {
140                 long skip = real_size - priv->nbuf;
141                 if (upkg_export_seek(f, skip, SEEK_CUR) != 0)
142                         return 0;
143                 priv->nbuf = 0;
144         }
145
146         return real_size;
147 }
148
149 /* Deserialize properties from an Unreal package. */
150 static int deserialize(UObject *o, struct upkg_file *f)
151 {
152         struct uobject_priv *priv = UOBJECT_GET_PRIV(o);
153         unsigned long rc, tot_len = 0;
154
155         while (1) {
156                 unsigned long len = 0;
157                 const char *name;
158                 long tmp;
159
160                 /* Read some data into buffer. */
161                 if (!f->eof) {
162                         void  *buf = priv->buf + priv->nbuf;
163                         size_t amt = sizeof priv->buf - priv->nbuf;
164                         rc = upkg_export_read(f, buf, amt);
165                         if (rc == 0)
166                                 return -1;
167                         priv->nbuf += rc;
168                 }
169
170                 /* Get the property name. */
171                 rc = upkg_decode_index(&tmp, priv->buf+len, priv->nbuf-len);
172                 if (rc == 0)
173                         return -1;
174                 len = rc;
175
176                 name = upkg_get_name(f->pkg, tmp);
177                 if (!name) {
178                         return -1;
179                 } else if (strcmp(name, "None") == 0) {
180                         tot_len += len;
181                         break;
182                 }
183
184                 rc = decode_property(UOBJECT(o), name, f, len);
185                 if (rc == 0)
186                         return -1;
187                 len = rc;
188
189                 tot_len += len;
190         }
191
192         f->base += tot_len;
193         f->len  -= tot_len;
194         upkg_export_seek(f, 0, SEEK_SET);
195
196         return 0;
197 }
198
199 int uobject_deserialize(GObject *obj, struct upkg *pkg, unsigned long idx)
200 {
201         g_return_val_if_fail(IS_UOBJECT(obj), -1);
202         UObject *uo = UOBJECT(obj);
203         struct upkg_file *f;
204         int rc;
205
206         g_return_val_if_fail(uo->pkg_file == NULL, -1);
207         f = upkg_export_open(pkg, idx);
208         if (!f) {
209                 return -1;
210         }
211
212         rc = UOBJECT_GET_CLASS(obj)->deserialize(uo, f);
213         if (rc != 0) {
214                 upkg_export_close(f);
215         } else {
216                 uo->pkg      = pkg;
217                 uo->pkg_idx  = idx;
218                 uo->pkg_file = f;
219         }
220
221         return rc;
222 }
223
224 static void uobject_init(UObject *o)
225 {
226         o->pkg      = NULL;
227         o->pkg_file = NULL;
228         o->pkg_idx  = 0;
229 }
230
231 static void uobject_finalize(GObject *o)
232 {
233         UObject *uo = UOBJECT(o);
234
235         if (uo->pkg_file) {
236                 upkg_export_close(uo->pkg_file);
237         }
238
239         G_OBJECT_CLASS(uobject_parent_class)->finalize(o);
240 }
241
242 static void uobject_class_init(UObjectClass *class)
243 {
244         g_type_class_add_private(class, sizeof (struct uobject_priv));
245         GObjectClass *go = G_OBJECT_CLASS(class);
246
247         class->deserialize = deserialize;
248         go->finalize       = uobject_finalize;
249 }