spr.c
/* GPLed. based on GIMP bmp plugin */
#include <errno.h>
#include <glib/gstdio.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#include "goonzu-spr.h"
static void query(void);
static void run(const gchar *name, gint nparams, const GimpParam *param,
gint *nreturn_vals, GimpParam **return_vals);
static ReadSPR(const gchar *name, GError **error);
static gint32 ReadImage(const char* filename, FILE *fd, SPR_Header *header,
SPR_Record records[], gushort pallet[SPR_PALLET_SIZE], GError **error);
static gboolean ReadPixels(FILE *fd, SPR_Record *record, guchar *buf, gint num_pixels);
static gint32 ToL (const guchar *puffer)
{
return (puffer[0] | puffer[1] << 8 | puffer[2] << 16 | puffer[3] << 24);
}
static gint16 ToS (const guchar *puffer)
{
return (puffer[0] | puffer[1] << 8);
}
const GimpPlugInInfo PLUG_IN_INFO = {
NULL,
NULL,
query,
run,
};
MAIN()
static void query(void)
{
static const GimpParamDef load_args[] = {
{ GIMP_PDB_INT32, "run-mode", "Interactive, no-interactive" },
{ GIMP_PDB_STRING, "filename", "The name of the file to load" },
{ GIMP_PDB_STRING, "raw-filename", "The name entered" },
};
static const GimpParamDef load_return_vals[] = {
{ GIMP_PDB_IMAGE, "image", "Output image" },
};
gimp_install_procedure(LOAD_PROC,
"Loads files of Goonzu (Kunshu) SPR file format",
"Loads files of Goonzu (Kunshu) SPR file format",
"testsan",
"testsan",
"2009",
"Goonzu (Kunshu) SPR image",
NULL,
GIMP_PLUGIN,
G_N_ELEMENTS(load_args),
G_N_ELEMENTS(load_return_vals),
load_args, load_return_vals);
gimp_register_magic_load_handler(LOAD_PROC, "spr", "", "0,string,\\x90\\x01");
}
static void run(const gchar *name, gint nparams, const GimpParam *param,
gint *nreturn_vals, GimpParam **return_vals)
{
static GimpParam values[2];
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint32 image_ID;
gint32 drawable_ID;
GimpExportReturn export = GIMP_EXPORT_CANCEL;
GError *error = NULL;
*nreturn_vals = 1;
*return_vals = values;
values[0].type = GIMP_PDB_STATUS;
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
if (strcmp(name, LOAD_PROC) == 0) {
image_ID = ReadSPR(param[1].data.d_string, &error);
if (image_ID != -1) {
*nreturn_vals = 2;
values[1].type = GIMP_PDB_IMAGE;
values[1].data.d_image = image_ID;
} else {
status = GIMP_PDB_EXECUTION_ERROR;
}
} else {
status = GIMP_PDB_CALLING_ERROR;
}
if (status != GIMP_PDB_SUCCESS && error) {
*nreturn_vals = 2;
values[1].type = GIMP_PDB_STRING;
values[1].data.d_string = error->message;
}
values[0].data.d_status = status;
}
static ReadSPR(const gchar *name, GError **error)
{
FILE *fd;
gint32 image_ID;
guchar buffer[SPR_RECORD_SIZE * SPR_MAX_RECORD_COUNT]; /* FIXME */
SPR_Header header;
SPR_Record records[SPR_MAX_RECORD_COUNT];
gushort pallet[SPR_PALLET_SIZE];
gint i;
fd = g_fopen(name, "rb");
if (!fd) {
g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno),
"Could not open '%s' for reading: %s",
gimp_filename_to_utf8(name), g_strerror(errno));
return -1;
}
gimp_progress_init_printf("Opening '%s'",gimp_filename_to_utf8(name));
if (!ReadOK(fd, buffer, SPR_HEADER_SIZE)) {
goto invalid_error;
}
if (ToS(&buffer[0x00]) != 0x0190) {
goto invalid_error;
}
header.width = ToL(&buffer[0x08]);
header.height = ToL(&buffer[0x0C]);
header.total_width = ToL(&buffer[0x10]);
header.total_height = ToL(&buffer[0x14]);
header.pallet_offset = ToL(&buffer[0x18]);
header.count = buffer[0x3C];
if (header.count == 0 || header.width == 0 || header.height == 0 ||
header.width > header.total_width || header.height > header.total_height ||
(header.count * header.width * header.height != header.total_width * header.total_height)) {
goto invalid_error;
}
if (!ReadOK(fd, buffer, SPR_RECORD_SIZE * SPR_MAX_RECORD_COUNT)) {
goto invalid_error;
}
for (i = 0; i < header.count; i++) {
records[i].offset = ToL(&buffer[i * SPR_RECORD_SIZE + 4]);
}
fseek(fd, SPR_DATA_OFFSET + header.pallet_offset, SEEK_SET);
if (!ReadOK(fd, pallet, SPR_PALLET_SIZE)) {
goto invalid_error;
}
image_ID = ReadImage(name, fd, &header, records, pallet, error);
fclose(fd);
if (image_ID < 0) {
return -1;
}
return image_ID;
invalid_error:
g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"'%s' is not a valid SPR file",
gimp_filename_to_utf8(name));
fclose(fd);
return -1;
}
static gint32 ReadImage(const char* filename, FILE *fd, SPR_Header *header,
SPR_Record records[], gushort pallet[SPR_PALLET_SIZE], GError **error)
{
gint32 image;
gint32 layer;
GimpDrawable *drawable;
guchar *dest;
gint i;
GimpPixelRgn pixel_rgn;
guchar gimp_cmap[256 * 3];
gchar layer_name[32];
gint r, g, b;
image = gimp_image_new(header->width, header->height, GIMP_INDEXED);
gimp_image_set_filename(image, filename);
dest = g_malloc0(header->width * header->height);
for (i = 0; i < header->count; i++) {
sprintf(layer_name, "image%03d", i);
layer = gimp_layer_new(image, layer_name,
header->width, header->height,
GIMP_INDEXED_IMAGE, 100, GIMP_NORMAL_MODE);
gimp_image_add_layer(image, layer, i);
drawable = gimp_drawable_get(layer);
ReadPixels(fd, &records[i], dest, header->width * header->height);
gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, TRUE, FALSE);
gimp_pixel_rgn_set_rect(&pixel_rgn, dest, 0, 0, drawable->width, drawable->height);
gimp_progress_update((gdouble)i / header->count);
gimp_drawable_flush(drawable);
gimp_drawable_detach(drawable);
}
for (i = 0; i < 256; i++) {
b = pallet[i] & 0x1F;
g = (pallet[i] >> 5) & 0x3F;
r = (pallet[i] >> 11) & 0x1F;
gimp_cmap[i*3 ] = (r << 3) | (r >> 2);
gimp_cmap[i*3+1] = (g << 2) | (g >> 4);
gimp_cmap[i*3+2] = (b << 3) | (b >> 2);
}
gimp_image_set_colormap(image, gimp_cmap, 256);
gimp_progress_update(1);
g_free(dest);
return image;
}
static gboolean ReadPixels(FILE *fd, SPR_Record *record, guchar *buf, gint num_pixels)
{
gint i, j;
guchar v, len;
fseek(fd, SPR_DATA_OFFSET + record->offset, SEEK_SET);
i = 0;
while (i < num_pixels) {
if (!ReadOK(fd, &v, 1)) {
return FALSE;
}
if (v == 0xFE) {
if (!ReadOK(fd, &len, 1)) {
return FALSE;
}
for (j = 0; (j < len) && (i < num_pixels); j++) {
*(buf+i) = 0xFE;
i++;
}
} else {
*(buf+i) = v;
i++;
}
}
return TRUE;
}