]> git.draconx.ca Git - upkg.git/commitdiff
Initial Engine.Mesh implementation.
authorNick Bowler <nbowler@draconx.ca>
Fri, 31 Jan 2020 18:16:42 +0000 (13:16 -0500)
committerNick Bowler <nbowler@draconx.ca>
Fri, 31 Jan 2020 18:20:16 +0000 (13:20 -0500)
This is very incomplete but let's take a first stab at mesh export.

There are very few objects in UT that actually use this class, most are
Engine.LodMesh which is still to be implemented.

Makefile.am
src/engine/.gitignore
src/engine/engine.c
src/engine/mesh.gob [new file with mode: 0644]
src/uobject/primitives.c [new file with mode: 0644]
src/uobject/uobject.c
src/uobject/uobject.h

index b1330d05cd1dfa0a9e94fbf3173d2271ea070e65..bf4759d26a3aa47d8bbed7f42541a9ecb35a362b 100644 (file)
@@ -47,7 +47,8 @@ uobject_HEADERS = src/uobject/uobject.h src/uobject/exportable.h \
 
 libuobject_la_SOURCES = src/uobject/uobject.c src/uobject/module.c \
        src/uobject/avl.c src/uobject/avl.h src/uobject/package.c \
-       src/uobject/exportable.c src/uobject/loadable.c src/uobject/vfs.c
+       src/uobject/exportable.c src/uobject/loadable.c src/uobject/vfs.c \
+       src/uobject/primitives.c
 libuobject_la_LDFLAGS  = $(AM_LDFLAGS) -export-symbols-regex '^u_'
 libuobject_la_LIBADD   = libgnu.la $(LIBLTDL) $(GLIB_LIBS)
 $(libuobject_la_OBJECTS): $(gnulib_headers)
@@ -58,7 +59,7 @@ $(libuobject_la): $(LTDLDEPS)
 moduleflags = -module -avoid-version -export-symbols-regex _LTX_
 
 engine_GOBS = src/engine/palette.gob src/engine/texture.gob \
-       src/engine/music.gob src/engine/sound.gob
+       src/engine/music.gob src/engine/sound.gob src/engine/mesh.gob
 
 MAINTAINERCLEANFILES += $(engine_GOBS:.gob=.gobstamp) \
        $(engine_GOBS:.gob=.c) $(engine_GOBS:.gob=.h)
index 5b468a1844a4a5461f0088ab6d61b7ea38aeb68e..e870f481d63b710e9778f41bbe6aecbf5952fd51 100644 (file)
@@ -1,4 +1,5 @@
-palette.[ch]
-texture.[ch]
-sound.[ch]
-music.[ch]
+/mesh.[ch]
+/music.[ch]
+/palette.[ch]
+/sound.[ch]
+/texture.[ch]
index 92cc0c3d17277921877de39c8785cd92a4631089..652614a795a4c937b215e8620a75a430eac4e4cd 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  upkg: tool for manipulating Unreal Tournament packages.
- *  Copyright © 2009-2011 Nick Bowler
+ *  Copyright © 2009-2012, 2020 Nick Bowler
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 #include <stdio.h>
 #include <glib-object.h>
 
+#include <engine/mesh.h>
 #include <engine/music.h>
-#include <engine/texture.h>
 #include <engine/palette.h>
 #include <engine/sound.h>
+#include <engine/texture.h>
 
 #define init engine_LTX_init
 #define exit engine_LTX_exit
 
 int init(GTypeModule *m)
 {
+       engine_mesh_register_type(m);
        engine_music_register_type(m);
+       engine_palette_register_type(m);
        engine_sound_register_type(m);
        engine_texture_register_type(m);
-       engine_palette_register_type(m);
        return 0;
 }
 
