Blame converter/other/pdbimgtopam.c

Packit 78deda
/*=============================================================================
Packit 78deda
                               pamtopdbimg
Packit 78deda
===============================================================================
Packit 78deda
Packit 78deda
  Convert Palm Pilot PDB Image format (for viewing by
Packit 78deda
  Pilot Image Viewer) to Netpbm image.
Packit 78deda
Packit 78deda
  Bryan Henderson derived this from Eric Howe's program named
Packit 78deda
  'imgvtopnm', in September 2010.
Packit 78deda
=============================================================================*/
Packit 78deda
/*
Packit 78deda
 * Copyright (C) 1997 Eric A. Howe
Packit 78deda
 *
Packit 78deda
 * This program is free software; you can redistribute it and/or modify
Packit 78deda
 * it under the terms of the GNU General Public License as published by
Packit 78deda
 * the Free Software Foundation; either version 2 of the License, or
Packit 78deda
 * (at your option) any later version.
Packit 78deda
 *
Packit 78deda
 * This program is distributed in the hope that it will be useful,
Packit 78deda
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 78deda
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 78deda
 * GNU General Public License for more details.
Packit 78deda
 *
Packit 78deda
 * You should have received a copy of the GNU General Public License
Packit 78deda
 * along with this program; if not, write to the Free Software
Packit 78deda
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Packit 78deda
 *
Packit 78deda
 *   Authors:  Eric A. Howe (mu@trends.net)
Packit 78deda
 *             Bryan Henderson
Packit 78deda
 */
