%alltop{ /* * upkg: tool for manipulating Unreal Tournament packages. * Copyright © 2009-2011 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 #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 (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 UBits; private unsigned VSize; private unsigned VClamp; private unsigned VBits; 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]) { 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" , blurb = "Width of the texture." , minimum = 0 , maximum = 2048 , default_value = 0 , link ); property UINT UClamp ( nick = "UClamp" , blurb = "???" , minimum = 0 , maximum = 2048 , default_value = 0 , link ); property UINT UBits ( nick = "UBits" , blurb = "???" , minimum = 0 , maximum = 64 , default_value = 0 , link ); property UINT VSize ( nick = "VSize" , blurb = "Height of the texture." , minimum = 0 , maximum = 2048 , default_value = 0 , link ); property UINT VClamp ( nick = "VClamp" , blurb = "???" , minimum = 0 , maximum = 2048 , default_value = 0 , link ); property UINT VBits ( nick = "VBits" , blurb = "???" , minimum = 0 , maximum = 64 , default_value = 0 , link ); property OBJECT Palette ( nick = "Palette" , blurb = "Reference to the texture's palette." , object_type = Engine:Palette , link ); }