diff --git a/src/engine/mesh.gob b/src/engine/mesh.gob
new file mode 100644 (file)
index 0000000..ed633a8
--- /dev/null
@@ -0,0 +1,621 @@
+%alltop{
+/*
+ *  upkg: tool for manipulating Unreal Tournament packages.
+ *  Copyright © 2020 Nick Bowler
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+%}
+
+%{
+#include <stdint.h>
+#include <uobject/loadable.h>
+#include <uobject/exportable.h>
+#include "pack.h"
+%}
+
+%h{
+#include <uobject/uobject.h>
+#include <engine/texture.h>
+
+/* Hack to work around broken type parsing in GOB2. */
+typedef float engine_mesh_vertex[3];
+
+struct engine_mesh_face {
+       unsigned long flags;
+       unsigned short verts[3];
+       unsigned short texture;
+       float uv[3][2];
+};
+%}
+
+class Engine:Mesh from U:Object (dynamic)
+       (interface U:Object:Loadable)
+       (interface U:Object:Exportable)
+{
+private struct upkg_file pkg_file;
+
+protected long vert_base;
+protected long vert_num;
+protected long tri_base;
+protected long tri_num;
+protected long conn_base;
+protected long conn_num;
+protected long vlink_base;
+protected long vlink_num;
+
+protected engine_mesh_vertex *vertices;
+protected struct engine_mesh_face *faces;
+
+protected long num_textures = 0;
+protected Engine:Texture **textures destroy {
+       long i;
+       for (i = 0; i < self->num_textures; i++) {
+               if (textures[i])
+                       g_object_unref(textures[i]);
+       }
+       free(textures);
+};
+
+private int skip_array(struct UObject *uo, long *nent, long entsz,
+                       unsigned char *buf, size_t buflen)
+{
+       struct upkg_file *f = uo->pkg_file;
+       unsigned long buf_offset = f->offset - buflen;
+       unsigned char mybuf[9];
+       unsigned long skip;
+       size_t sz, pos = 0;
+
+       if (!buf) {
+               buf_offset = f->offset;
+               buflen = upkg_export_read(f, mybuf, sizeof mybuf);
+               buf = mybuf;
+       }
+
+       if (f->pkg->version >= 62) {
+               if (buflen < 4)
+                       return -1;
+               skip = unpack_32_le(buf);
+               pos += 4;
+       }
+
+       pos += (sz = upkg_decode_index(nent, buf+pos, buflen-pos));
+       if (sz == 0)
+               return -1;
+       if (*nent > SIZE_MAX / entsz) {
+               u_warn(uo, "bogus array size");
+               return -1;
+       }
+       sz = (size_t)*nent * entsz;
+
+       if (f->pkg->version >= 62 && sz != skip - f->base - buf_offset - pos) {
+               u_warn(uo, "array skip does not match array size");
+               return -1;
+       }
+
+       if (upkg_export_seek(f, buf_offset + pos + sz, SEEK_SET) != 0)
+               return -1;
+
+       return 0;
+}
+
+private size_t fill_buf(struct upkg_file *f, unsigned char *buf,
+                        size_t pos, size_t buflen)
+{
+       size_t rc, rem = buflen - pos;
+
+       memmove(buf, buf+pos, rem);
+       rem += upkg_export_read(f, buf+rem, buflen-rem);
+       return rem;
+}
+
+private int load_vertices(Self *self)
+{
+       struct upkg_file *f = &self->_priv->pkg_file;
+       unsigned char buf[100];
+       size_t buflen, pos;
+       long i, n;
+
+       if (upkg_export_seek(f, self->vert_base, SEEK_SET) != 0)
+               return -1;
+
+       buflen = upkg_export_read(f, buf, sizeof buf);
+       pos = upkg_decode_index(&n, buf, buflen);
+       g_return_val_if_fail(pos && n == self->vert_num, -1);
+
+       if (self->vert_num > SIZE_MAX / sizeof self->vertices[0]) {
+               u_err(self, "failed to allocate memory");
+               return -1;
+       }
+
+       self->vertices = malloc(self->vert_num * sizeof self->vertices[0]);
+       if (!self->vertices) {
+               u_err(self, "failed to allocate memory");
+               return -1;
+       }
+
+       for (i = 0; i < n; i++) {
+               unsigned long vertex;
+
+               if (buflen - pos < 4) {
+                       buflen = self_fill_buf(f, buf, pos, buflen);
+                       pos = 0;
+               }
+
+               if (buflen - pos < 4) {
+                       u_err(self, "failed to read vertex data");
+                       free(self->vertices);
+                       return -1;
+               }
+
+               vertex = unpack_32_le(buf+pos); pos += 4;
+               self->vertices[i][0] = (vertex & 0x7ff) / 8.0;
+               if (self->vertices[i][0] >= 128)
+                       self->vertices[i][0] -= 256;
+
+               self->vertices[i][1] = ((vertex >> 11) & 0x7ff) / 8.0;
+               if (self->vertices[i][1] >= 128)
+                       self->vertices[i][1] -= 256;
+
+               self->vertices[i][2] = ((vertex >> 22) & 0x3ff) / 4.0;
+               if (self->vertices[i][2] >= 128)
+                       self->vertices[i][2] -= 256;
+       }
+
+       return 0;
+}
+
+private int load_faces(Self *self)
+{
+       struct upkg_file *f = &self->_priv->pkg_file;
+       unsigned char buf[100];
+       size_t buflen, pos;
+       long i, n;
+
+       if (upkg_export_seek(f, self->tri_base, SEEK_SET) != 0)
+               return -1;
+
+       buflen = upkg_export_read(f, buf, sizeof buf);
+       pos = upkg_decode_index(&n, buf, buflen);
+       g_return_val_if_fail(pos && n == self->tri_num, -1);
+
+       if (self->tri_num > SIZE_MAX / sizeof self->faces[0]) {
+               u_err(self, "failed to allocate memory");
+               return -1;
+       }
+
+       self->faces = malloc(self->tri_num * sizeof self->faces[0]);
+       if (!self->faces) {
+               u_err(self, "failed to allocate memory");
+               return -1;
+       }
+
+       for (i = 0; i < n; i++) {
+               struct engine_mesh_face *face = &self->faces[i];
+               unsigned long tmp;
+               int j;
+
+               if (buflen - pos < 20) {
+                       buflen = self_fill_buf(f, buf, pos, buflen);
+                       pos = 0;
+               }
+
+               if (buflen - pos < 20)
+                       goto read_err;
+
+               for (j = 0; j < 3; j++) {
+                       face->verts[j] = unpack_16_le(buf + pos + 2*j);
+                       if (face->verts[j] >= self->vert_num)
+                               goto read_err;
+
+                       face->uv[j][0] = buf[pos + 6 + 2*j] / 255.0;
+                       face->uv[j][1] = 1 - buf[pos + 7 + 2*j] / 255.0;
+               }
+
+               face->flags = unpack_32_le(buf+pos+12);
+
+               tmp = unpack_32_le(buf+pos+16);
+               if (tmp > USHRT_MAX || tmp >= self->num_textures)
+                       goto read_err;
+               face->texture = tmp;
+
+               pos += 20;
+       }
+
+       return 0;
+read_err:
+       u_err(self, "failed to read triangle data");
+       free(self->faces);
+       return -1;
+}
+
+
+interface U:Object:Loadable
+private int load(U:Object *uo)
+{
+       Self *self = SELF(uo);
+
+       if (self_load_vertices(self) != 0) {
+               u_err(self, "error loading vertex data");
+               return -1;
+       }
+
+       if (self_load_faces(self) != 0) {
+               u_err(self, "error loading face data");
+               free(self->vertices);
+               return -1;
+       }
+
+       return 0;
+}
+
+interface U:Object:Loadable
+private void unload(U:Object *uo)
+{
+       Self *self = SELF(uo);
+       free(self->vertices);
+       free(self->faces);
+}
+
+interface U:Object:Exportable
+private int export_name(U:Object *uo, char *buf, size_t n)
+{
+       return snprintf(buf, n, "%s.obj", uo->pkg_file->name);
+}
+
+private int uv_compar(const void *a_, const void *b_)
+{
+       const float *a = a_, *b = b_;
+
+       if (a[0] < b[0])
+               return -1;
+       if (a[0] > b[0])
+               return 1;
+       if (a[1] < b[1])
+               return -1;
+       if (a[1] > b[1])
+               return 1;
+
+       return 0;
+}
+
+interface U:Object:Exportable
+private int export(U:Object *uo, FILE *f)
+{
+       Self *self = SELF(uo);
+       int rc, ret = -1;
+       long i;
+
+       float (*uv_work)[2] = NULL;
+       long uv_num = 0;
+
+       long last_texture = -1;
+
+       if (fprintf(f, "mtllib %s.mtl\n", uo->pkg_file->name) < 0)
+               goto out;
+
+       if (fprintf(f, "# %ld vertices\n", self->vert_num) < 0)
+               goto out;
+
+       for (i = 0; i < self->vert_num; i++) {
+               float *v = self->vertices[i];
+
+               if (fprintf(f, "v %f %f %f\n", v[0], v[1], v[2]) < 0)
+                       goto out;
+       }
+
+       /* Optimize UV maps */
+       uv_work = malloc(3 * self->tri_num * sizeof uv_work[0]);
+       if (!uv_work) {
+               u_warn(uo, "failed to allocate memory");
+               uv_num = 3 * self->tri_num;
+       } else {
+               for (i = 0; i < self->tri_num; i++) {
+                       int j;
+
+                       for (j = 0; j < 3; j++) {
+                               uv_work[3*i+j][0] = self->faces[i].uv[j][0];
+                               uv_work[3*i+j][1] = self->faces[i].uv[j][1];
+                       }
+               }
+               qsort(uv_work, 3 * self->tri_num, sizeof uv_work[0],
+                     self_uv_compar);
+
+               for (i = 0; i < 3 * self->tri_num; i++) {
+                       if (i > 0 && !self_uv_compar(uv_work[i-1], uv_work[i]))
+                               continue;
+
+                       memmove(uv_work[uv_num], uv_work[i], sizeof uv_work[0]);
+                       uv_num++;
+               }
+       }
+
+       if (fprintf(f, "# %ld texture coords\n", uv_num) < 0)
+               goto out;
+
+       for (i = 0; i < uv_num; i++) {
+               float *uv;
+
+               if (uv_work)
+                       uv = uv_work[i];
+               else
+                       uv = self->faces[i/3].uv[i%3];
+
+               if (fprintf(f, "vt %f %f\n", uv[0], uv[1]) < 0)
+                       goto out;
+       }
+
+       if (fprintf(f, "# %ld triangles\n", self->tri_num) < 0)
+               return -1;
+
+       for (i = 0; i < self->tri_num; i++) {
+               struct engine_mesh_face *face = &self->faces[i];
+               unsigned long uv_idx[3];
+               int j;
+
+               if (face->texture != last_texture) {
+                       UObject *t = U_OBJECT(self->textures[face->texture]);
+
+                       if (t && fprintf(f, "usemtl %s\n", t->pkg_name) < 0)
+                               goto out;
+
+                       last_texture = face->texture;
+               }
+
+               for (j = 0; j < 3; j++) {
+                       if (uv_work) {
+                               float (*result)[2];
+
+                               result = bsearch(&face->uv[j], uv_work,
+                                                uv_num, sizeof uv_work[0],
+                                                self_uv_compar);
+                               g_return_val_if_fail(result, -1);
+
+                               uv_idx[j] = result - &uv_work[0] + 1;
+                       } else {
+                               uv_idx[j] = 3ul*i+j+1;
+                       }
+               }
+
+               rc = fprintf(f, "f %lu/%lu %lu/%lu %lu/%lu\n",
+                               face->verts[0]+1lu, uv_idx[0],
+                               face->verts[1]+1lu, uv_idx[1],
+                               face->verts[2]+1lu, uv_idx[2]);
+               if (rc < 0)
+                       goto out;
+       }
+
+       ret = 0;
+       free(uv_work);
+out:
+       return ret;
+}
+
+private int
+deserialize_animations(Self *self, unsigned char *buf, size_t *bufsz)
+{
+       struct upkg_file *f = U_OBJECT(self)->pkg_file;
+       size_t rc, buflen, pos = 0;
+       long i, j, anim_count;
+
+       buflen = upkg_export_read(f, buf, *bufsz);
+       pos += (rc = upkg_decode_index(&anim_count, buf, buflen));
+       if (rc == 0)
+               return -1;
+
+       for (i = 0; i < anim_count; i++) {
+               long x, nfuncs;
+               const char *s;
+
+               /* Name */
+               pos += (rc = upkg_decode_index(&x, buf+pos, buflen-pos));
+               if (rc == 0)
+                       return -1;
+               s = upkg_get_name(f->pkg, x);
+               if (!s)
+                       return -1;
+
+               /* Group */
+               pos += (rc = upkg_decode_index(&x, buf+pos, buflen-pos));
+               if (rc == 0)
+                       return -1;
+
+               /* Start/end frame */
+               if (buflen-pos < 8)
+                       return -1;
+               pos += 8;
+
+               /* Function count */
+               pos += (rc = upkg_decode_index(&nfuncs, buf+pos, buflen-pos));
+               if (rc == 0)
+                       return -1;
+
+               for (j = 0; j < nfuncs; j++) {
+                       if (buflen - pos < 13) {
+                               buflen = self_fill_buf(f, buf, pos, buflen);
+                               pos = 0;
+                       }
+
+                       /* time */
+                       if (buflen-pos < 4)
+                               return -1;
+                       pos += 4;
+
+                       /* function */
+                       rc = upkg_decode_index(&x, buf+pos, buflen-pos);
+                       if (rc == 0)
+                               return -1;
+                       pos += rc;
+               }
+
+               /* rate */
+               if (buflen-pos < 4)
+                       return -1;
+               pos += 4;
+
+               buflen = self_fill_buf(f, buf, pos, buflen);
+               pos = 0;
+       }
+
+       if (pos != 0)
+               memmove(buf, buf+pos, buflen-pos);
+       *bufsz = buflen-pos;
+
+       return 0;
+}
+
+private int deserialize_textures(Self *self, unsigned char *buf, size_t bufsz)
+{
+       struct upkg_file *f = U_OBJECT(self)->pkg_file;
+       size_t rc, buflen, pos = 0;
+       long i, idx;
+
+       buflen = upkg_export_read(f, buf, bufsz);
+       pos += (rc = upkg_decode_index(&idx, buf, buflen));
+       if (rc == 0 || idx < 0 || idx > SIZE_MAX / sizeof self->textures[0]) {
+               u_warn(self, "invalid texture count");
+               return -1;
+       }
+       self->num_textures = idx;
+
+       self->textures = malloc(self->num_textures * sizeof self->textures[0]);
+       if (!self->textures) {
+               u_warn(self, "failed to allocate memory");
+               return -1;
+       }
+
+       for (i = 0; i < self->num_textures; i++) {
+               GObject *obj = NULL;
+
+               if (buflen < 5) {
+                       buflen = self_fill_buf(f, buf, pos, buflen);
+                       pos = 0;
+               }
+
+               pos += (rc = upkg_decode_index(&idx, buf+pos, buflen-pos));
+               if (rc == 0) {
+                       u_warn(self, "invalid texture reference");
+               } else if (idx == 0) {
+                       /* Seems these lists start with null references, ignore... */
+               } else {
+                       obj = u_object_get_by_link(G_OBJECT(self), idx);
+                       if (!obj) {
+                               u_warn(self, "failed to load texture %ld %ld", idx);
+                       }
+               }
+               self->textures[i] = ENGINE_TEXTURE(obj);
+       }
+
+       if (upkg_export_seek(f, (long)pos - (long)buflen, SEEK_CUR) != 0)
+               return -1;
+
+       return 0;
+}
+
+override (U:Object) int deserialize(U:Object *uo)
+{
+       Self *self = SELF(uo);
+       struct upkg_file *f = uo->pkg_file;
+       const size_t sphere_sz = 12 + 4 * (f->pkg->version >= 62);
+       size_t buflen, pos = 0;
+       long idx;
+
+       unsigned char buf[50];
+
+       PARENT_HANDLER(uo);
+
+       buflen = upkg_export_read(f, buf, sizeof buf);
+
+       /* Bounding box */
+       if (buflen - pos < 25)
+               return -1;
+       pos += 25;
+
+       /* Bounding sphere */
+       if (buflen - pos < sphere_sz)
+               return -1;
+       pos += sphere_sz;
+
+       self->vert_base = pos + 4*(f->pkg->version >= 61);
+       if (self_skip_array(uo, &self->vert_num, 4, buf+pos, buflen-pos) != 0) {
+               u_err(uo, "invalid vertex array");
+               return -1;
+       }
+
+       self->tri_base = f->offset + 4*(f->pkg->version >= 61);
+       if (self_skip_array(uo, &self->tri_num, 20, NULL, 0) != 0) {
+               u_err(uo, "invalid triangle array");
+               return -1;
+       }
+
+       if (self_deserialize_animations(self, buf, &buflen) != 0) {
+               return -1;
+       }
+
+       self->conn_base = f->offset - buflen;
+       if (self_skip_array(uo, &self->conn_num, 8, buf, buflen) != 0) {
+               u_err(uo, "invalid connection array");
+               return -1;
+       }
+
+       buflen = upkg_export_read(f, buf, sizeof buf);
+       pos = 0;
+
+       /* Another bounding box? */
+       if (buflen - pos < 25)
+               return -1;
+       pos += 25;
+
+       /* Another bounding sphere? */
+       if (buflen - pos < sphere_sz)
+               return -1;
+       pos += sphere_sz;
+
+       self->vlink_base = f->offset - (buflen - pos);
+       if (self_skip_array(uo, &self->vlink_num, 4, buf+pos, buflen-pos)) {
+               u_err(uo, "invalid vertex link array");
+               return -1;
+       }
+
+       if (self_deserialize_textures(self, buf, buflen) != 0) {
+               return -1;
+       }
+
+       /* More bounding boxes!? */
+       buflen = upkg_export_read(f, buf, 5);
+       pos = upkg_decode_index(&idx, buf, buflen);
+       if (pos == 0 || idx < 0 || idx > LONG_MAX / 25) {
+               u_err(uo, "invalid bounding box array");
+               return -1;
+       }
+
+       if (upkg_export_seek(f, (long)pos - 5 + idx * 25, SEEK_CUR) != 0)
+               return -1;
+
+       /* More bounding spheres!? */
+       buflen = upkg_export_read(f, buf, 5);
+       pos = upkg_decode_index(&idx, buf, buflen);
+       if (pos == 0 || idx < 0 || idx > LONG_MAX / 16) {
+               u_err(uo, "invalid bounding sphere array");
+               return -1;
+       }
+
+       if (upkg_export_seek(f, (long)pos - 5 + idx * sphere_sz, SEEK_CUR) != 0)
+               return -1;
+
+       self->_priv->pkg_file = *f;
+       return 0;
+}
+
+}
diff --git a/src/uobject/primitives.c b/src/uobject/primitives.c
new file mode 100644 (file)
index 0000000..b907348
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  upkg: tool for manipulating Unreal Tournament packages.
+ *  Copyright © 2012, 2020 Nick Bowler
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <math.h>
+
+#include <uobject/uobject.h>
+#include "pack.h"
+
+/*
+ * Deserialize an IEEE 754 binary32 value in "little endian" (for whatever
+ * that term is worth in this context).  That is, when interpreted as a little
+ * endian 32-bit unsigned integer: bit 31 is the sign, bits 30-23 are the
+ * (biased) exponent, and bits 22-0 are the encoded part of the significand.
+ *
+ * The implementation is designed to be agnostic of the platform's actual
+ * float type, but the conversion may be lossy if "float" is not itself a
+ * binary32 format.  NaN payloads are not preserved.
+ */
+float u_unpack_binary32_le(const unsigned char *buf)
+{
+       unsigned long raw;
+       long significand;
+       int exponent;
+       float result;
+
+       raw = unpack_32_le(buf);
+       exponent = (raw & 0x7f800000) >> 23;
+       significand = (raw & 0x007fffff) >> 0;
+
+       switch (exponent) {
+       case 255:
+               result = significand ? NAN : INFINITY;
+               break;
+       default:
+               significand |= 0x00800000;
+               /* fall through */
+       case 0:
+               result = ldexpf(significand, exponent-126-24);
+       }
+
+       return copysignf(result, raw & 0x80000000 ? -1 : 1);
+}
index 867390f2d7c07ced6bba9de4a3f20c0464d2b869..3bada241cf9e7ed06a4c71c76dff59685fc7a275 100644 (file)
@@ -279,6 +279,21 @@ out:
        return obj;
 }
 
