From 67b795eeb9c9515adb9450562a3e7e15eb63a21d Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Fri, 31 Jan 2020 13:16:42 -0500 Subject: [PATCH] Initial Engine.Mesh implementation. 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 | 5 +- src/engine/.gitignore | 9 +- src/engine/engine.c | 8 +- src/engine/mesh.gob | 621 +++++++++++++++++++++++++++++++++++++++ src/uobject/primitives.c | 58 ++++ src/uobject/uobject.c | 61 ++-- src/uobject/uobject.h | 5 + 7 files changed, 715 insertions(+), 52 deletions(-) create mode 100644 src/engine/mesh.gob create mode 100644 src/uobject/primitives.c diff --git a/Makefile.am b/Makefile.am index b1330d0..bf4759d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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) diff --git a/src/engine/.gitignore b/src/engine/.gitignore index 5b468a1..e870f48 100644 --- a/src/engine/.gitignore +++ b/src/engine/.gitignore @@ -1,4 +1,5 @@ -palette.[ch] -texture.[ch] -sound.[ch] -music.[ch] +/mesh.[ch] +/music.[ch] +/palette.[ch] +/sound.[ch] +/texture.[ch] diff --git a/src/engine/engine.c b/src/engine/engine.c index 92cc0c3..652614a 100644 --- a/src/engine/engine.c +++ b/src/engine/engine.c @@ -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 @@ -19,20 +19,22 @@ #include #include +#include #include -#include #include #include +#include #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 index 0000000..ed633a8 --- /dev/null +++ b/src/engine/mesh.gob @@ -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 . + */ +%} + +%{ +#include +#include +#include +#include "pack.h" +%} + +%h{ +#include +#include + +/* 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 index 0000000..b907348 --- /dev/null +++ b/src/uobject/primitives.c @@ -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 . + */ + +#include +#include + +#include +#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); +} diff --git a/src/uobject/uobject.c b/src/uobject/uobject.c index 867390f..3bada24 100644 --- a/src/uobject/uobject.c +++ b/src/uobject/uobject.c @@ -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: diff --git a/src/uobject/uobject.h b/src/uobject/uobject.h index 00f742b..4347800 100644 --- a/src/uobject/uobject.h +++ b/src/uobject/uobject.h @@ -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, ...); -- 2.43.2