3 * upkg: tool for manipulating Unreal Tournament packages.
4 * Copyright © 2020, 2022 Nick Bowler
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
27 #include <uobject/loadable.h>
28 #include <uobject/exportable.h>
32 # define SIZE_MAX ((size_t)-1)
37 #include <uobject/uobject.h>
38 #include <engine/texture.h>
40 /* Hack to work around broken type parsing in GOB2. */
41 typedef float engine_mesh_vertex[3];
43 struct engine_mesh_face {
45 unsigned short verts[3];
46 unsigned short texture;
51 class Engine:Mesh from U:Object (dynamic)
52 (interface U:Object:Loadable)
53 (interface U:Object:Exportable)
55 private struct upkg_file pkg_file;
57 protected long vert_base;
58 protected long vert_num;
59 protected long tri_base;
60 protected long tri_num;
61 protected long conn_base;
62 protected long conn_num;
63 protected long vlink_base;
64 protected long vlink_num;
66 protected engine_mesh_vertex *vertices;
67 protected struct engine_mesh_face *faces;
69 protected long num_textures = 0;
70 protected Engine:Texture **textures destroy {
72 for (i = 0; i < self->num_textures; i++) {
74 g_object_unref(textures[i]);
79 private int skip_array(struct UObject *uo, long *nent, long entsz,
80 unsigned char *buf, size_t buflen)
82 struct upkg_file *f = uo->pkg_file;
83 unsigned long buf_offset = f->offset - buflen;
84 unsigned char mybuf[9];
89 buf_offset = f->offset;
90 buflen = upkg_export_read(f, mybuf, sizeof mybuf);
94 if (f->pkg->version >= 62) {
97 skip = unpack_32_le(buf);
101 pos += (sz = upkg_decode_index(nent, buf+pos, buflen-pos));
104 if (*nent > SIZE_MAX / entsz) {
105 u_warn(uo, "bogus array size");
108 sz = (size_t)*nent * entsz;
110 if (f->pkg->version >= 62 && sz != skip - f->base - buf_offset - pos) {
111 u_warn(uo, "array skip does not match array size");
115 if (upkg_export_seek(f, buf_offset + pos + sz, SEEK_SET) != 0)
121 private size_t fill_buf(struct upkg_file *f, unsigned char *buf,
122 size_t pos, size_t buflen)
124 size_t rc, rem = buflen - pos;
126 memmove(buf, buf+pos, rem);
127 rem += upkg_export_read(f, buf+rem, buflen-rem);
131 private int load_vertices(Self *self)
133 struct upkg_file *f = &self->_priv->pkg_file;
134 unsigned char buf[100];
138 if (upkg_export_seek(f, self->vert_base, SEEK_SET) != 0)
141 buflen = upkg_export_read(f, buf, sizeof buf);
142 pos = upkg_decode_index(&n, buf, buflen);
143 g_return_val_if_fail(pos && n == self->vert_num, -1);
145 if (self->vert_num > SIZE_MAX / sizeof self->vertices[0]) {
146 u_err(self, "failed to allocate memory");
150 self->vertices = malloc(self->vert_num * sizeof self->vertices[0]);
151 if (!self->vertices) {
152 u_err(self, "failed to allocate memory");
156 for (i = 0; i < n; i++) {
157 unsigned long vertex;
159 if (buflen - pos < 4) {
160 buflen = self_fill_buf(f, buf, pos, buflen);
164 if (buflen - pos < 4) {
165 u_err(self, "failed to read vertex data");
166 free(self->vertices);
170 vertex = unpack_32_le(buf+pos); pos += 4;
171 self->vertices[i][0] = (vertex & 0x7ff) / 8.0;
172 if (self->vertices[i][0] >= 128)
173 self->vertices[i][0] -= 256;
175 self->vertices[i][1] = ((vertex >> 11) & 0x7ff) / 8.0;
176 if (self->vertices[i][1] >= 128)
177 self->vertices[i][1] -= 256;
179 self->vertices[i][2] = ((vertex >> 22) & 0x3ff) / 4.0;
180 if (self->vertices[i][2] >= 128)
181 self->vertices[i][2] -= 256;
187 private int load_faces(Self *self)
189 struct upkg_file *f = &self->_priv->pkg_file;
190 unsigned char buf[100];
194 if (upkg_export_seek(f, self->tri_base, SEEK_SET) != 0)
197 buflen = upkg_export_read(f, buf, sizeof buf);
198 pos = upkg_decode_index(&n, buf, buflen);
199 g_return_val_if_fail(pos && n == self->tri_num, -1);
201 if (self->tri_num > SIZE_MAX / sizeof self->faces[0]) {
202 u_err(self, "failed to allocate memory");
206 self->faces = malloc(self->tri_num * sizeof self->faces[0]);
208 u_err(self, "failed to allocate memory");
212 for (i = 0; i < n; i++) {
213 struct engine_mesh_face *face = &self->faces[i];
217 if (buflen - pos < 20) {
218 buflen = self_fill_buf(f, buf, pos, buflen);
222 if (buflen - pos < 20)
225 for (j = 0; j < 3; j++) {
226 face->verts[j] = unpack_16_le(buf + pos + 2*j);
227 if (face->verts[j] >= self->vert_num)
230 face->uv[j][0] = buf[pos + 6 + 2*j] / 255.0;
231 face->uv[j][1] = 1 - buf[pos + 7 + 2*j] / 255.0;
234 face->flags = unpack_32_le(buf+pos+12);
236 tmp = unpack_32_le(buf+pos+16);
237 if (tmp > USHRT_MAX || tmp >= self->num_textures)
246 u_err(self, "failed to read triangle data");
252 interface U:Object:Loadable
253 private int load(U:Object *uo)
255 Self *self = SELF(uo);
257 if (self_load_vertices(self) != 0) {
258 u_err(self, "error loading vertex data");
262 if (self_load_faces(self) != 0) {
263 u_err(self, "error loading face data");
264 free(self->vertices);
271 interface U:Object:Loadable
272 private void unload(U:Object *uo)
274 Self *self = SELF(uo);
275 free(self->vertices);
279 interface U:Object:Exportable
280 private int export_name(U:Object *uo, char *buf, size_t n)
282 return snprintf(buf, n, "%s.obj", uo->pkg_file->name);
285 private int uv_compar(const void *a_, const void *b_)
287 const float *a = a_, *b = b_;
301 interface U:Object:Exportable
302 private int export(U:Object *uo, FILE *f)
304 Self *self = SELF(uo);
308 float (*uv_work)[2] = NULL;
311 long last_texture = -1;
313 if (fprintf(f, "mtllib %s.mtl\n", uo->pkg_file->name) < 0)
316 if (fprintf(f, "# %ld vertices\n", self->vert_num) < 0)
319 for (i = 0; i < self->vert_num; i++) {
320 float *v = self->vertices[i];
322 if (fprintf(f, "v %f %f %f\n", v[0], v[1], v[2]) < 0)
326 /* Optimize UV maps */
327 uv_work = malloc(3 * self->tri_num * sizeof uv_work[0]);
329 u_warn(uo, "failed to allocate memory");
330 uv_num = 3 * self->tri_num;
332 for (i = 0; i < self->tri_num; i++) {
335 for (j = 0; j < 3; j++) {
336 uv_work[3*i+j][0] = self->faces[i].uv[j][0];
337 uv_work[3*i+j][1] = self->faces[i].uv[j][1];
340 qsort(uv_work, 3 * self->tri_num, sizeof uv_work[0],
343 for (i = 0; i < 3 * self->tri_num; i++) {
344 if (i > 0 && !self_uv_compar(uv_work[i-1], uv_work[i]))
347 memmove(uv_work[uv_num], uv_work[i], sizeof uv_work[0]);
352 if (fprintf(f, "# %ld texture coords\n", uv_num) < 0)
355 for (i = 0; i < uv_num; i++) {
361 uv = self->faces[i/3].uv[i%3];
363 if (fprintf(f, "vt %f %f\n", uv[0], uv[1]) < 0)
367 if (fprintf(f, "# %ld triangles\n", self->tri_num) < 0)
370 for (i = 0; i < self->tri_num; i++) {
371 struct engine_mesh_face *face = &self->faces[i];
372 unsigned long uv_idx[3];
375 if (face->texture != last_texture) {
376 UObject *t = U_OBJECT(self->textures[face->texture]);
378 if (t && fprintf(f, "usemtl %s\n", t->pkg_name) < 0)
381 last_texture = face->texture;
384 for (j = 0; j < 3; j++) {
388 result = bsearch(&face->uv[j], uv_work,
389 uv_num, sizeof uv_work[0],
391 g_return_val_if_fail(result, -1);
393 uv_idx[j] = result - &uv_work[0] + 1;
395 uv_idx[j] = 3ul*i+j+1;
399 rc = fprintf(f, "f %lu/%lu %lu/%lu %lu/%lu\n",
400 face->verts[0]+1lu, uv_idx[0],
401 face->verts[1]+1lu, uv_idx[1],
402 face->verts[2]+1lu, uv_idx[2]);
414 deserialize_animations(Self *self, unsigned char *buf, size_t *bufsz)
416 struct upkg_file *f = U_OBJECT(self)->pkg_file;
417 size_t rc, buflen, pos = 0;
418 long i, j, anim_count;
420 buflen = upkg_export_read(f, buf, *bufsz);
421 pos += (rc = upkg_decode_index(&anim_count, buf, buflen));
425 for (i = 0; i < anim_count; i++) {
430 pos += (rc = upkg_decode_index(&x, buf+pos, buflen-pos));
433 s = upkg_get_name(f->pkg, x);
438 pos += (rc = upkg_decode_index(&x, buf+pos, buflen-pos));
442 /* Start/end frame */
448 pos += (rc = upkg_decode_index(&nfuncs, buf+pos, buflen-pos));
452 for (j = 0; j < nfuncs; j++) {
453 if (buflen - pos < 13) {
454 buflen = self_fill_buf(f, buf, pos, buflen);
464 rc = upkg_decode_index(&x, buf+pos, buflen-pos);
475 buflen = self_fill_buf(f, buf, pos, buflen);
480 memmove(buf, buf+pos, buflen-pos);
486 private int deserialize_textures(Self *self, unsigned char *buf, size_t bufsz)
488 struct upkg_file *f = U_OBJECT(self)->pkg_file;
489 size_t rc, buflen, pos = 0;
492 buflen = upkg_export_read(f, buf, bufsz);
493 pos += (rc = upkg_decode_index(&idx, buf, buflen));
494 if (rc == 0 || idx < 0 || idx > SIZE_MAX / sizeof self->textures[0]) {
495 u_warn(self, "invalid texture count");
498 self->num_textures = idx;
500 self->textures = malloc(self->num_textures * sizeof self->textures[0]);
501 if (!self->textures) {
502 u_warn(self, "failed to allocate memory");
506 for (i = 0; i < self->num_textures; i++) {
510 buflen = self_fill_buf(f, buf, pos, buflen);
514 pos += (rc = upkg_decode_index(&idx, buf+pos, buflen-pos));
516 u_warn(self, "invalid texture reference");
517 } else if (idx == 0) {
518 /* Seems these lists start with null references, ignore... */
520 obj = u_object_get_by_link(G_OBJECT(self), idx);
522 u_warn(self, "failed to load texture %ld %ld", idx);
525 self->textures[i] = ENGINE_TEXTURE(obj);
528 if (upkg_export_seek(f, (long)pos - (long)buflen, SEEK_CUR) != 0)
534 override (U:Object) int deserialize(U:Object *uo)
536 Self *self = SELF(uo);
537 struct upkg_file *f = uo->pkg_file;
538 const size_t sphere_sz = 12 + 4 * (f->pkg->version >= 62);
539 size_t buflen, pos = 0;
542 unsigned char buf[50];
546 buflen = upkg_export_read(f, buf, sizeof buf);
549 if (buflen - pos < 25)
553 /* Bounding sphere */
554 if (buflen - pos < sphere_sz)
558 self->vert_base = pos + 4*(f->pkg->version >= 61);
559 if (self_skip_array(uo, &self->vert_num, 4, buf+pos, buflen-pos) != 0) {
560 u_err(uo, "invalid vertex array");
564 self->tri_base = f->offset + 4*(f->pkg->version >= 61);
565 if (self_skip_array(uo, &self->tri_num, 20, NULL, 0) != 0) {
566 u_err(uo, "invalid triangle array");
570 if (self_deserialize_animations(self, buf, &buflen) != 0) {
574 self->conn_base = f->offset - buflen;
575 if (self_skip_array(uo, &self->conn_num, 8, buf, buflen) != 0) {
576 u_err(uo, "invalid connection array");
580 buflen = upkg_export_read(f, buf, sizeof buf);
583 /* Another bounding box? */
584 if (buflen - pos < 25)
588 /* Another bounding sphere? */
589 if (buflen - pos < sphere_sz)
593 self->vlink_base = f->offset - (buflen - pos);
594 if (self_skip_array(uo, &self->vlink_num, 4, buf+pos, buflen-pos)) {
595 u_err(uo, "invalid vertex link array");
599 if (self_deserialize_textures(self, buf, buflen) != 0) {
603 /* More bounding boxes!? */
604 buflen = upkg_export_read(f, buf, 5);
605 pos = upkg_decode_index(&idx, buf, buflen);
606 if (pos == 0 || idx < 0 || idx > LONG_MAX / 25) {
607 u_err(uo, "invalid bounding box array");
611 if (upkg_export_seek(f, (long)pos - 5 + idx * 25, SEEK_CUR) != 0)
614 /* More bounding spheres!? */
615 buflen = upkg_export_read(f, buf, 5);
616 pos = upkg_decode_index(&idx, buf, buflen);
617 if (pos == 0 || idx < 0 || idx > LONG_MAX / 16) {
618 u_err(uo, "invalid bounding sphere array");
622 if (upkg_export_seek(f, (long)pos - 5 + idx * sphere_sz, SEEK_CUR) != 0)
625 self->_priv->pkg_file = *f;