]> git.draconx.ca Git - upkg.git/blob - src/libupkg.c
Add copyright and licensing info.
[upkg.git] / src / libupkg.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
24 #include "upkg.h"
25 #include "pack.h"
26
27 #define MIN(a, b) ((a) < (b) ? (a) : (b))
28
29 struct upkg_name {
30         unsigned long flags;
31         char *name;
32 };
33
34 struct upkg_export {
35         const char *name;
36
37         long package, class, super;
38         unsigned long flags;
39         unsigned long size, offset;
40 };
41
42 struct upkg_import {
43         const char *class_package, *class_name, *object_name;
44         long package;
45 };
46
47 struct upkg_private {
48         FILE *f;
49
50         struct upkg_name   *names;
51         struct upkg_export *exports;
52         struct upkg_import *imports;
53
54         unsigned long name_offset, export_offset, import_offset;
55         unsigned char guid[16];
56 };
57
58 /*
59  * Decode the compact index format from the upkg.  This format is fucked.
60  * Stores the result in *val and returns the number of input bytes read (or 0
61  * if the input is invalid, in which case *val is undefined).
62  */
63 static size_t decode_index(long *val, unsigned char *bytes, size_t n)
64 {
65         size_t i = 0;
66
67         *val = 0;
68         while (i < MIN(n, 5)) {
69                 /*
70                  * Least significant bytes are first, so we need to do this
71                  * nonsense.
72                  */
73                 long tmp = bytes[i] & (i == 0 ? 0x3f : 0x7f);
74
75                 if (i > 0) tmp <<= 6;
76                 if (i > 1) tmp <<= 7*(i-1);
77                 *val += tmp;
78
79                 if (!(bytes[i] & (i == 0 ? 0x40 : 0x80))) {
80                         i++;
81                         break;
82                 }
83
84                 i++;
85         }
86
87         if (i > MIN(n, 5) || n == 0)
88                 return 0;
89         if (bytes[0] & 0x80)
90                 *val = -*val;
91         return i;
92 }
93
94 static struct upkg *init_upkg(unsigned char hdr[static UPKG_HDR_SIZE])
95 {
96         struct upkg *pkg;
97
98         pkg = malloc(sizeof *pkg);
99         if (!pkg) {
100                 return NULL;
101         }
102
103         pkg->priv = malloc(sizeof *pkg->priv);
104         if (!pkg->priv) {
105                 free(pkg);
106                 return NULL;
107         }
108
109         pkg->version      = unpack_16_le(hdr+4);
110         pkg->license      = unpack_16_le(hdr+6);
111         pkg->flags        = unpack_32_le(hdr+8);
112         pkg->name_count   = unpack_32_le(hdr+12);
113         pkg->export_count = unpack_32_le(hdr+20);
114         pkg->import_count = unpack_32_le(hdr+28);
115
116         pkg->priv->name_offset   = unpack_32_le(hdr+16);
117         pkg->priv->export_offset = unpack_32_le(hdr+24);
118         pkg->priv->import_offset = unpack_32_le(hdr+32);
119
120         return pkg;
121 }
122
123 static int pkg_init_names(struct upkg *pkg)
124 {
125         size_t rc, len, nbuf = 0;
126         unsigned long index = 0;
127         char buf[512];
128
129         if (fseek(pkg->priv->f, pkg->priv->name_offset, SEEK_SET) != 0)
130                 return -1;
131
132         pkg->priv->names = malloc(pkg->name_count * sizeof *pkg->priv->names);
133         if (!pkg->priv->names)
134                 return -1;
135
136         while (index < pkg->name_count) {
137                 struct upkg_name *name = &pkg->priv->names[index];
138
139                 /* Read some data into buffer. */
140                 if (!feof(pkg->priv->f)) {
141                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
142                         if (rc == 0)
143                                 goto err;
144                         nbuf += rc;
145                 }
146
147                 if (pkg->version >= 64) {
148                         len = buf[0];
149                         if (nbuf <= len + 4 || buf[len])
150                                 goto err;
151                         name->name = malloc(len);
152                         if (!name->name)
153                                 goto err;
154                         memcpy(name->name, buf+1, len);
155                         name->flags = unpack_32_le(buf+len+1);
156                         len += 4;
157
158                         nbuf -= len + 1;
159                         memmove(buf, buf+len+1, nbuf);
160                         index++;
161                 } else {
162                         char *c = memchr(buf, 0, nbuf);
163                         if (!c || nbuf <= c - buf + 5)
164                                 goto err;
165                         len = c - buf + 1;
166                         name->name = malloc(len);
167                         if (!name->name)
168                                 goto err;
169                         memcpy(name->name, buf, len);
170                         name->flags = unpack_32_le(buf+len);
171                         len += 4;
172
173                         nbuf -= len;
174                         memmove(buf, buf+len, nbuf);
175                         index++;
176                 }
177         }
178
179         return 0;
180 err:
181         for (unsigned i = 0; i < index; i++)
182                 free(pkg->priv->names[i].name);
183         free(pkg->priv->names);
184         return -1;
185 }
186
187 static int pkg_init_exports(struct upkg *pkg)
188 {
189         size_t rc, len, nbuf = 0;
190         unsigned long index = 0;
191         char buf[512];
192
193         if (fseek(pkg->priv->f, pkg->priv->export_offset, SEEK_SET) != 0)
194                 return -1;
195
196         pkg->priv->exports = malloc(pkg->export_count * sizeof *pkg->priv->exports);
197         if (!pkg->priv->exports)
198                 return -1;
199
200         while (index < pkg->export_count) {
201                 struct upkg_export *export = &pkg->priv->exports[index];
202                 long tmp;
203
204                 /* Read some data into buffer. */
205                 if (!feof(pkg->priv->f)) {
206                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
207                         if (rc == 0)
208                                 goto err;
209                         nbuf += rc;
210                 }
211
212                 len = 0;
213                 rc = decode_index(&export->class, buf+len, nbuf-len);
214                 if (rc == 0) goto err;
215                 len += rc;
216
217                 rc = decode_index(&export->super, buf+len, nbuf-len);
218                 if (rc == 0) goto err;
219                 len += rc;
220
221                 if (nbuf-len < 4) goto err;
222                 export->package = unpack_32_le(buf+len);
223                 len += 4;
224
225                 rc = decode_index(&tmp, buf+len, nbuf-len);
226                 if (rc == 0 || tmp < 0 || tmp >= pkg->name_count) goto err;
227                 export->name = pkg->priv->names[tmp].name;
228                 len += rc;
229
230                 if (nbuf-len < 4) goto err;
231                 export->flags = unpack_32_le(buf+len);
232                 len += 4;
233
234                 rc = decode_index(&export->size, buf+len, nbuf-len);
235                 if (rc == 0) goto err;
236                 len += rc;
237
238                 if (export->size) {
239                         rc = decode_index(&export->offset, buf+len, nbuf-len);
240                         if (rc == 0) goto err;
241                         len += rc;
242                 }
243
244                 nbuf -= len;
245                 memmove(buf, buf+len, nbuf);
246                 index++;
247         }
248
249         return 0;
250 err:
251         free(pkg->priv->exports);
252         return -1;
253 }
254
255 static int pkg_init_imports(struct upkg *pkg)
256 {
257         size_t rc, len, nbuf = 0;
258         unsigned long index = 0;
259         char buf[512];
260
261         if (fseek(pkg->priv->f, pkg->priv->import_offset, SEEK_SET) != 0)
262                 return -1;
263
264         pkg->priv->imports = malloc(pkg->import_count * sizeof *pkg->priv->imports);
265         if (!pkg->priv->imports)
266                 return -1;
267
268         while (index < pkg->import_count) {
269                 struct upkg_import *import = &pkg->priv->imports[index];
270                 long tmp;
271
272                 /* Read some data into buffer. */
273                 if (!feof(pkg->priv->f)) {
274                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
275                         if (rc == 0)
276                                 goto err;
277                         nbuf += rc;
278                 }
279
280                 len = 0;
281                 rc = decode_index(&tmp, buf+len, nbuf-len);
282                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
283                 import->class_package = pkg->priv->names[tmp].name;
284                 len += rc;
285
286                 rc = decode_index(&tmp, buf+len, nbuf-len);
287                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
288                 import->class_name = pkg->priv->names[tmp].name;
289                 len += rc;
290
291                 if (nbuf-len < 4) goto err;
292                 import->package = unpack_32_le(buf+len);
293                 len += 4;
294
295                 rc = decode_index(&tmp, buf+len, nbuf-len);
296                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
297                 import->object_name = pkg->priv->names[tmp].name;
298                 len += rc;
299
300                 nbuf -= len;
301                 memmove(buf, buf+len, nbuf);
302                 index++;
303         }
304
305         return 0;
306 err:
307         free(pkg->priv->imports);
308         return -1;
309 }
310
311 struct upkg *upkg_fopen(const char *path)
312 {
313         unsigned char hdr_buf[UPKG_HDR_SIZE];
314         struct upkg *pkg;
315         FILE *f;
316
317         if (!(f = fopen(path, "rb"))) {
318                 return NULL;
319         }
320         if (fread(hdr_buf, sizeof hdr_buf, 1, f) != 1) {
321                 goto err1;
322         }
323         if (unpack_32_le(hdr_buf) != UPKG_HDR_MAGIC) {
324                 goto err1;
325         }
326
327         /* Initialize package structure. */
328         pkg = init_upkg(hdr_buf);
329         if (!pkg) {
330                 goto err1;
331         }
332         pkg->priv->f = f;
333
334         if (pkg_init_names(pkg) != 0) {
335                 goto err2;
336         }
337
338         if (pkg_init_exports(pkg) != 0) {
339                 goto err3;
340         }
341
342         if (pkg_init_imports(pkg) != 0) {
343                 goto err4;
344         }
345
346         return pkg;
347 err4:
348         free(pkg->priv->exports);
349 err3:
350         for (unsigned i = 0; i < pkg->name_count; i++)
351                 free(pkg->priv->names[i].name);
352         free(pkg->priv->names);
353 err2:
354         free(pkg->priv);
355         free(pkg);
356 err1:
357         fclose(f);
358         return NULL;
359 }
360
361 int upkg_close(struct upkg *pkg)
362 {
363         int rc = 0;
364
365         if (pkg->priv->f) {
366                 rc = fclose(pkg->priv->f);
367
368                 for (unsigned i = 0; i < pkg->name_count; i++) {
369                         free(pkg->priv->names[i].name);
370                 }
371         }
372
373         free(pkg->priv->imports);
374         free(pkg->priv->exports);
375         free(pkg->priv->names);
376         free(pkg->priv);
377         free(pkg);
378
379         return rc;
380 }
381
382 const char *upkg_get_name(struct upkg *pkg, unsigned long idx)
383 {
384         if (idx >= pkg->name_count)
385                 return 0;
386         return pkg->priv->names[idx].name;
387 }