about summary refs log tree commit diff
path: root/converter/other/infotopam.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/other/infotopam.c')
-rw-r--r--converter/other/infotopam.c543
1 files changed, 543 insertions, 0 deletions
diff --git a/converter/other/infotopam.c b/converter/other/infotopam.c
new file mode 100644
index 00000000..4f29eb07
--- /dev/null
+++ b/converter/other/infotopam.c
@@ -0,0 +1,543 @@
+/* infotopam:  A program to convert Amiga Info icon files to PAM files
+ * Copyright (C) 2004  Richard Griswold - griswold@acm.org
+ *
+ * Thanks to the following people on comp.sys.amiga.programmer for tips
+ * and pointers on decoding the info file format:
+ *
+ *   Ben Hutchings
+ *   Thomas Richter
+ *   Kjetil Svalastog Matheussen
+ *   Anders Melchiorsen
+ *   Dirk Stoecker
+ *   Ronald V.D.
+ *
+ * The format of the Amiga info file is as follows:
+ *
+ *   DiskObject header            (78 bytes)
+ *   Optional DrawerData header   (56 bytes)
+ *   First icon header            (20 bytes)
+ *   First icon data
+ *   Second icon header           (20 bytes)
+ *   Second icon data
+ *
+ * The DiskObject header contains, among other things, the magic number
+ * (0xE310), the object width and height (inside the embedded Gadget header),
+ * and the version.
+ *
+ * Each icon header contains the icon width and height, which can be smaller
+ * than the object width and height, and the number of bit-planes.
+ *
+ * The icon data has the following format:
+ *
+ *   BIT-PLANE planes, each with HEIGHT rows of (WIDTH +15) / 16 * 2 bytes
+ *   length.
+ *
+ * So if you have a 9x3x2 icon, the icon data will look like this:
+ *
+ *   aaaa aaaa a000 0000
+ *   aaaa aaaa a000 0000
+ *   aaaa aaaa a000 0000
+ *   bbbb bbbb b000 0000
+ *   bbbb bbbb b000 0000
+ *   bbbb bbbb b000 0000
+ *
+ * Where 'a' is a bit for the first bit-plane, 'b' is a bit for the second
+ * bit-plane, and '0' is padding.  Thanks again to Ben Hutchings for his
+ * very helpful post!
+ *
+ * This program uses code from "sidplay" and an older "infotoxpm" program I
+ * wrote, both of which are released under GPL.
+ *
+ *-------------------------------------------------------------------------
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include "pam.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* Struct to hold miscellaneous icon information */
+typedef struct IconInfo_ {
+    const char  *name;        /* Icon file name */
+    FILE        *fp;          /* Input file pointer */
+
+    bool         forceColor;  /* Convert 1 bitplane icon to color icon */
+    unsigned int numColors;   /* Number of colors to override */
+    bool         selected;    /* Converting selected (second) icon */
+
+    bool         drawerData;  /* Icon has drawer data */
+    unsigned int version;     /* Icon version */
+    unsigned int width;       /* Width in pixels */
+    unsigned int height;      /* Height in pixels */
+    unsigned int depth;       /* Bits of color per pixel */
+    pixel        colors[4];   /* Colors to use for converted icons */
+    unsigned char *icon;      /* Completed icon */
+
+} IconInfo;
+
+/* Header for each icon image */
+typedef struct IconHeader_ { /* 20 bytes */
+    unsigned char pad0[4];        /* Padding (always seems to be zero) */
+    unsigned char iconWidth[2];   /* Width (usually equal to Gadget width) */
+    unsigned char iconHeight[2];  
+    /* Height (usually equal to Gadget height -1) */
+    unsigned char bpp[2];         /* Bits per pixel */
+    unsigned char pad1[10];       /* ??? */
+} IconHeader;
+
+/*
+ * Gadget and DiskObject structs come from the libsidplay 1.36.57 info_.h file
+ * http://www.geocities.com/SiliconValley/Lakes/5147/sidplay/linux.html
+ */
+typedef struct DiskObject_ { /* 78 bytes (including Gadget struct) */
+    unsigned char magic[2];         /* Magic number at the start of the file */
+    unsigned char version[2];       /* Object version number */
+    unsigned char gadget[44];       /* Copy of in memory gadget (44 by */
+    unsigned char type;             /* ??? */
+    unsigned char pad;              /* Pad it out to the next word boundry */
+    unsigned char pDefaultTool[4];  /* Pointer  to default tool */
+    unsigned char ppToolTypes[4];   /* Pointer pointer to tool types */
+    unsigned char currentX[4];      /* Current X position (?) */
+    unsigned char currentY[4];      /* Current Y position (?) */
+    unsigned char pDrawerData[4];   /* Pointer to drawer data */
+    unsigned char pToolWindow[4];   /* Ptr to tool window - only for tools */
+    unsigned char stackSize[4];     /* Stack size - only for tools */
+} DiskObject;
+
+
+
+static void
+parseCommandLine( int              argc,
+                  char *           argv[],
+                  IconInfo * const infoP ) {
+
+    unsigned int numColorArgs,  /* Number of arguments for overriding colors */
+        colorIdx,      /* Color index */
+        i;             /* Argument index */
+    const char  * const colors[4] = { 
+        /* Pixel colors based on original Amiga colors */
+        "#0055AA",    /*   Blue      0,  85, 170 */
+        "#FFFFFF",    /*   White   255, 255, 255 */
+        "#000020",    /*   Black     0,   0,  32 */
+        "#FF8A00"     /*   Orange  255, 138,   0 */
+    };
+
+    /* Option entry variables */
+    optEntry     *option_def;
+    optStruct3    opt;
+    unsigned int  option_def_index;
+    unsigned int numColorsSpec, forceColorSpec, selectedSpec;
+  
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    /* Set command line options */
+    option_def_index = 0;   /* Incremented by OPTENT3 */
+    OPTENT3(0, "forcecolor", OPT_FLAG, NULL, &forceColorSpec, 0);
+    OPTENT3(0, "numcolors",  OPT_UINT, &infoP->numColors, &numColorsSpec, 0);
+    OPTENT3(0, "selected",   OPT_FLAG, NULL, &selectedSpec,   0);
+
+    /* Initialize the iconInfo struct */
+    infoP->name = NULL;
+    infoP->fp = NULL;
+    infoP->drawerData = FALSE;
+    infoP->version = 0;
+    infoP->width = 0;
+    infoP->height = 0;
+    infoP->depth = 0;
+    infoP->icon = NULL;
+    for ( colorIdx = 0; colorIdx < 4; colorIdx++ )
+        infoP->colors[colorIdx] = 
+            ppm_parsecolor( (char*) colors[colorIdx], 0xFF );
+
+    /* Initialize option structure */
+    opt.opt_table     = option_def;
+    opt.short_allowed = FALSE;  /* No short (old-fashioned) options */
+    opt.allowNegNum   = FALSE;  /* No negative number parameters */
+
+    /* Parse the command line */
+    optParseOptions3( &argc, argv, opt, sizeof( opt ), 0 );
+
+    infoP->forceColor = forceColorSpec;
+    infoP->selected = selectedSpec;
+    if (!numColorsSpec)
+        infoP->numColors = 0;
+
+    /* Get colors and file name */
+    numColorArgs = infoP->numColors * 2;
+    if ( ( argc - 1 != numColorArgs ) && ( argc - 1 != numColorArgs + 1 ) ) {
+        pm_error( "Wrong number of arguments for number of colors.  "
+                  "For %u colors, you need %u color arguments, "
+                  "with possibly one more argument for the input file name.",
+                  infoP->numColors, numColorArgs );
+    }
+
+    /* Convert color arguments */
+    for ( i = 1; i < numColorArgs; i += 2 ) {
+        char *       endptr;        /* End pointer for strtol() */
+        unsigned int colorIdx;
+
+        /* Get color index from argument */
+        colorIdx = strtoul( argv[i], &endptr, 0 );
+
+        if ( *endptr != '\0' ) {
+            pm_error( "'%s' is not a valid color index", argv[i] );
+        }
+
+        /* Check color index range (current 0 to 3) */
+        if ( ( colorIdx < 0 ) || ( colorIdx > 3 ) ) {
+            pm_error( "%u is not a valid color index (minimum 0, maximum 3)",
+                      colorIdx );
+        }
+
+        /* Convert the color for this color index */
+        infoP->colors[colorIdx] = ppm_parsecolor( argv[i+1], 0xFF );
+    }
+
+    /* Set file name */
+    if ( i > argc-1 )
+        infoP->name = "-";  /* Read from standard input */
+    else
+        infoP->name = argv[i];
+}
+
+
+
+static void
+getDiskObject( IconInfo * const infoP ) {
+/*-------------------------------------------------------------------------
+ * Get fields from disk object portion of info file
+ *-------------------------------------------------------------------------*/
+    DiskObject  dobj;      /* Disk object structure */
+    size_t      bytesRead;
+
+    /* Read the disk object header */
+    bytesRead = fread( &dobj, 1, sizeof(dobj), infoP->fp );
+    if (ferror(infoP->fp))
+        pm_error( "Cannot read disk object header for file '%s'.  "
+                  "fread() errno = %d (%s)",
+                  infoP->name, errno, strerror( errno ) );
+    else if ( bytesRead != sizeof(dobj) )
+        pm_error( "Cannot read entire disk object header for file '%s'.  "
+                  "Only read 0x%X of 0x%X bytes",
+                  infoP->name, bytesRead, sizeof(dobj) );
+
+    /* Check magic number */
+    if ( ( dobj.magic[0] != 0xE3 ) && ( dobj.magic[1] != 0x10 ) )
+        pm_error( "Wrong magic number for file '%s'.  "
+                  "Expected 0xE310, but got 0x%X%X",
+                  infoP->name, dobj.magic[0], dobj.magic[1] );
+
+    /* Set version info and have drawer data flag */
+    infoP->version     = ( dobj.version[0]     <<  8 ) +
+        ( dobj.version[1]           );
+    infoP->drawerData  = ( dobj.pDrawerData[0] << 24 ) +
+        ( dobj.pDrawerData[1] << 16 ) +
+        ( dobj.pDrawerData[2] <<  8 ) +
+        ( dobj.pDrawerData[3]       ) ? TRUE : FALSE;
+}
+
+
+
+static void 
+getIconHeader( IconInfo * const infoP ) {
+/*-------------------------------------------------------------------------
+ * Get fields from icon header portion of info file
+ *-------------------------------------------------------------------------*/
+    IconHeader  ihead;      /* Icon header structure */
+    size_t      bytesRead;
+
+    /* Read icon header */
+    bytesRead = fread( &ihead, 1, sizeof(ihead), infoP->fp );
+    if ( ferror(infoP->fp ) )
+         pm_error( "Cannot read icon header for file '%s'.  "
+                   "fread() errno = %d (%s)",
+                   infoP->name, errno, strerror( errno ) );
+    else if ( bytesRead != sizeof(ihead) )
+        pm_error( "Cannot read the entire icon header for file '%s'.  "
+                  "Only read 0x%X of 0x%X bytes",
+                  infoP->name, bytesRead, sizeof(ihead) );
+
+    /* Get icon width, heigh, and bitplanes */
+    infoP->width  = ( ihead.iconWidth[0]  << 8 ) + ihead.iconWidth[1];
+    infoP->height = ( ihead.iconHeight[0] << 8 ) + ihead.iconHeight[1];
+    infoP->depth  = ( ihead.bpp[0]        << 8 ) + ihead.bpp[1];
+
+    /* Check number of bit planes */
+    if ( ( infoP->depth > 2 ) || ( infoP->depth < 1 ) )
+        pm_error( "We don't know how to interpret %u bitplanes file '%s'.  ",
+                  infoP->depth, infoP->name );
+}
+
+
+
+static void
+addBitplane(unsigned char * const icon,
+            unsigned int    const bpsize,
+            unsigned char * const buff) {
+/*----------------------------------------------------------------------------
+   Add bitplane to existing icon image
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    unsigned int j;
+    
+    for (i = j = 0; i < bpsize; ++i, j += 8) {
+        icon[j+0] = (icon[j+0] << 1) | ((buff[i] >> 0) & 0x01);
+        icon[j+1] = (icon[j+1] << 1) | ((buff[i] >> 1) & 0x01);
+        icon[j+2] = (icon[j+2] << 1) | ((buff[i] >> 2) & 0x01);
+        icon[j+3] = (icon[j+3] << 1) | ((buff[i] >> 3) & 0x01);
+        icon[j+4] = (icon[j+4] << 1) | ((buff[i] >> 4) & 0x01);
+        icon[j+5] = (icon[j+5] << 1) | ((buff[i] >> 5) & 0x01);
+        icon[j+6] = (icon[j+6] << 1) | ((buff[i] >> 6) & 0x01);
+        icon[j+7] = (icon[j+7] << 1) | ((buff[i] >> 7) & 0x01);
+    }
+}
+
+
+
+static void
+readIconData(FILE *           const fileP,
+             unsigned int     const width,
+             unsigned int     const height, 
+             unsigned int     const depth,
+             unsigned char ** const iconP) {
+/*-------------------------------------------------------------------------
+ * Read icon data from file
+ *-------------------------------------------------------------------------*/
+    int             bitplane; /* Bitplane index */
+    unsigned char * buff;     /* Buffer to hold bits for 1 bitplane */
+    unsigned char * icon;
+
+    unsigned int const bpsize = height * (((width + 15) / 16) * 2);
+        /* Bitplane size in bytes, with padding */
+
+  
+    MALLOCARRAY(buff, bpsize);
+    if ( buff == NULL )
+        pm_error( "Cannot allocate memory to hold icon pixels" );
+
+    MALLOCARRAY(icon, bpsize * 8);
+    if (icon == NULL)
+        pm_error( "Cannot allocate memory to hold icon" );
+
+    /* Initialize to zero */
+    memset(buff, 0, bpsize);
+    memset(icon, 0, bpsize * 8);
+
+    /* Each bitplane is stored independently in the icon file.  This
+     * loop reads one bitplane at a time into buff.  Since fread() may
+     * not read all of the bitplane on the first call, the inner loop
+     * continues until all bytes are read.  The buffer pointer, bp,
+     * points to the next byte in buff to fill in.  When the inner
+     * loop is done, bp points to the end of buff.
+     *
+     * After reading in the entire bitplane, the second inner loop splits the 
+     * eight pixels in each byte of the bitplane into eight separate bytes in 
+     * the icon buffer.  The existing contents of each byte in icon are left 
+     * shifted by one to make room for the next bit. 
+     *
+     * Each byte in the completed icon contains a value from 0 to
+     * 2^depth (0 to 1 for depth of 1 and 0 to 3 for a depth of 3).
+     * This is an index into the colors array in the info struct.  */
+
+    for (bitplane = 0; bitplane < depth; ++bitplane) {
+        /* Read bitplane into buffer */
+        int toread;   /* Number of bytes left to read */
+        unsigned char * buffp;    /* Buffer point for reading data */
+       
+        toread = bpsize; buffp = &buff[0];
+
+        while (toread > 0) {
+            size_t bytesRead;
+
+            bytesRead = fread(buffp, 1, toread, fileP);
+            if (ferror(fileP))
+                pm_error("Cannot read from file info file.  "
+                         "fread() errno = %d (%s)",
+                         errno, strerror(errno));
+            else if (bytesRead == 0)
+                pm_error("Premature end-of-file.  "
+                         "Still have 0x%X bytes to read",
+                         toread );
+            
+            toread -= bytesRead;
+            buffp  += bytesRead;
+        }
+        addBitplane(icon, bpsize, buff);
+    }
+    *iconP = icon;
+
+    free(buff);
+}
+
+
+
+static void
+writeIconData( IconInfo *   const infoP,
+               struct pam * const pamP ) {
+/*-------------------------------------------------------------------------
+ * Write icon data to file
+ *-------------------------------------------------------------------------*/
+    unsigned int const bpwidth = ( ( infoP->width + 15 ) / 16 ) * 16;
+        /* Bitplane width; Width of each row in icon, including padding */
+
+    tuple * row;      /* Output row */
+
+    /* Allocate row */
+    row = pnm_allocpamrow( pamP );
+
+    /* Write icon image to output file */
+    /* Put if check outside for loop to reduce number of times check is made */
+    if ( infoP->depth == 1 ) {
+        if ( infoP->forceColor ) {
+            /* Convert 1 bitplane icon into color PAM */
+            unsigned int i;
+            for ( i = 0; i < infoP->height; ++i ) {
+                unsigned int j;
+                for ( j = 0; j < infoP->width; ++j ) {
+                    /* 1 is black and 0 is white */
+                    unsigned int colorIdx =
+                        infoP->icon[ i * bpwidth + j ] ? 2 : 1;
+                    row[j][PAM_RED_PLANE] =
+                        PPM_GETR( infoP->colors[colorIdx] );
+                    row[j][PAM_GRN_PLANE] =
+                        PPM_GETG( infoP->colors[colorIdx] );
+                    row[j][PAM_BLU_PLANE] =
+                        PPM_GETB( infoP->colors[colorIdx] );
+                }
+                pnm_writepamrow( pamP, row );
+            }
+        } else {
+            /* Convert 1 bitplane icon into bitmap PAM */
+            unsigned int i;
+            for ( i = 0; i < infoP->height; ++i ) {
+                unsigned int j;
+                for ( j = 0; j < infoP->width; j++ ) {
+                    /* 1 is black and 0 is white */
+                    row[j][0] = infoP->icon[ i * bpwidth + j ] ? 0 : 1;
+                }
+                pnm_writepamrow( pamP, row );
+            }
+        }
+    } else {
+        /* Convert color icon into color PAM */
+        unsigned int i;
+        for ( i = 0; i < infoP->height; ++i ) {
+            unsigned int j;
+            for ( j = 0; j < infoP->width; ++j ) {
+                unsigned int const colorIdx = infoP->icon[ i * bpwidth + j ];
+                row[j][PAM_RED_PLANE] = PPM_GETR( infoP->colors[colorIdx] );
+                row[j][PAM_GRN_PLANE] = PPM_GETG( infoP->colors[colorIdx] );
+                row[j][PAM_BLU_PLANE] = PPM_GETB( infoP->colors[colorIdx] );
+            }
+            pnm_writepamrow( pamP, row );
+        }
+    }
+
+    /* Clean up allocated memory */
+    pnm_freepamrow( row );
+}
+
+
+
+int
+main( int argc,
+      char *argv[] ) {
+
+    IconInfo    info;    /* Miscellaneous icon information */
+    struct pam  pam;     /* PAM header */
+    int         skip;    /* Bytes to skip to read next icon header */
+
+    /* Init PNM library */
+    pnm_init( &argc, argv );
+
+    /* Parse command line arguments */
+    parseCommandLine( argc, argv, &info );
+
+    /* Open input file */
+    info.fp = pm_openr( info.name );
+
+    /* Read disk object header */
+    getDiskObject( &info );
+
+    /* Skip drawer data, if any */
+    if ( info.drawerData ) {
+        skip = 56;   /* Draw data size */
+        if ( fseek( info.fp, skip, SEEK_CUR ) < 0 )
+            pm_error( "Cannot skip header information in file '%s'.  "
+                      "fseek() errno = %d (%s)",
+                      info.name, errno, strerror( errno ) );
+    }
+
+    /* Get dimensions for first icon */
+    getIconHeader( &info );
+
+    /* Skip ahead to next header if converting second icon */
+    if ( info.selected ) {
+        skip = info.height * ( ( ( info.width + 15 ) / 16 ) * 2 ) * info.depth;
+
+        if ( fseek( info.fp, skip, SEEK_CUR ) < 0 )
+            pm_error( "Cannot skip to next icon in file '%s'.  "
+                      "fseek() errno = %d (%s)",
+                      info.name, errno, strerror( errno ) );
+
+        /* Get dimensions for second icon */
+        getIconHeader( &info );
+    }
+
+    /* Read icon data */
+    readIconData( info.fp, info.width, info.height, info.depth, &info.icon );
+
+    /* Print icon info */
+    pm_message( "converting %s, version %d, %s icon: %d X %d X %d",
+                info.name, info.version, info.selected ? "second" : "first",
+                info.width, info.height, info.depth );
+
+    /* Write PAM header */
+    pam.size   = sizeof( pam );
+    pam.len    = PAM_STRUCT_SIZE( tuple_type );
+    pam.file   = stdout;
+    pam.height = info.height;
+    pam.width  = info.width;
+    pam.format = PAM_FORMAT;
+
+    if ( ( info.depth == 1 ) && ( info.forceColor == FALSE ) ) {
+        pam.depth  = 1;
+        pam.maxval = 1;
+        strcpy( pam.tuple_type, "BLACKANDWHITE" );
+    } else {
+        pam.depth  = 3;
+        pam.maxval = 0xFF;
+        strcpy( pam.tuple_type, "RGB" );
+    }
+    pnm_writepaminit( &pam );
+
+    /* Write icon data */
+    writeIconData( &info, &pam );
+
+    free( info.icon );
+
+    /* Close input file and return */
+    pm_close( pam.file );
+    pm_close( info.fp );
+
+    return 0;
+}