Packit 78deda
#include <stdlib.h>
Packit 78deda
#include <assert.h>
Packit 78deda
Packit 78deda
#include "pm_c_util.h"
Packit 78deda
#include "mallocvar.h"
Packit 78deda
#include "nstring.h"
Packit 78deda
#include "shhopt.h"
Packit 78deda
#include "pam.h"
Packit 78deda
Packit 78deda
#include "ipdb.h"
Packit 78deda
Packit 78deda
Packit 78deda
struct cmdlineInfo {
Packit 78deda
    /* All the information the user supplied in the command line,
Packit 78deda
       in a form easy for the program to use.
Packit 78deda
    */
Packit 78deda
    const char * inputFileName;  /* '-' if stdin */
Packit 78deda
    const char * notefile;  /* NULL if not specified */
Packit 78deda
    unsigned int verbose;
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseCommandLine(int argc, const char ** argv,
Packit 78deda
                 struct cmdlineInfo * const cmdlineP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   parse program command line described in Unix standard form by argc
Packit 78deda
   and argv.  Return the information in the options as *cmdlineP.  
Packit 78deda
Packit 78deda
   If command line is internally inconsistent (invalid options, etc.),
Packit 78deda
   issue error message to stderr and abort program.
Packit 78deda
Packit 78deda
   Note that the strings we return are stored in the storage that
Packit 78deda
   was passed to us as the argv array.  We also trash *argv.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    optEntry *option_def;
Packit 78deda
        /* Instructions to pm_optParseOptions3 on how to parse our options.
Packit 78deda
         */
Packit 78deda
    optStruct3 opt;
Packit 78deda
Packit 78deda
    unsigned int option_def_index;
Packit 78deda
Packit 78deda
    unsigned int notefileSpec;
Packit 78deda
Packit 78deda
    MALLOCARRAY_NOFAIL(option_def, 100);
Packit 78deda
Packit 78deda
    option_def_index = 0;   /* incremented by OPTENT3 */
Packit 78deda
    OPTENT3(0, "notefile",            OPT_STRING,    &cmdlineP->notefile,
Packit 78deda
            &notefileSpec,            0);
Packit 78deda
    OPTENT3(0, "verbose",             OPT_FLAG,    NULL,
Packit 78deda
            &cmdlineP->verbose,       0);
Packit 78deda
Packit 78deda
    opt.opt_table = option_def;
Packit 78deda
    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
Packit 78deda
    opt.allowNegNum = false;  /* We have no parms that are negative numbers */
Packit 78deda
Packit 78deda
    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
Packit 78deda
        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
Packit 78deda
Packit 78deda
    if (!notefileSpec)
Packit 78deda
        cmdlineP->notefile = NULL;
Packit 78deda
    
Packit 78deda
    if (argc-1 < 1)
Packit 78deda
        cmdlineP->inputFileName = "-";
Packit 78deda
    else if (argc-1 == 1)
Packit 78deda
        cmdlineP->inputFileName = argv[1];
Packit 78deda
    else
Packit 78deda
        pm_error("Program takes at most one argument:  input file name");
Packit 78deda
Packit 78deda
    free(option_def);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
#define getg16pixel(b,o)    (((b) >> (4 - 4*(o))) & 0x0f)
Packit 78deda
#define getgpixel(b,o)      (((b) >> (6 - 2*(o))) & 0x03)
Packit 78deda
#define getmpixel(b,o)      (((b) >> (7 - (o))) & 0x01)
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
abortShort() {
Packit 78deda
    pm_error("Invalid image.  Compression algorithm runs out of "
Packit 78deda
             "compressed data before generating the expected "
Packit 78deda
             "amount of image data");
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
abortOverrun() {
Packit 78deda
    pm_error("Invalid image.  Compression algorithm finds the end of "
Packit 78deda
             "the image in the middle of a run");
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
decompress(const uint8_t * const compressed,
Packit 78deda
           size_t          const compressedSize,
Packit 78deda
           size_t          const imageSize,
Packit 78deda
           uint8_t **      const uncompressedP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Decompress the data 'compressed', which is 'compressedSize' bytes long.
Packit 78deda
   Return the decompressed data in newly malloced storage as
Packit 78deda
   *decompressedP.  Decompression should yield exactly 'imageSize' bytes.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    /*
Packit 78deda
     * The compression scheme used is a simple RLE; the control codes,
Packit 78deda
     * CODE, are one byte and have the following meanings:
Packit 78deda
     *
Packit 78deda
     *  CODE >  0x80    Insert (CODE + 1 - 0x80) copies of the next byte.
Packit 78deda
     *  CODE <= 0x80    Insert the next (CODE + 1) literal bytes.
Packit 78deda
     *
Packit 78deda
     * Compressed pieces can (and do) cross row boundaries.
Packit 78deda
     */
Packit 78deda
    uint8_t * uncompressed;
Packit 78deda
Packit 78deda
    MALLOCARRAY(uncompressed, imageSize);
Packit 78deda
Packit 78deda
    if (uncompressed) {
Packit 78deda
        const uint8_t * inP;
Packit 78deda
        uint8_t *       outP;
Packit 78deda
        size_t          bytesLeft;
Packit 78deda
        
Packit 78deda
        for (bytesLeft = imageSize,
Packit 78deda
                 inP  = &compressed[0], outP = &uncompressed[0];
Packit 78deda
             bytesLeft > 0;
Packit 78deda
            ) {
Packit 78deda
Packit 78deda
            int got, put;
Packit 78deda
Packit 78deda
            if (inP > compressed + compressedSize)
Packit 78deda
                abortShort();
Packit 78deda
Packit 78deda
            if (*inP > 0x80) {
Packit 78deda
                put = *inP++ + 1 - 0x80;
Packit 78deda
                if (outP + put > uncompressed + imageSize)
Packit 78deda
                    abortOverrun();
Packit 78deda
                memset(outP, *inP, put);
Packit 78deda
                got = 1;
Packit 78deda
            } else {
Packit 78deda
                put = *inP++ + 1;
Packit 78deda
                if (inP + put > compressed + compressedSize)
Packit 78deda
                    abortShort();
Packit 78deda
                if (outP + put > uncompressed + imageSize)
Packit 78deda
                    abortOverrun();
Packit 78deda
                memcpy(outP, inP, put);
Packit 78deda
                got = put;
Packit 78deda
            }
Packit 78deda
            inP       += got;
Packit 78deda
            outP      += put;
Packit 78deda
            assert(bytesLeft >= put);
Packit 78deda
            bytesLeft -= put;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    *uncompressedP = uncompressed;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
#define UNKNOWN_OFFSET  (uint32_t)-1
Packit 78deda
Packit 78deda
static void
Packit 78deda
readCompressed(IMAGE *    const imgP,
Packit 78deda
               uint32_t   const end_offset,
Packit 78deda
               FILE *     const fP,
Packit 78deda
               size_t *   const dataSizeP,
Packit 78deda
               uint8_t ** const dataP,
Packit 78deda
               int *      const retvalP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Read the compressed data from file *fP (actually, if the image isn't
Packit 78deda
   compressed, then it's just the regular data).
Packit 78deda
Packit 78deda
   Return the data in newly malloced storage as *dataP, which is
Packit 78deda
   *dataSizeP bytes long.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    int retval;
Packit 78deda
    uint8_t * buffer;
Packit 78deda
    size_t dataSize;
Packit 78deda
Packit 78deda
    dataSize = 0;  /* initial value */
Packit 78deda
Packit 78deda
    if (end_offset == UNKNOWN_OFFSET) {
Packit 78deda
        /*
Packit 78deda
         * Read sufficient amount for worst-case compressed image,
Packit 78deda
         * or until EOF.  Some of them have an extra zero byte
Packit 78deda
         * dangling off the end.  I originally thought this was
Packit 78deda
         * an empty note record (even though there was no record
Packit 78deda
         * header for it); however, the release notes for Image
Packit 78deda
         * Compression Manager 1.1 on http://www.pilotgear.com
Packit 78deda
         * note this extra byte as a bug in Image Compression
Packit 78deda
         * Manager 1.0 which 1.1 fixes.  We'll just blindly read
Packit 78deda
         * this extra byte and ignore it by paying attention to
Packit 78deda
         * the image dimensions.
Packit 78deda
         */
Packit 78deda
       size_t const maxCompressedSizeWithBloat = ipdb_img_size(imgP) * 2;
Packit 78deda
         /*
Packit 78deda
          * Provide a buffer large enough for the worst case.
Packit 78deda
          * See note in lib/util/runlength.c .
Packit 78deda
          * We do not use pm_rlenc_allocoutbuf() because there is no
Packit 78deda
          * guarantee that the encoder that produced the image was
Packit 78deda
          * efficient.
Packit 78deda
          */
Packit 78deda
       MALLOCARRAY(buffer, maxCompressedSizeWithBloat);
Packit 78deda
Packit 78deda
        if (buffer == NULL)
Packit 78deda
            retval = ENOMEM;
Packit 78deda
        else {
Packit 78deda
            dataSize = fread(buffer, 1,  maxCompressedSizeWithBloat, fP);
Packit 78deda
            if (dataSize <= 0)
Packit 78deda
                retval = EIO;
Packit 78deda
            else
Packit 78deda
                retval = 0;
Packit 78deda
Packit 78deda
            if (retval != 0)
Packit 78deda
                free(buffer);
Packit 78deda
        }
Packit 78deda
    } else {
Packit 78deda
        /*
Packit 78deda
         * Read to the indicated offset.
Packit 78deda
         */
Packit 78deda
        dataSize = end_offset - ftell(fP) + 1;
Packit 78deda
        
Packit 78deda
        MALLOCARRAY(buffer, dataSize);
Packit 78deda
Packit 78deda
        if (buffer == NULL)
Packit 78deda
            retval = ENOMEM;
Packit 78deda
        else {
Packit 78deda
            ssize_t rc;
Packit 78deda
            rc = fread(buffer, 1, dataSize, fP);
Packit 78deda
            if (rc != dataSize)
Packit 78deda
                retval = EIO;
Packit 78deda
            else
Packit 78deda
                retval = 0;
Packit 78deda
Packit 78deda
            if (retval != 0)
Packit 78deda
                free(buffer);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    *dataSizeP = dataSize;
Packit 78deda
    *dataP = buffer;
Packit 78deda
    *retvalP = retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
imageReadHeader(FILE *  const fileP,
Packit 78deda
                IMAGE * const imgP,
Packit 78deda
                bool    const dump) {
Packit 78deda
Packit 78deda
    fread(&imgP->name, 1, 32, fileP);
Packit 78deda
    pm_readcharu(fileP, &imgP->version);
Packit 78deda
    pm_readcharu(fileP, &imgP->type);
Packit 78deda
    fread(&imgP->reserved1, 1, 4, fileP);
Packit 78deda
    fread(&imgP->note, 1, 4, fileP);
Packit 78deda
    pm_readbigshortu(fileP, &imgP->x_last);
Packit 78deda
    pm_readbigshortu(fileP, &imgP->y_last);
Packit 78deda
    fread(&imgP->reserved2, 1, 4, fileP);
Packit 78deda
    pm_readbigshortu(fileP, &imgP->x_anchor);
Packit 78deda
    pm_readbigshortu(fileP, &imgP->y_anchor);
Packit 78deda
    pm_readbigshortu(fileP, &imgP->width);
Packit 78deda
    pm_readbigshortu(fileP, &imgP->height);
Packit 78deda
Packit 78deda
    if (dump) {
Packit 78deda
        pm_message("PDB IMAGE header:");
Packit 78deda
        pm_message("  Name: '%.*s'", (int)sizeof(imgP->name), imgP->name);
Packit 78deda
        pm_message("  Version: %02x", imgP->version);
Packit 78deda
        pm_message("  Type: %s", ipdb_typeName(imgP->type));
Packit 78deda
        pm_message("  Note: %02x %02x %02x %02x",
Packit 78deda
                   imgP->note[0], imgP->note[1], imgP->note[2], imgP->note[3]);
Packit 78deda
        pm_message("  X_last: %u", imgP->x_last);
Packit 78deda
        pm_message("  Y_last: %u", imgP->y_last);
Packit 78deda
        pm_message("  X_anchor: %u", imgP->x_anchor);
Packit 78deda
        pm_message("  Y_anchor: %u", imgP->y_anchor);
Packit 78deda
        pm_message("  Width: %u", imgP->width);
Packit 78deda
        pm_message("  Height: %u", imgP->height);
Packit 78deda
        pm_message("Pixels per byte: %u", ipdb_img_ppb(imgP));
Packit 78deda
        pm_message("Image size: %lu bytes",
Packit 78deda
                   (unsigned long)ipdb_img_size(imgP));
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
static int
Packit 78deda
imageReadData(FILE *   const fileP,
Packit 78deda
              IMAGE *  const imgP,
Packit 78deda
              uint32_t const end_offset) {
Packit 78deda
Packit 78deda
    size_t const imageSize = ipdb_img_size(imgP);  
Packit 78deda
Packit 78deda
    int retval;
Packit 78deda
    size_t dataSize;
Packit 78deda
    uint8_t * buffer;
Packit 78deda
Packit 78deda
    readCompressed(imgP, end_offset, fileP, &dataSize, &buffer, &retval);
Packit 78deda
Packit 78deda
    if (retval == 0) {
Packit 78deda
        /*
Packit 78deda
         * Compressed data can cross row boundaries so we decompress
Packit 78deda
         * the data here to avoid messiness in the row access functions.
Packit 78deda
         */
Packit 78deda
        if (dataSize < imageSize || imgP->version == 1) {
Packit 78deda
            if (imgP->version == 0)
Packit 78deda
                pm_message("Image header says raster data is uncompressed.  "
Packit 78deda
                           "Encountered only %u instead of the "
Packit 78deda
                           "required %u bytes.  Assuming compressed mode.",
Packit 78deda
                           (unsigned)dataSize, (unsigned)imageSize);
Packit 78deda
            decompress(buffer, dataSize, imageSize, &imgP->data);
Packit 78deda
            if (imgP->data == NULL)
Packit 78deda
                retval = ENOMEM;
Packit 78deda
            else
Packit 78deda
                imgP->compressed = true;
Packit 78deda
            free(buffer);
Packit 78deda
        } else {
Packit 78deda
            if (dataSize > imageSize)
Packit 78deda
                pm_message("Image header says raster data is uncompressed. "
Packit 78deda
                           "Encountered %u instead of the required %u bytes. "
Packit 78deda
                           "Assuming uncompressed mode.",
Packit 78deda
                           (unsigned)dataSize, (unsigned)imageSize);
Packit 78deda
            imgP->compressed = false;
Packit 78deda
            imgP->data       = buffer;
Packit 78deda
            /* Storage at 'buffer' now belongs to *imgP */
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static int
Packit 78deda
imageRead(IMAGE *  const imgP,
Packit 78deda
          uint32_t const end_offset,
Packit 78deda
          FILE *   const fileP,
Packit 78deda
          bool     const verbose) {
Packit 78deda
Packit 78deda
    if (imgP) {
Packit 78deda
        imgP->r->offset = (uint32_t)ftell(fileP);
Packit 78deda
Packit 78deda
        imageReadHeader(fileP, imgP, verbose);
Packit 78deda
Packit 78deda
        imageReadData(fileP, imgP, end_offset);
Packit 78deda
    }
Packit 78deda
    return 0;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static int
Packit 78deda
textRead(TEXT * const textP,
Packit 78deda
         FILE * const fileP) {
Packit 78deda
Packit 78deda
    int retval;
Packit 78deda
    char    * s;
Packit 78deda
    char    buf[128];
Packit 78deda
    int used, alloced, len;
Packit 78deda
Packit 78deda
    if (textP == NULL)
Packit 78deda
        return 0;
Packit 78deda
Packit 78deda
    textP->r->offset = (uint32_t)ftell(fileP);
Packit 78deda
    
Packit 78deda
    /*
Packit 78deda
     * What a pain in the ass!  Why the hell isn't there a length
Packit 78deda
     * attached to the text record?  I suppose the designer wasn't
Packit 78deda
     * concerned about non-seekable (i.e. pipes) input streams.
Packit 78deda
     * Perhaps I'm being a little harsh, the lack of a length probably
Packit 78deda
     * isn't much of an issue on the Pilot.
Packit 78deda
     */
Packit 78deda
    used    = 0;
Packit 78deda
    alloced = 0;
Packit 78deda
    s       = NULL;
Packit 78deda
    retval = 0;  /* initial value */
Packit 78deda
    while ((len = fread(buf, 1, sizeof(buf), fileP)) != 0 && retval == 0) {
Packit 78deda
        if (buf[len - 1] == '\0')
Packit 78deda
            --len;
Packit 78deda
        if (used + len > alloced) {
Packit 78deda
            alloced += 2 * sizeof(buf);
Packit 78deda
            REALLOCARRAY(s, alloced);
Packit 78deda
Packit 78deda
            if (s == NULL)
Packit 78deda
                retval = ENOMEM;
Packit 78deda
        }
Packit 78deda
        if (retval == 0) {
Packit 78deda
            memcpy(s + used, buf, len);
Packit 78deda
            used += len;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    if (retval == 0) {
Packit 78deda
        textP->data = calloc(1, used + 1);
Packit 78deda
        if (textP->data == NULL)
Packit 78deda
            retval = ENOMEM;
Packit 78deda
        else
Packit 78deda
            memcpy(textP->data, s, used);
Packit 78deda
    }
Packit 78deda
    if (s)
Packit 78deda
        free(s);
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static int
Packit 78deda
pdbheadRead(PDBHEAD * const pdbHeadP,
Packit 78deda
            FILE *    const fileP) {
Packit 78deda
Packit 78deda
    int retval;
Packit 78deda
Packit 78deda
    fread(pdbHeadP->name, 1, 32, fileP);
Packit 78deda
    pm_readbigshortu(fileP, &pdbHeadP->flags);
Packit 78deda
    pm_readbigshortu(fileP, &pdbHeadP->version);
Packit 78deda
    pm_readbiglongu2(fileP, &pdbHeadP->ctime);
Packit 78deda
    pm_readbiglongu2(fileP, &pdbHeadP->mtime);
Packit 78deda
    pm_readbiglongu2(fileP, &pdbHeadP->btime);
Packit 78deda
    pm_readbiglongu2(fileP, &pdbHeadP->mod_num);
Packit 78deda
    pm_readbiglongu2(fileP, &pdbHeadP->app_info);
Packit 78deda
    pm_readbiglongu2(fileP, &pdbHeadP->sort_info);
Packit 78deda
    fread(pdbHeadP->type, 1, 4,  fileP);
Packit 78deda
    fread(pdbHeadP->id,   1, 4,  fileP);
Packit 78deda
    pm_readbiglongu2(fileP, &pdbHeadP->uniq_seed);
Packit 78deda
    pm_readbiglongu2(fileP, &pdbHeadP->next_rec);
Packit 78deda
    pm_readbigshortu(fileP, &pdbHeadP->num_recs);
Packit 78deda
Packit 78deda
    if (!memeq(pdbHeadP->type, IPDB_vIMG, 4) 
Packit 78deda
        || !memeq(pdbHeadP->id, IPDB_View, 4))
Packit 78deda
        retval = E_NOTIMAGE;
Packit 78deda
    else
Packit 78deda
        retval = 0;
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static int
Packit 78deda
rechdrRead(RECHDR * const rechdrP,
Packit 78deda
           FILE *   const fileP) {
Packit 78deda
Packit 78deda
    int retval;
Packit 78deda
    off_t   len;
Packit 78deda
Packit 78deda
    pm_readbiglongu2(fileP, &rechdrP->offset);
Packit 78deda
Packit 78deda
    len = (off_t)rechdrP->offset - ftell(fileP);
Packit 78deda
    switch(len) {
Packit 78deda
    case 4:
Packit 78deda
    case 12:
Packit 78deda
        /*
Packit 78deda
         * Version zero (eight bytes of record header) or version
Packit 78deda
         * two with a note (two chunks of eight record header bytes).
Packit 78deda
         */
Packit 78deda
        fread(&rechdrP->unknown[0], 1, 3, fileP);
Packit 78deda
        fread(&rechdrP->rec_type,   1, 1, fileP);
Packit 78deda
        rechdrP->n_extra = 0;
Packit 78deda
        rechdrP->extra   = NULL;
Packit 78deda
        retval = 0;
Packit 78deda
        break;
Packit 78deda
    case 6:
Packit 78deda
        /*
Packit 78deda
         * Version one (ten bytes of record header).
Packit 78deda
         */
Packit 78deda
        fread(&rechdrP->unknown[0], 1, 3, fileP);
Packit 78deda
        fread(&rechdrP->rec_type,   1, 1, fileP);
Packit 78deda
        rechdrP->n_extra = 2;
Packit 78deda
        MALLOCARRAY(rechdrP->extra, rechdrP->n_extra);
Packit 78deda
        if (rechdrP->extra == NULL)
Packit 78deda
            retval = ENOMEM;
Packit 78deda
        else {
Packit 78deda
            fread(rechdrP->extra, 1, rechdrP->n_extra, fileP);
Packit 78deda
            retval = 0;
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
    default:
Packit 78deda
        /*
Packit 78deda
         * hmmm.... I'll assume this is the record header
Packit 78deda
         * for a text record.
Packit 78deda
         */
Packit 78deda
        fread(&rechdrP->unknown[0], 1, 3, fileP);
Packit 78deda
        fread(&rechdrP->rec_type,   1, 1, fileP);
Packit 78deda
        rechdrP->n_extra = 0;
Packit 78deda
        rechdrP->extra   = NULL;
Packit 78deda
        retval = 0;
Packit 78deda
        break;
Packit 78deda
    }
Packit 78deda
    if (retval == 0) {
Packit 78deda
        if ((rechdrP->rec_type != IMG_REC && rechdrP->rec_type != TEXT_REC)
Packit 78deda
            || !memeq(rechdrP->unknown, IPDB_MYST, 3))
Packit 78deda
            retval = E_NOTRECHDR;
Packit 78deda
    }
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static int
Packit 78deda
ipdbRead(IPDB * const pdbP,
Packit 78deda
         FILE * const fileP,
Packit 78deda
         bool   const verbose) {
Packit 78deda
Packit 78deda
    int retval;
Packit 78deda
Packit 78deda
    ipdb_clear(pdbP);
Packit 78deda
Packit 78deda
    pdbP->p = ipdb_pdbhead_alloc(NULL);
Packit 78deda
Packit 78deda
    if (pdbP->p == NULL)
Packit 78deda
        retval = ENOMEM;
Packit 78deda
    else {
Packit 78deda
        int status;
Packit 78deda
Packit 78deda
        status = pdbheadRead(pdbP->p, fileP);
Packit 78deda
Packit 78deda
        if (status != 0)
Packit 78deda
            retval = status;
Packit 78deda
        else {
Packit 78deda
            pdbP->i = ipdb_image_alloc(pdbP->p->name, IMG_GRAY, 0, 0);
Packit 78deda
            if (pdbP->i == NULL)
Packit 78deda
                retval = ENOMEM;
Packit 78deda
            else {
Packit 78deda
                int status;
Packit 78deda
                status = rechdrRead(pdbP->i->r, fileP);
Packit 78deda
                if (status != 0)
Packit 78deda
                    retval = status;
Packit 78deda
                else {
Packit 78deda
                    if (pdbP->p->num_recs > 1) {
Packit 78deda
                        pdbP->t = ipdb_text_alloc(NULL);
Packit 78deda
                        if (pdbP->t == NULL)
Packit 78deda
                            retval = ENOMEM;
Packit 78deda
                        else {
Packit 78deda
                            int status;
Packit 78deda
                            status = rechdrRead(pdbP->t->r, fileP);
Packit 78deda
                            if (status != 0)
Packit 78deda
                                retval = status;
Packit 78deda
                            else
Packit 78deda
                                retval = 0;
Packit 78deda
                        }
Packit 78deda
                    } else
Packit 78deda
                        retval = 0;
Packit 78deda
                    
Packit 78deda
                    if (retval == 0) {
Packit 78deda
                        uint32_t const offset =
Packit 78deda
                            pdbP->t == NULL ?
Packit 78deda
                            UNKNOWN_OFFSET : pdbP->t->r->offset - 1;
Packit 78deda
Packit 78deda
                        int status;
Packit 78deda
Packit 78deda
                        status = imageRead(pdbP->i, offset, fileP, verbose);
Packit 78deda
                        if (status != 0)
Packit 78deda
                            retval = status;
Packit 78deda
                        else {
Packit 78deda
                            if (pdbP->t != NULL) {
Packit 78deda
                                int status;
Packit 78deda
                                
Packit 78deda
                                status = textRead(pdbP->t, fileP);
Packit 78deda
                                if (status != 0)
Packit 78deda
                                    retval = status;
Packit 78deda
                            }
Packit 78deda
                        }
Packit 78deda
                    }
Packit 78deda
                }
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
g16unpack(const uint8_t * const p,
Packit 78deda
          uint8_t *       const g,
Packit 78deda
          int             const w) {
Packit 78deda
Packit 78deda
    static const uint8_t pal[] =
Packit 78deda
        {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
Packit 78deda
         0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00};
Packit 78deda
    const uint8_t * seg;
Packit 78deda
    unsigned int i;
Packit 78deda
Packit 78deda
    for (i = 0, seg = p; i < w; i += 2, ++seg) {
Packit 78deda
        g[i + 0] = pal[getg16pixel(*seg, 0)];
Packit 78deda
        g[i + 1] = pal[getg16pixel(*seg, 1)];
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
gunpack(const uint8_t * const p,
Packit 78deda
        uint8_t *       const g,
Packit 78deda
        int             const w) {
Packit 78deda
Packit 78deda
    static const uint8_t pal[] = {0xff, 0xaa, 0x55, 0x00};
Packit 78deda
    const uint8_t * seg;
Packit 78deda
    unsigned int i;
Packit 78deda
Packit 78deda
    for (i = 0, seg = p; i < w; i += 4, ++seg) {
Packit 78deda
        g[i + 0] = pal[getgpixel(*seg, 0)];
Packit 78deda
        g[i + 1] = pal[getgpixel(*seg, 1)];
Packit 78deda
        g[i + 2] = pal[getgpixel(*seg, 2)];
Packit 78deda
        g[i + 3] = pal[getgpixel(*seg, 3)];
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
munpack(const uint8_t * const p,
Packit 78deda
        uint8_t *       const b,
Packit 78deda
        int             const w) {
Packit 78deda
Packit 78deda
    static const uint8_t pal[] = {PAM_BW_WHITE, PAM_BLACK};
Packit 78deda
    const uint8_t * seg;
Packit 78deda
    unsigned int i;
Packit 78deda
Packit 78deda
    for (i = 0, seg = p; i < w; i += 8, ++seg) {
Packit 78deda
        b[i + 0] = pal[getmpixel(*seg, 0)];
Packit 78deda
        b[i + 1] = pal[getmpixel(*seg, 1)];
Packit 78deda
        b[i + 2] = pal[getmpixel(*seg, 2)];
Packit 78deda
        b[i + 3] = pal[getmpixel(*seg, 3)];
Packit 78deda
        b[i + 4] = pal[getmpixel(*seg, 4)];
Packit 78deda
        b[i + 5] = pal[getmpixel(*seg, 5)];
Packit 78deda
        b[i + 6] = pal[getmpixel(*seg, 6)];
Packit 78deda
        b[i + 7] = pal[getmpixel(*seg, 7)];
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
g16row(IPDB *       const pdbP,
Packit 78deda
       unsigned int const row,
Packit 78deda
       uint8_t *    const buffer) {
Packit 78deda
    
Packit 78deda
    g16unpack(ipdb_img_row(pdbP->i, row), buffer, ipdb_width(pdbP));
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
grow(IPDB *       const pdbP,
Packit 78deda
     unsigned int const row,
Packit 78deda
     uint8_t *    const buffer) {
Packit 78deda
Packit 78deda
    gunpack(ipdb_img_row(pdbP->i, row), buffer, ipdb_width(pdbP));
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
mrow(IPDB *       const pdbP,
Packit 78deda
     unsigned int const row,
Packit 78deda
     uint8_t *    const buffer) {
Packit 78deda
Packit 78deda
    munpack(ipdb_img_row(pdbP->i, row), buffer, ipdb_width(pdbP));
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writeImgPam(IPDB * const pdbP,
Packit 78deda
            FILE * const ofP) {
Packit 78deda
Packit 78deda
    struct pam pam;
Packit 78deda
    tuple * tupleRow;
Packit 78deda
    unsigned int row;
Packit 78deda
    uint8_t * imgRow;
Packit 78deda
Packit 78deda
    MALLOCARRAY(imgRow, ipdb_width(pdbP));
Packit 78deda
Packit 78deda
    pam.size             = sizeof(pam);
Packit 78deda
    pam.len              = PAM_STRUCT_SIZE(tuple_type);
Packit 78deda
    pam.file             = ofP;
Packit 78deda
    pam.plainformat      = 0;
Packit 78deda
    pam.width            = ipdb_width(pdbP);
Packit 78deda
    pam.height           = ipdb_height(pdbP);
Packit 78deda
    pam.depth            = 1;
Packit 78deda
    pam.maxval           = ipdb_type(pdbP) == IMG_MONO ? 1 : 255;
Packit 78deda
    pam.bytes_per_sample = pnm_bytespersample(pam.maxval);
Packit 78deda
    pam.format           = PAM_FORMAT;
Packit 78deda
    strcpy(pam.tuple_type,
Packit 78deda
           ipdb_type(pdbP) == IMG_MONO ?
Packit 78deda
           PAM_PBM_TUPLETYPE : PAM_PGM_TUPLETYPE);
Packit 78deda
Packit 78deda
    pnm_writepaminit(&pam;;
Packit 78deda
    
Packit 78deda
    tupleRow = pnm_allocpamrow(&pam;;
Packit 78deda
Packit 78deda
    for (row = 0; row < pam.height; ++row) {
Packit 78deda
        unsigned int col;
Packit 78deda
Packit 78deda
Packit 78deda
        if (ipdb_type(pdbP) == IMG_MONO)
Packit 78deda
            mrow(pdbP, row, imgRow);
Packit 78deda
        else if (ipdb_type(pdbP) == IMG_GRAY)
Packit 78deda
            grow(pdbP, row, imgRow);
Packit 78deda
        else
Packit 78deda
            g16row(pdbP, row, imgRow);
Packit 78deda
Packit 78deda
        for (col = 0; col < pam.width; ++col)
Packit 78deda
            tupleRow[col][0] = imgRow[col];
Packit 78deda
        
Packit 78deda
        pnm_writepamrow(&pam, tupleRow);
Packit 78deda
    }
Packit 78deda
    pnm_freepamrow(tupleRow);
Packit 78deda
Packit 78deda
    free(imgRow);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writeText(IPDB *       const pdbP,
Packit 78deda
          const char * const name) {
Packit 78deda
Packit 78deda
    const char * const note = ipdb_text(pdbP);
Packit 78deda
Packit 78deda
    FILE * fP;
Packit 78deda
Packit 78deda
    if (name == NULL || note == NULL) {
Packit 78deda
    } else {
Packit 78deda
        fP = pm_openw(name);
Packit 78deda
        if (fP == NULL)
Packit 78deda
            pm_error("Could not open note file '%s' for output", name);
Packit 78deda
        
Packit 78deda
        fprintf(fP, "%s\n", note);
Packit 78deda
Packit 78deda
        pm_close(fP);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
int
Packit 78deda
main(int argc, const char ** argv) {
Packit 78deda
Packit 78deda
    struct cmdlineInfo cmdline;
Packit 78deda
    FILE * ifP;
Packit 78deda
    IPDB * pdbP;
Packit 78deda
    int status;
Packit 78deda
Packit 78deda
    pm_proginit(&argc, argv);
Packit 78deda
Packit 78deda
    parseCommandLine(argc, argv, &cmdline);
Packit 78deda
Packit 78deda
    ifP = pm_openr(cmdline.inputFileName);
Packit 78deda
Packit 78deda
    pdbP = ipdb_alloc(NULL);
Packit 78deda
    if (pdbP == NULL)
Packit 78deda
        pm_error("Could not allocate IPDB structure.");
Packit 78deda
Packit 78deda
    status = ipdbRead(pdbP, ifP, cmdline.verbose);
Packit 78deda
    if (status != 0)
Packit 78deda
        pm_error("Image header read error: %s.", ipdb_err(status));
Packit 78deda
Packit 78deda
    writeImgPam(pdbP, stdout);
Packit 78deda
Packit 78deda
    writeText(pdbP, cmdline.notefile);
Packit 78deda
Packit 78deda
    ipdb_free(pdbP);
Packit 78deda
Packit 78deda
    pm_close(ifP);
Packit 78deda
Packit 78deda
    return EXIT_SUCCESS;
Packit 78deda
}