3 * upkg: tool for manipulating Unreal Tournament packages.
4 * Copyright © 2009-2012, 2020, 2022 Nick Bowler
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
29 #include <uobject/loadable.h>
30 #include <uobject/exportable.h>
31 #include <engine/palette.h>
32 #include <engine/pcx.h>
37 #include <uobject/uobject.h>
39 struct engine_texture_data {
40 unsigned long width, height;
42 unsigned long datalen;
49 struct engine_texture_data *decode_mipmap(UObject *uo)
51 struct upkg_file *f = uo->pkg_file;
52 struct engine_texture_data *data;
53 size_t rc, pos = 0, buflen;
54 unsigned long end_offset;
55 unsigned char buf[32];
58 buflen = upkg_export_read(f, buf, sizeof buf);
59 if (f->pkg->version >= 63) {
61 * There's an offset to the end of the image data here; not
62 * clear why it's useful since it's implied by the very next
63 * field anyway. However, we will nevertheless validate this
68 end_offset = unpack_32_le(buf);
72 rc = upkg_decode_index(&datalen, buf+pos, buflen-pos);
73 if (rc == 0 || datalen < 0)
77 data = malloc(sizeof *data + datalen);
80 data->datalen = datalen;
82 /* Rewind to the start of the image data, and slurp it in. */
83 if (upkg_export_seek(f, -(long)(buflen-pos), SEEK_CUR) != 0)
85 rc = upkg_export_read(f, data->data, data->datalen);
86 if (rc != data->datalen)
89 /* At this point, the current file offset should match the one recorded
91 if (f->pkg->version >= 63 && end_offset != (f->base + f->offset)) {
92 u_err(uo, "mipmap end offset does not match data size");
96 /* Read in the remaining fields */
97 buflen = upkg_export_read(f, buf, 10);
101 data->width = unpack_32_le(buf+0);
102 data->height = unpack_32_le(buf+4);
103 /* Last 2 bytes seem to be simply the base-2 log of width/height for
104 * whatever reason. Ignore them for now. */
113 class Engine:Texture from U:Object (dynamic)
114 (interface U:Object:Exportable)
115 (interface U:Object:Loadable)
117 private unsigned USize;
118 private unsigned UClamp;
119 private unsigned UBits;
120 private unsigned VSize;
121 private unsigned VClamp;
122 private unsigned VBits;
124 private float DrawScale;
126 private Engine:Palette *Palette = NULL destroywith g_object_unref;
127 private Engine:Texture *DetailTexture = NULL destroywith g_object_unref;
129 private struct engine_texture_data **mipmap_data;
130 private unsigned char mipmap_count;
132 interface U:Object:Loadable
133 private int load(U:Object *uo)
135 struct engine_texture_data **data;
136 Self *self = SELF(uo);
138 if (upkg_export_seek(uo->pkg_file, 0, SEEK_SET) != 0)
141 data = malloc(self->_priv->mipmap_count * sizeof *data);
145 for (int i = 0; i < self->_priv->mipmap_count; i++) {
146 data[i] = decode_mipmap(uo);
148 u_err(uo, "error decoding mipmap level %d", i);
150 /* Unwind the allocations. */
158 self->_priv->mipmap_data = data;
162 interface U:Object:Loadable
163 private void unload(U:Object *uo)
165 Self *self = SELF(uo);
167 for (int i = 0; i < self->_priv->mipmap_count; i++)
168 free(self->_priv->mipmap_data[i]);
169 free(self->_priv->mipmap_data);
170 self->_priv->mipmap_data = NULL;
173 interface U:Object:Exportable
174 private int export_name(U:Object *uo, char *buf, size_t n)
176 return snprintf(buf, n, "%s.pcx", uo->pkg_file->name);
179 interface U:Object:Exportable
180 private int export(U:Object *uo, FILE *f)
182 Self *self = SELF(uo);
183 struct engine_texture_data *data;
184 struct pcx_head head;
186 if (!self->_priv->mipmap_data || !self->_priv->Palette)
188 data = self->_priv->mipmap_data[0];
190 head.width = data->width;
191 head.height = data->height;
193 if (pcx_init_header(&head) != 0)
195 if (fwrite(head.encoded, sizeof head.encoded, 1, f) != 1)
198 for (unsigned i = 0; i < head.height; i++) {
199 if (pcx_write_scanline(&head, data->data+i*data->width,
205 if (pcx_write_palette(self->_priv->Palette, f) != 0)
211 override (U:Object) int deserialize(U:Object *uo)
213 struct upkg_file *f = uo->pkg_file;
214 Self *self = SELF(uo);
218 if (upkg_export_read(f, &self->_priv->mipmap_count, 1) != 1)
220 if (self->_priv->mipmap_count == 0)
225 upkg_export_seek(f, 0, SEEK_SET);
232 , blurb = "Width of the texture."
256 , blurb = "Height of the texture."
279 property FLOAT DrawScale
280 ( nick = "Draw Scale"
281 , blurb = "Relative size to parent surface"
285 property OBJECT Palette
287 , blurb = "Reference to the texture's palette."
288 , object_type = Engine:Palette
292 property OBJECT DetailTexture
293 ( nick = "Detail Texture"
294 , blurb = "Reference to the corresponding detail texture."
295 , object_type = Engine:Texture