]> git.draconx.ca Git - upkg.git/blobdiff - src/engine/texture.gob
texture: Add initial support for texture exports.
[upkg.git] / src / engine / texture.gob
index 35dd739a439210762256419b70b319610ae4f989..3db176e045982b31de94268bed9031a7f63cf889 100644 (file)
 
 %{
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <uobject/loadable.h>
+#include <uobject/exportable.h>
 #include <engine/palette.h>
+#include "pack.h"
+#include "pcx.h"
 %}
 
 %h{
 #include <uobject/uobject.h>
+
+struct engine_texture_data {
+       unsigned long width, height;
+
+       unsigned long datalen;
+       unsigned char data[];
+};
+
+%}
+
+%{
+struct engine_texture_data *decode_mipmap(UObject *uo)
+{
+       struct upkg_file *f = uo->pkg_file;
+       struct engine_texture_data *data;
+       size_t rc, pos = 0, buflen;
+       unsigned long end_offset;
+       unsigned char buf[32];
+       long datalen;
+
+       buflen = upkg_export_read(f, buf, sizeof buf);
+       if (uo->pkg->version >= 63) {
+               /*
+                * There's an offset to the end of the image data here; not
+                * clear why it's useful since it's implied by the very next
+                * field anyway.  However, we will nevertheless validate this
+                * property below.
+                */
+               if (pos + 4 > buflen)
+                       return NULL;
+               end_offset = unpack_32_le(buf);
+               pos += 4;
+       }
+
+       rc = upkg_decode_index(&datalen, buf+pos, buflen-pos);
+       if (rc == 0 || datalen < 0)
+               return NULL;
+       pos += rc;
+
+       data = malloc(sizeof *data + datalen);
+       if (!data)
+               return NULL;
+       data->datalen = datalen;
+
+       /* Rewind to the start of the image data, and slurp it in. */
+       if (upkg_export_seek(f, -(long)(buflen-pos), SEEK_CUR) != 0)
+               goto err_free;
+       rc = upkg_export_read(f, data->data, data->datalen);
+       if (rc != data->datalen)
+               goto err_free;
+
+       /* At this point, the current file offset should match the one recorded
+        * above. */
+       if (uo->pkg->version >= 63 && end_offset != (f->base + f->offset))
+               goto err_free;
+
+       /* Read in the remaining fields */
+       buflen = upkg_export_read(f, buf, 10);
+       if (buflen < 10)
+               goto err_free;
+
+       data->width  = unpack_32_le(buf+0);
+       data->height = unpack_32_le(buf+4);
+       /* Last 2 bytes seem to be simply the base-2 log of width/height for
+        * whatever reason.  Ignore them for now. */
+
+       return data;
+err_free:
+       free(data);
+       return NULL;
+}
 %}
 
 class Engine:Texture from U:Object (dynamic)
+       (interface U:Object:Exportable)
+       (interface U:Object:Loadable)
 {
        private unsigned USize;
        private unsigned UClamp;
@@ -39,6 +117,105 @@ class Engine:Texture from U:Object (dynamic)
 
        private Engine:Palette *Palette;
 
+       private struct engine_texture_data **mipmap_data;
+       private unsigned char mipmap_count;
+
+       interface U:Object:Loadable
+       private int load(U:Object *uo)
+       {
+               struct engine_texture_data **data;
+               Self *self = SELF(uo);
+
+               if (upkg_export_seek(uo->pkg_file, 0, SEEK_SET) != 0)
+                       return -1;
+
+               data = malloc(self->_priv->mipmap_count * sizeof *data);
+               if (!data)
+                       return -1;
+
+               for (int i = 0; i < self->_priv->mipmap_count; i++) {
+                       data[i] = decode_mipmap(uo);
+                       if (!data[i]) {
+                               /* Unwind the allocations. */
+                               for (; i >= 0; i--)
+                                       free(data[i]);
+                               free(data);
+                               return -1;
+                       }
+               }
+
+               self->_priv->mipmap_data = data;
+               return 0;
+       }
+
+       interface U:Object:Loadable
+       private void unload(U:Object *uo)
+       {
+               Self *self = SELF(uo);
+
+               for (int i = 0; i < self->_priv->mipmap_count; i++)
+                       free(self->_priv->mipmap_data[i]);
+               free(self->_priv->mipmap_data);
+               self->_priv->mipmap_data = NULL;
+       }
+
+       interface U:Object:Exportable
+       private int export_name(U:Object *uo, char *buf, size_t n)
+       {
+               return snprintf(buf, n, "%s.pcx", uo->pkg_file->name);
+       }
+
+       interface U:Object:Exportable
+       private int export(U:Object *uo, FILE *f)
+       {
+               Self *self = SELF(uo);
+               struct engine_texture_data *data;
+               struct pcx_head head;
+
+               if (!self->_priv->mipmap_data || !self->_priv->Palette)
+                       return -1;
+               data = self->_priv->mipmap_data[0];
+
+               head.width  = data->width;
+               head.height = data->height;
+
+               if (pcx_init_header(&head) != 0)
+                       return -1;
+               if (fwrite(head.encoded, sizeof head.encoded, 1, f) != 1)
+                       return -1;
+
+               for (unsigned i = 0; i < head.height; i++) {
+                       if (pcx_write_scanline(&head, data->data+i*data->width,
+                                              f) != 0) {
+                               return -1;
+                       }
+               }
+
+               if (pcx_write_palette(self->_priv->Palette, f) != 0)
+                       return -1;
+
+               return 0;
+       }
+
+       override (U:Object) int deserialize(U:Object *uo)
+       {
+               struct upkg_file *f = uo->pkg_file;
+               Self *self = SELF(uo);
+
+               PARENT_HANDLER(uo);
+
+               if (upkg_export_read(f, &self->_priv->mipmap_count, 1) != 1)
+                       return -1;
+               if (self->_priv->mipmap_count == 0)
+                       return -1;
+
+               f->base += 1;
+               f->len  -= 1;
+               upkg_export_seek(f, 0, SEEK_SET);
+
+               return 0;
+       }
+
        property UINT USize
                ( nick = "USize"
                , blurb = "Width of the texture."