%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 float DrawScale;
private Engine:Palette *Palette;
private Engine:Texture *DetailTexture;
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 FLOAT DrawScale
( nick = "Draw Scale"
, blurb = "Relative size to parent surface"
, link
);
property OBJECT Palette
( nick = "Palette"
, blurb = "Reference to the texture's palette."
, object_type = Engine:Palette
, link
);
property OBJECT DetailTexture
( nick = "Detail Texture"
, blurb = "Reference to the corresponding detail texture."
, object_type = Engine:Texture
, link
);
}