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