]> git.draconx.ca Git - upkg.git/blob - src/engine/texture.gob
Stop using gnulib's flexmember module.
[upkg.git] / src / engine / texture.gob
1 %alltop{
2 /*
3  * upkg: tool for manipulating Unreal Tournament packages.
4  * Copyright © 2009-2012, 2020, 2022 Nick Bowler
5  *
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.
10  *
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.
15  *
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/>.
18  */
19 %}
20
21 %ctop{
22 #include <config.h>
23 %}
24
25 %{
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <uobject/loadable.h>
30 #include <uobject/exportable.h>
31 #include <engine/palette.h>
32 #include <engine/pcx.h>
33 #include <pack.h>
34 %}
35
36 %h{
37 #include <uobject/uobject.h>
38
39 struct engine_texture_data {
40         unsigned long width, height;
41
42         unsigned long datalen;
43         unsigned char data[FLEXIBLE_ARRAY_MEMBER];
44 };
45
46 %}
47
48 %{
49 struct engine_texture_data *decode_mipmap(UObject *uo)
50 {
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];
56         long datalen;
57
58         buflen = upkg_export_read(f, buf, sizeof buf);
59         if (f->pkg->version >= 63) {
60                 /*
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
64                  * property below.
65                  */
66                 if (pos + 4 > buflen)
67                         return NULL;
68                 end_offset = unpack_32_le(buf);
69                 pos += 4;
70         }
71
72         rc = upkg_decode_index(&datalen, buf+pos, buflen-pos);
73         if (rc == 0 || datalen < 0)
74                 return NULL;
75         pos += rc;
76
77         data = malloc(sizeof *data + datalen);
78         if (!data)
79                 return NULL;
80         data->datalen = datalen;
81
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)
84                 goto err_free;
85         rc = upkg_export_read(f, data->data, data->datalen);
86         if (rc != data->datalen)
87                 goto err_free;
88
89         /* At this point, the current file offset should match the one recorded
90          * above. */
91         if (f->pkg->version >= 63 && end_offset != (f->base + f->offset)) {
92                 u_err(uo, "mipmap end offset does not match data size");
93                 goto err_free;
94         }
95
96         /* Read in the remaining fields */
97         buflen = upkg_export_read(f, buf, 10);
98         if (buflen < 10)
99                 goto err_free;
100
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. */
105
106         return data;
107 err_free:
108         free(data);
109         return NULL;
110 }
111 %}
112
113 class Engine:Texture from U:Object (dynamic)
114         (interface U:Object:Exportable)
115         (interface U:Object:Loadable)
116 {
117         private unsigned USize;
118         private unsigned UClamp;
119         private unsigned UBits;
120         private unsigned VSize;
121         private unsigned VClamp;
122         private unsigned VBits;
123
124         private float DrawScale;
125
126         private Engine:Palette *Palette = NULL destroywith g_object_unref;
127         private Engine:Texture *DetailTexture = NULL destroywith g_object_unref;
128
129         private struct engine_texture_data **mipmap_data;
130         private unsigned char mipmap_count;
131
132         interface U:Object:Loadable
133         private int load(U:Object *uo)
134         {
135                 struct engine_texture_data **data;
136                 Self *self = SELF(uo);
137
138                 if (upkg_export_seek(uo->pkg_file, 0, SEEK_SET) != 0)
139                         return -1;
140
141                 data = malloc(self->_priv->mipmap_count * sizeof *data);
142                 if (!data)
143                         return -1;
144
145                 for (int i = 0; i < self->_priv->mipmap_count; i++) {
146                         data[i] = decode_mipmap(uo);
147                         if (!data[i]) {
148                                 u_err(uo, "error decoding mipmap level %d", i);
149
150                                 /* Unwind the allocations. */
151                                 for (; i >= 0; i--)
152                                         free(data[i]);
153                                 free(data);
154                                 return -1;
155                         }
156                 }
157
158                 self->_priv->mipmap_data = data;
159                 return 0;
160         }
161
162         interface U:Object:Loadable
163         private void unload(U:Object *uo)
164         {
165                 Self *self = SELF(uo);
166
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;
171         }
172
173         interface U:Object:Exportable
174         private int export_name(U:Object *uo, char *buf, size_t n)
175         {
176                 return snprintf(buf, n, "%s.pcx", uo->pkg_file->name);
177         }
178
179         interface U:Object:Exportable
180         private int export(U:Object *uo, FILE *f)
181         {
182                 Self *self = SELF(uo);
183                 struct engine_texture_data *data;
184                 struct pcx_head head;
185
186                 if (!self->_priv->mipmap_data || !self->_priv->Palette)
187                         return -1;
188                 data = self->_priv->mipmap_data[0];
189
190                 head.width  = data->width;
191                 head.height = data->height;
192
193                 if (pcx_init_header(&head) != 0)
194                         return -1;
195                 if (fwrite(head.encoded, sizeof head.encoded, 1, f) != 1)
196                         return -1;
197
198                 for (unsigned i = 0; i < head.height; i++) {
199                         if (pcx_write_scanline(&head, data->data+i*data->width,
200                                                f) != 0) {
201                                 return -1;
202                         }
203                 }
204
205                 if (pcx_write_palette(self->_priv->Palette, f) != 0)
206                         return -1;
207
208                 return 0;
209         }
210
211         override (U:Object) int deserialize(U:Object *uo)
212         {
213                 struct upkg_file *f = uo->pkg_file;
214                 Self *self = SELF(uo);
215
216                 PARENT_HANDLER(uo);
217
218                 if (upkg_export_read(f, &self->_priv->mipmap_count, 1) != 1)
219                         return -1;
220                 if (self->_priv->mipmap_count == 0)
221                         return -1;
222
223                 f->base += 1;
224                 f->len  -= 1;
225                 upkg_export_seek(f, 0, SEEK_SET);
226
227                 return 0;
228         }
229
230         property UINT USize
231                 ( nick = "USize"
232                 , blurb = "Width of the texture."
233                 , minimum = 0
234                 , maximum = 2048
235                 , default_value = 0
236                 , link
237                 );
238         property UINT UClamp
239                 ( nick = "UClamp"
240                 , blurb = "???"
241                 , minimum = 0
242                 , maximum = 2048
243                 , default_value = 0
244                 , link
245                 );
246         property UINT UBits
247                 ( nick = "UBits"
248                 , blurb = "???"
249                 , minimum = 0
250                 , maximum = 64
251                 , default_value = 0
252                 , link
253                 );
254         property UINT VSize
255                 ( nick = "VSize"
256                 , blurb = "Height of the texture."
257                 , minimum = 0
258                 , maximum = 2048
259                 , default_value = 0
260                 , link
261                 );
262         property UINT VClamp
263                 ( nick = "VClamp"
264                 , blurb = "???"
265                 , minimum = 0
266                 , maximum = 2048
267                 , default_value = 0
268                 , link
269                 );
270         property UINT VBits
271                 ( nick = "VBits"
272                 , blurb = "???"
273                 , minimum = 0
274                 , maximum = 64
275                 , default_value = 0
276                 , link
277                 );
278
279         property FLOAT DrawScale
280                 ( nick = "Draw Scale"
281                 , blurb = "Relative size to parent surface"
282                 , link
283                 );
284
285         property OBJECT Palette
286                 ( nick = "Palette"
287                 , blurb = "Reference to the texture's palette."
288                 , object_type = Engine:Palette
289                 , link
290                 );
291
292         property OBJECT DetailTexture
293                 ( nick = "Detail Texture"
294                 , blurb = "Reference to the corresponding detail texture."
295                 , object_type = Engine:Texture
296                 , link
297                 );
298 }