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