X-Git-Url: https://git.draconx.ca/gitweb/upkg.git/blobdiff_plain/bb37d402fddbdae411fbc276ed5af9dee864aa67..a0efa108741b6cb53ddc3b1228a91b2e0c75a56a:/src/engine/texture.gob diff --git a/src/engine/texture.gob b/src/engine/texture.gob index 35dd739..3db176e 100644 --- a/src/engine/texture.gob +++ b/src/engine/texture.gob @@ -20,15 +20,93 @@ %{ #include +#include #include +#include +#include #include +#include "pack.h" +#include "pcx.h" %} %h{ #include + +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."