+GObject *u_object_get_by_link(GObject *go, long index)
+{
+       g_return_val_if_fail(IS_U_OBJECT(go), NULL);
+       UObject *uo = U_OBJECT(go);
+
+       if (index == 0)
+               return NULL;
+
+       if (index < 0) {
+               return get_import_object(uo, -(index+1));
+       }
+
+       return u_object_new_from_package(uo->pkg, index-1);
+}
+
 static int decode_object_property(UObject *uo, GValue *val, unsigned long len)
 {
        struct u_object_priv *priv = U_OBJECT_GET_PRIV(uo);
@@ -287,55 +302,15 @@ static int decode_object_property(UObject *uo, GValue *val, unsigned long len)
        int rc;
 
        rc = upkg_decode_index(&index, priv->buf+len, priv->nbuf-len);
-       if (rc == 0 || index == 0)
+       if (rc == 0)
                return -1;
 
-       if (index < 0) {
-               obj = get_import_object(uo, -(index+1));
-       } else {
-               obj = u_object_new_from_package(uo->pkg, index-1);
-       }
-
+       obj = u_object_get_by_link(G_OBJECT(uo), index);
        g_value_init(val, U_TYPE_OBJECT);
        g_value_take_object(val, obj);
        return 0;
 }
 
-/*
- * Deserialize an IEEE 754 binary32 value in "little endian" (for whatever
- * that term is worth in this context).  That is, when interpreted as a little
- * endian 32-bit unsigned integer: bit 31 is the sign, bits 30-23 are the
- * (biased) exponent, and bits 22-0 are the encoded part of the significand.
- *
- * The implementation is designed to be agnostic of the platform's actual
- * float type, but the conversion may be lossy if "float" is not itself a
- * binary32 format.  NaN payloads are not preserved.
- */
-static float unpack_binary32_le(const unsigned char *buf)
-{
-       unsigned long raw;
-       long significand;
-       int exponent;
-       float result;
-
-       raw = unpack_32_le(buf);
-       exponent = (raw & 0x7f800000) >> 23;
-       significand = (raw & 0x007fffff) >> 0;
-
-       switch (exponent) {
-       case 255:
-               result = significand ? NAN : INFINITY;
-               break;
-       default:
-               significand |= 0x00800000;
-               /* fall through */
-       case 0:
-               result = ldexpf(significand, exponent-126-24);
-       }
-
-       return copysignf(result, raw & 0x80000000 ? -1 : 1);
-}
-
 static unsigned long deserialize_property(UObject *uo, struct prop_head *head)
 {
        struct u_object_priv *priv = U_OBJECT_GET_PRIV(uo);
@@ -387,7 +362,7 @@ static unsigned long deserialize_property(UObject *uo, struct prop_head *head)
                if (head->size != 4 || priv->nbuf-len < head->size)
                        return 0;
                g_value_init(&val, G_TYPE_FLOAT);
-               g_value_set_float(&val, unpack_binary32_le(priv->buf+len));
+               g_value_set_float(&val, u_unpack_binary32_le(priv->buf+len));
                g_object_set_property(G_OBJECT(uo), head->prop_name, &val);
                break;
        default:
index 00f742bd1d0b23a9696114d12cc73a8006aff911..434780017a66acd3bafaaaf4790ba20888f641b7 100644 (file)
@@ -56,8 +56,13 @@ GType u_object_get_type(void);
 
 int u_object_deserialize(GObject *obj, GTypeModule *pkg, unsigned long idx);
 
+float u_unpack_binary32_le(const unsigned char *buf);
+
 GObject *u_object_new_from_package(GTypeModule *pkg, unsigned long idx);
 
+/* Resolve an object reference from one object to another. */
+GObject *u_object_get_by_link(GObject *obj, long link);
+
 /* Logging helpers for UObject class implementations. */
 void u_vlog_full(GObject *o, GLogLevelFlags level, const char *fmt, va_list ap);
 void u_log_full(GObject *o, GLogLevelFlags level, const char *fmt, ...);