%{
#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 (f->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 (f->pkg->version >= 63 && end_offset != (f->base + f->offset)) {
+ u_err(uo, "mipmap end offset does not match data size");
+ 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;
private unsigned VBits;
private Engine:Palette *Palette;
+ private Engine:Texture *DetailTexture;
+
+ 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]) {
+ u_err(uo, "error decoding mipmap level %d", 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"
, object_type = Engine:Palette
, link
);
+
+ property OBJECT DetailTexture
+ ( nick = "Detail Texture"
+ , blurb = "Reference to the corresponding detail texture."
+ , object_type = Engine:Texture
+ , link
+ );
}