]> git.draconx.ca Git - upkg.git/blob - src/uobject/uobject.c
upkg: Print object properties in info mode.
[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 "upkg.h"
27 #include "pack.h"
28
29 #define U_OBJECT_GET_PRIV(o) \
30         G_TYPE_INSTANCE_GET_PRIVATE(o, U_OBJECT_TYPE, struct u_object_priv)
31
32 enum {
33         PROPERTY_BYTE = 1,
34         PROPERTY_INTEGER,
35         PROPERTY_BOOLEAN,
36         PROPERTY_FLOAT,
37         PROPERTY_OBJECT,
38         PROPERTY_NAME,
39         PROPERTY_STRING,
40         PROPERTY_CLASS,
41         PROPERTY_ARRAY,
42         PROPERTY_STRUCT,
43         PROPERTY_VECTOR,
44         PROPERTY_ROTATOR,
45         PROPERTY_STR,
46         PROPERTY_MAP,
47         PROPERTY_FIXEDARRAY,
48 };
49
50 struct u_object_priv {
51         struct upkg_file *f;
52         size_t base, len;
53
54         unsigned char buf[2048];
55         unsigned long nbuf;
56 };
57
58 G_DEFINE_TYPE(UObject, u_object, G_TYPE_OBJECT);
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 u_object_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                 g_object_set_property(G_OBJECT(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                 g_object_set_property(G_OBJECT(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 *uo)
145 {
146         struct u_object_priv *priv = U_OBJECT_GET_PRIV(uo);
147         struct upkg_file *f = uo->pkg_file;
148         unsigned long rc, tot_len = 0;
149
150         while (1) {
151                 unsigned long len = 0;
152                 const char *name;
153                 long tmp;
154
155                 /* Read some data into buffer. */
156                 if (!f->eof) {
157                         void  *buf = priv->buf + priv->nbuf;
158                         size_t amt = sizeof priv->buf - priv->nbuf;
159                         rc = upkg_export_read(f, buf, amt);
160                         if (rc == 0 && priv->nbuf == 0)
161                                 return -1;
162                         priv->nbuf += rc;
163                 }
164
165                 /* Get the property name. */
166                 rc = upkg_decode_index(&tmp, priv->buf+len, priv->nbuf-len);
167                 if (rc == 0)
168                         return -1;
169                 len = rc;
170
171                 name = upkg_get_name(uo->pkg, tmp);
172                 if (!name) {
173                         return -1;
174                 } else if (strcmp(name, "None") == 0) {
175                         tot_len += len;
176                         break;
177                 }
178
179                 rc = decode_property(uo, name, f, len);
180                 if (rc == 0)
181                         return -1;
182                 len = rc;
183
184                 tot_len += len;
185         }
186
187         f->base += tot_len;
188         f->len  -= tot_len;
189         upkg_export_seek(f, 0, SEEK_SET);
190
191         return 0;
192 }
193
194 int u_object_deserialize(GObject *obj, struct upkg *pkg, unsigned long idx)
195 {
196         g_return_val_if_fail(IS_U_OBJECT(obj), -1);
197         UObject *uo = U_OBJECT(obj);
198         struct upkg_file *f;
199         int rc;
200
201         g_return_val_if_fail(uo->pkg_file == NULL, -1);
202         f = upkg_export_open(pkg, idx);
203         if (!f) {
204                 return -1;
205         }
206
207         uo->pkg      = pkg;
208         uo->pkg_file = f;
209
210         rc = U_OBJECT_GET_CLASS(obj)->deserialize(uo);
211         if (rc != 0) {
212                 upkg_export_close(f);
213                 uo->pkg_file = NULL;
214         }
215
216         return rc;
217 }
218
219 static void u_object_init(UObject *o)
220 {
221         o->pkg      = NULL;
222         o->pkg_file = NULL;
223 }
224
225 static void u_object_finalize(GObject *o)
226 {
227         UObject *uo = U_OBJECT(o);
228
229         if (uo->pkg_file) {
230                 upkg_export_close(uo->pkg_file);
231         }
232
233         G_OBJECT_CLASS(u_object_parent_class)->finalize(o);
234 }
235
236 static void u_object_class_init(UObjectClass *class)
237 {
238         g_type_class_add_private(class, sizeof (struct u_object_priv));
239         GObjectClass *go = G_OBJECT_CLASS(class);
240
241         class->deserialize = deserialize;
242         go->finalize       = u_object_finalize;
243 }