]> git.draconx.ca Git - upkg.git/blob - src/engine/mesh.gob
Disable deprecation warnings from Glib.
[upkg.git] / src / engine / mesh.gob
1 %alltop{
2 /*
3  * upkg: tool for manipulating Unreal Tournament packages.
4  * Copyright © 2020, 2022 Nick Bowler
5  *
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.
10  *
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.
15  *
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/>.
18  */
19 %}
20
21 %ctop{
22 #include <config.h>
23 %}
24
25 %{
26 #include <stdint.h>
27 #include <uobject/loadable.h>
28 #include <uobject/exportable.h>
29 #include "pack.h"
30 %}
31
32 %h{
33 #include <uobject/uobject.h>
34 #include <engine/texture.h>
35
36 /* Hack to work around broken type parsing in GOB2. */
37 typedef float engine_mesh_vertex[3];
38
39 struct engine_mesh_face {
40         unsigned long flags;
41         unsigned short verts[3];
42         unsigned short texture;
43         float uv[3][2];
44 };
45 %}
46
47 class Engine:Mesh from U:Object (dynamic)
48         (interface U:Object:Loadable)
49         (interface U:Object:Exportable)
50 {
51 private struct upkg_file pkg_file;
52
53 protected long vert_base;
54 protected long vert_num;
55 protected long tri_base;
56 protected long tri_num;
57 protected long conn_base;
58 protected long conn_num;
59 protected long vlink_base;
60 protected long vlink_num;
61
62 protected engine_mesh_vertex *vertices;
63 protected struct engine_mesh_face *faces;
64
65 protected long num_textures = 0;
66 protected Engine:Texture **textures destroy {
67         long i;
68         for (i = 0; i < self->num_textures; i++) {
69                 if (textures[i])
70                         g_object_unref(textures[i]);
71         }
72         free(textures);
73 };
74
75 private int skip_array(struct UObject *uo, long *nent, long entsz,
76                        unsigned char *buf, size_t buflen)
77 {
78         struct upkg_file *f = uo->pkg_file;
79         unsigned long buf_offset = f->offset - buflen;
80         unsigned char mybuf[9];
81         unsigned long skip;
82         size_t sz, pos = 0;
83
84         if (!buf) {
85                 buf_offset = f->offset;
86                 buflen = upkg_export_read(f, mybuf, sizeof mybuf);
87                 buf = mybuf;
88         }
89
90         if (f->pkg->version >= 62) {
91                 if (buflen < 4)
92                         return -1;
93                 skip = unpack_32_le(buf);
94                 pos += 4;
95         }
96
97         pos += (sz = upkg_decode_index(nent, buf+pos, buflen-pos));
98         if (sz == 0)
99                 return -1;
100         if (*nent > SIZE_MAX / entsz) {
101                 u_warn(uo, "bogus array size");
102                 return -1;
103         }
104         sz = (size_t)*nent * entsz;
105
106         if (f->pkg->version >= 62 && sz != skip - f->base - buf_offset - pos) {
107                 u_warn(uo, "array skip does not match array size");
108                 return -1;
109         }
110
111         if (upkg_export_seek(f, buf_offset + pos + sz, SEEK_SET) != 0)
112                 return -1;
113
114         return 0;
115 }
116
117 private size_t fill_buf(struct upkg_file *f, unsigned char *buf,
118                         size_t pos, size_t buflen)
119 {
120         size_t rc, rem = buflen - pos;
121
122         memmove(buf, buf+pos, rem);
123         rem += upkg_export_read(f, buf+rem, buflen-rem);
124         return rem;
125 }
126
127 private int load_vertices(Self *self)
128 {
129         struct upkg_file *f = &self->_priv->pkg_file;
130         unsigned char buf[100];
131         size_t buflen, pos;
132         long i, n;
133
134         if (upkg_export_seek(f, self->vert_base, SEEK_SET) != 0)
135                 return -1;
136
137         buflen = upkg_export_read(f, buf, sizeof buf);
138         pos = upkg_decode_index(&n, buf, buflen);
139         g_return_val_if_fail(pos && n == self->vert_num, -1);
140
141         if (self->vert_num > SIZE_MAX / sizeof self->vertices[0]) {
142                 u_err(self, "failed to allocate memory");
143                 return -1;
144         }
145
146         self->vertices = malloc(self->vert_num * sizeof self->vertices[0]);
147         if (!self->vertices) {
148                 u_err(self, "failed to allocate memory");
149                 return -1;
150         }
151
152         for (i = 0; i < n; i++) {
153                 unsigned long vertex;
154
155                 if (buflen - pos < 4) {
156                         buflen = self_fill_buf(f, buf, pos, buflen);
157                         pos = 0;
158                 }
159
160                 if (buflen - pos < 4) {
161                         u_err(self, "failed to read vertex data");
162                         free(self->vertices);
163                         return -1;
164                 }
165
166                 vertex = unpack_32_le(buf+pos); pos += 4;
167                 self->vertices[i][0] = (vertex & 0x7ff) / 8.0;
168                 if (self->vertices[i][0] >= 128)
169                         self->vertices[i][0] -= 256;
170
171                 self->vertices[i][1] = ((vertex >> 11) & 0x7ff) / 8.0;
172                 if (self->vertices[i][1] >= 128)
173                         self->vertices[i][1] -= 256;
174
175                 self->vertices[i][2] = ((vertex >> 22) & 0x3ff) / 4.0;
176                 if (self->vertices[i][2] >= 128)
177                         self->vertices[i][2] -= 256;
178         }
179
180         return 0;
181 }
182
183 private int load_faces(Self *self)
184 {
185         struct upkg_file *f = &self->_priv->pkg_file;
186         unsigned char buf[100];
187         size_t buflen, pos;
188         long i, n;
189
190         if (upkg_export_seek(f, self->tri_base, SEEK_SET) != 0)
191                 return -1;
192
193         buflen = upkg_export_read(f, buf, sizeof buf);
194         pos = upkg_decode_index(&n, buf, buflen);
195         g_return_val_if_fail(pos && n == self->tri_num, -1);
196
197         if (self->tri_num > SIZE_MAX / sizeof self->faces[0]) {
198                 u_err(self, "failed to allocate memory");
199                 return -1;
200         }
201
202         self->faces = malloc(self->tri_num * sizeof self->faces[0]);
203         if (!self->faces) {
204                 u_err(self, "failed to allocate memory");
205                 return -1;
206         }
207
208         for (i = 0; i < n; i++) {
209                 struct engine_mesh_face *face = &self->faces[i];
210                 unsigned long tmp;
211                 int j;
212
213                 if (buflen - pos < 20) {
214                         buflen = self_fill_buf(f, buf, pos, buflen);
215                         pos = 0;
216                 }
217
218                 if (buflen - pos < 20)
219                         goto read_err;
220
221                 for (j = 0; j < 3; j++) {
222                         face->verts[j] = unpack_16_le(buf + pos + 2*j);
223                         if (face->verts[j] >= self->vert_num)
224                                 goto read_err;
225
226                         face->uv[j][0] = buf[pos + 6 + 2*j] / 255.0;
227                         face->uv[j][1] = 1 - buf[pos + 7 + 2*j] / 255.0;
228                 }
229
230                 face->flags = unpack_32_le(buf+pos+12);
231
232                 tmp = unpack_32_le(buf+pos+16);
233                 if (tmp > USHRT_MAX || tmp >= self->num_textures)
234                         goto read_err;
235                 face->texture = tmp;
236
237                 pos += 20;
238         }
239
240         return 0;
241 read_err:
242         u_err(self, "failed to read triangle data");
243         free(self->faces);
244         return -1;
245 }
246
247
248 interface U:Object:Loadable
249 private int load(U:Object *uo)
250 {
251         Self *self = SELF(uo);
252
253         if (self_load_vertices(self) != 0) {
254                 u_err(self, "error loading vertex data");
255                 return -1;
256         }
257
258         if (self_load_faces(self) != 0) {
259                 u_err(self, "error loading face data");
260                 free(self->vertices);
261                 return -1;
262         }
263
264         return 0;
265 }
266
267 interface U:Object:Loadable
268 private void unload(U:Object *uo)
269 {
270         Self *self = SELF(uo);
271         free(self->vertices);
272         free(self->faces);
273 }
274
275 interface U:Object:Exportable
276 private int export_name(U:Object *uo, char *buf, size_t n)
277 {
278         return snprintf(buf, n, "%s.obj", uo->pkg_file->name);
279 }
280
281 private int uv_compar(const void *a_, const void *b_)
282 {
283         const float *a = a_, *b = b_;
284
285         if (a[0] < b[0])
286                 return -1;
287         if (a[0] > b[0])
288                 return 1;
289         if (a[1] < b[1])
290                 return -1;
291         if (a[1] > b[1])
292                 return 1;
293
294         return 0;
295 }
296
297 interface U:Object:Exportable
298 private int export(U:Object *uo, FILE *f)
299 {
300         Self *self = SELF(uo);
301         int rc, ret = -1;
302         long i;
303
304         float (*uv_work)[2] = NULL;
305         long uv_num = 0;
306
307         long last_texture = -1;
308
309         if (fprintf(f, "mtllib %s.mtl\n", uo->pkg_file->name) < 0)
310                 goto out;
311
312         if (fprintf(f, "# %ld vertices\n", self->vert_num) < 0)
313                 goto out;
314
315         for (i = 0; i < self->vert_num; i++) {
316                 float *v = self->vertices[i];
317
318                 if (fprintf(f, "v %f %f %f\n", v[0], v[1], v[2]) < 0)
319                         goto out;
320         }
321
322         /* Optimize UV maps */
323         uv_work = malloc(3 * self->tri_num * sizeof uv_work[0]);
324         if (!uv_work) {
325                 u_warn(uo, "failed to allocate memory");
326                 uv_num = 3 * self->tri_num;
327         } else {
328                 for (i = 0; i < self->tri_num; i++) {
329                         int j;
330
331                         for (j = 0; j < 3; j++) {
332                                 uv_work[3*i+j][0] = self->faces[i].uv[j][0];
333                                 uv_work[3*i+j][1] = self->faces[i].uv[j][1];
334                         }
335                 }
336                 qsort(uv_work, 3 * self->tri_num, sizeof uv_work[0],
337                       self_uv_compar);
338
339                 for (i = 0; i < 3 * self->tri_num; i++) {
340                         if (i > 0 && !self_uv_compar(uv_work[i-1], uv_work[i]))
341                                 continue;
342
343                         memmove(uv_work[uv_num], uv_work[i], sizeof uv_work[0]);
344                         uv_num++;
345                 }
346         }
347
348         if (fprintf(f, "# %ld texture coords\n", uv_num) < 0)
349                 goto out;
350
351         for (i = 0; i < uv_num; i++) {
352                 float *uv;
353
354                 if (uv_work)
355                         uv = uv_work[i];
356                 else
357                         uv = self->faces[i/3].uv[i%3];
358
359                 if (fprintf(f, "vt %f %f\n", uv[0], uv[1]) < 0)
360                         goto out;
361         }
362
363         if (fprintf(f, "# %ld triangles\n", self->tri_num) < 0)
364                 return -1;
365
366         for (i = 0; i < self->tri_num; i++) {
367                 struct engine_mesh_face *face = &self->faces[i];
368                 unsigned long uv_idx[3];
369                 int j;
370
371                 if (face->texture != last_texture) {
372                         UObject *t = U_OBJECT(self->textures[face->texture]);
373
374                         if (t && fprintf(f, "usemtl %s\n", t->pkg_name) < 0)
375                                 goto out;
376
377                         last_texture = face->texture;
378                 }
379
380                 for (j = 0; j < 3; j++) {
381                         if (uv_work) {
382                                 float (*result)[2];
383
384                                 result = bsearch(&face->uv[j], uv_work,
385                                                  uv_num, sizeof uv_work[0],
386                                                  self_uv_compar);
387                                 g_return_val_if_fail(result, -1);
388
389                                 uv_idx[j] = result - &uv_work[0] + 1;
390                         } else {
391                                 uv_idx[j] = 3ul*i+j+1;
392                         }
393                 }
394
395                 rc = fprintf(f, "f %lu/%lu %lu/%lu %lu/%lu\n",
396                                 face->verts[0]+1lu, uv_idx[0],
397                                 face->verts[1]+1lu, uv_idx[1],
398                                 face->verts[2]+1lu, uv_idx[2]);
399                 if (rc < 0)
400                         goto out;
401         }
402
403         ret = 0;
404         free(uv_work);
405 out:
406         return ret;
407 }
408
409 private int
410 deserialize_animations(Self *self, unsigned char *buf, size_t *bufsz)
411 {
412         struct upkg_file *f = U_OBJECT(self)->pkg_file;
413         size_t rc, buflen, pos = 0;
414         long i, j, anim_count;
415
416         buflen = upkg_export_read(f, buf, *bufsz);
417         pos += (rc = upkg_decode_index(&anim_count, buf, buflen));
418         if (rc == 0)
419                 return -1;
420
421         for (i = 0; i < anim_count; i++) {
422                 long x, nfuncs;
423                 const char *s;
424
425                 /* Name */
426                 pos += (rc = upkg_decode_index(&x, buf+pos, buflen-pos));
427                 if (rc == 0)
428                         return -1;
429                 s = upkg_get_name(f->pkg, x);
430                 if (!s)
431                         return -1;
432
433                 /* Group */
434                 pos += (rc = upkg_decode_index(&x, buf+pos, buflen-pos));
435                 if (rc == 0)
436                         return -1;
437
438                 /* Start/end frame */
439                 if (buflen-pos < 8)
440                         return -1;
441                 pos += 8;
442
443                 /* Function count */
444                 pos += (rc = upkg_decode_index(&nfuncs, buf+pos, buflen-pos));
445                 if (rc == 0)
446                         return -1;
447
448                 for (j = 0; j < nfuncs; j++) {
449                         if (buflen - pos < 13) {
450                                 buflen = self_fill_buf(f, buf, pos, buflen);
451                                 pos = 0;
452                         }
453
454                         /* time */
455                         if (buflen-pos < 4)
456                                 return -1;
457                         pos += 4;
458
459                         /* function */
460                         rc = upkg_decode_index(&x, buf+pos, buflen-pos);
461                         if (rc == 0)
462                                 return -1;
463                         pos += rc;
464                 }
465
466                 /* rate */
467                 if (buflen-pos < 4)
468                         return -1;
469                 pos += 4;
470
471                 buflen = self_fill_buf(f, buf, pos, buflen);
472                 pos = 0;
473         }
474
475         if (pos != 0)
476                 memmove(buf, buf+pos, buflen-pos);
477         *bufsz = buflen-pos;
478
479         return 0;
480 }
481
482 private int deserialize_textures(Self *self, unsigned char *buf, size_t bufsz)
483 {
484         struct upkg_file *f = U_OBJECT(self)->pkg_file;
485         size_t rc, buflen, pos = 0;
486         long i, idx;
487
488         buflen = upkg_export_read(f, buf, bufsz);
489         pos += (rc = upkg_decode_index(&idx, buf, buflen));
490         if (rc == 0 || idx < 0 || idx > SIZE_MAX / sizeof self->textures[0]) {
491                 u_warn(self, "invalid texture count");
492                 return -1;
493         }
494         self->num_textures = idx;
495
496         self->textures = malloc(self->num_textures * sizeof self->textures[0]);
497         if (!self->textures) {
498                 u_warn(self, "failed to allocate memory");
499                 return -1;
500         }
501
502         for (i = 0; i < self->num_textures; i++) {
503                 GObject *obj = NULL;
504
505                 if (buflen < 5) {
506                         buflen = self_fill_buf(f, buf, pos, buflen);
507                         pos = 0;
508                 }
509
510                 pos += (rc = upkg_decode_index(&idx, buf+pos, buflen-pos));
511                 if (rc == 0) {
512                         u_warn(self, "invalid texture reference");
513                 } else if (idx == 0) {
514                         /* Seems these lists start with null references, ignore... */
515                 } else {
516                         obj = u_object_get_by_link(G_OBJECT(self), idx);
517                         if (!obj) {
518                                 u_warn(self, "failed to load texture %ld %ld", idx);
519                         }
520                 }
521                 self->textures[i] = ENGINE_TEXTURE(obj);
522         }
523
524         if (upkg_export_seek(f, (long)pos - (long)buflen, SEEK_CUR) != 0)
525                 return -1;
526
527         return 0;
528 }
529
530 override (U:Object) int deserialize(U:Object *uo)
531 {
532         Self *self = SELF(uo);
533         struct upkg_file *f = uo->pkg_file;
534         const size_t sphere_sz = 12 + 4 * (f->pkg->version >= 62);
535         size_t buflen, pos = 0;
536         long idx;
537
538         unsigned char buf[50];
539
540         PARENT_HANDLER(uo);
541
542         buflen = upkg_export_read(f, buf, sizeof buf);
543
544         /* Bounding box */
545         if (buflen - pos < 25)
546                 return -1;
547         pos += 25;
548
549         /* Bounding sphere */
550         if (buflen - pos < sphere_sz)
551                 return -1;
552         pos += sphere_sz;
553
554         self->vert_base = pos + 4*(f->pkg->version >= 61);
555         if (self_skip_array(uo, &self->vert_num, 4, buf+pos, buflen-pos) != 0) {
556                 u_err(uo, "invalid vertex array");
557                 return -1;
558         }
559
560         self->tri_base = f->offset + 4*(f->pkg->version >= 61);
561         if (self_skip_array(uo, &self->tri_num, 20, NULL, 0) != 0) {
562                 u_err(uo, "invalid triangle array");
563                 return -1;
564         }
565
566         if (self_deserialize_animations(self, buf, &buflen) != 0) {
567                 return -1;
568         }
569
570         self->conn_base = f->offset - buflen;
571         if (self_skip_array(uo, &self->conn_num, 8, buf, buflen) != 0) {
572                 u_err(uo, "invalid connection array");
573                 return -1;
574         }
575
576         buflen = upkg_export_read(f, buf, sizeof buf);
577         pos = 0;
578
579         /* Another bounding box? */
580         if (buflen - pos < 25)
581                 return -1;
582         pos += 25;
583
584         /* Another bounding sphere? */
585         if (buflen - pos < sphere_sz)
586                 return -1;
587         pos += sphere_sz;
588
589         self->vlink_base = f->offset - (buflen - pos);
590         if (self_skip_array(uo, &self->vlink_num, 4, buf+pos, buflen-pos)) {
591                 u_err(uo, "invalid vertex link array");
592                 return -1;
593         }
594
595         if (self_deserialize_textures(self, buf, buflen) != 0) {
596                 return -1;
597         }
598
599         /* More bounding boxes!? */
600         buflen = upkg_export_read(f, buf, 5);
601         pos = upkg_decode_index(&idx, buf, buflen);
602         if (pos == 0 || idx < 0 || idx > LONG_MAX / 25) {
603                 u_err(uo, "invalid bounding box array");
604                 return -1;
605         }
606
607         if (upkg_export_seek(f, (long)pos - 5 + idx * 25, SEEK_CUR) != 0)
608                 return -1;
609
610         /* More bounding spheres!? */
611         buflen = upkg_export_read(f, buf, 5);
612         pos = upkg_decode_index(&idx, buf, buflen);
613         if (pos == 0 || idx < 0 || idx > LONG_MAX / 16) {
614                 u_err(uo, "invalid bounding sphere array");
615                 return -1;
616         }
617
618         if (upkg_export_seek(f, (long)pos - 5 + idx * sphere_sz, SEEK_CUR) != 0)
619                 return -1;
620
621         self->_priv->pkg_file = *f;
622         return 0;
623 }
624
625 }