/* pnmtopalm.c - read a portable pixmap and write a Palm Tbmp file
 *
 * Program originally ppmtoTbmp.c by Ian Goldberg <iang@cs.berkeley.edu>
 *
 * Mods for multiple bits per pixel by George Caswell 
 * <tetsujin@sourceforge.net>
 *  and Bill Janssen <bill@janssen.org>
 * 
 * See LICENSE file for licensing information.
 *
 * Based on ppmtopuzz.c by Jef Poskanzer, from the netpbm-1mar1994 package.
 */

#include <stdio.h>
#include <assert.h>
#include <limits.h>

#include "pnm.h"
#include "palm.h"
#include "shhopt.h"
#include "mallocvar.h"

struct cmdline_info {
    /* All the information the user supplied in the command line,
       in a form easy for the program to use.
    */
    const char *inputFilespec;  /* Filespecs of input files */
    char *transparent;    /* -transparent value.  Null if unspec */
    unsigned int depth;     /* -depth value.  0 if unspec */
    unsigned int maxdepth;     /* -maxdepth value.  0 if unspec */
    unsigned int rle_compression;
    unsigned int scanline_compression;
    unsigned int verbose;
    unsigned int colormap;
    unsigned int offset;
};



static void
parseCommandLine(int argc, char ** argv,
                 struct cmdline_info *cmdlineP) {
/*----------------------------------------------------------------------------
   Note that the file spec array we return is stored in the storage that
   was passed to us as the argv array.
-----------------------------------------------------------------------------*/
    optStruct3 opt;  /* set by OPTENT3 */
    optEntry *option_def;
    unsigned int option_def_index;

    unsigned int transSpec, depthSpec, maxdepthSpec;

    MALLOCARRAY_NOFAIL(option_def, 100);

    option_def_index = 0;   /* incremented by OPTENTRY */
    OPTENT3(0, "transparent",      OPT_STRING, 
            &cmdlineP->transparent, &transSpec, 0);
    OPTENT3(0, "depth",            OPT_UINT, 
            &cmdlineP->depth,       &depthSpec, 0);
    OPTENT3(0, "maxdepth",         OPT_UINT, 
            &cmdlineP->maxdepth,    &maxdepthSpec, 0);
    OPTENT3(0, "rle_compression",  OPT_FLAG, 
            NULL,                   &cmdlineP->rle_compression, 0);
    OPTENT3(0, "scanline_compression", OPT_FLAG, 
            NULL,                   &cmdlineP->scanline_compression, 0);
    OPTENT3(0, "verbose",          OPT_FLAG, 
            NULL,                   &cmdlineP->verbose, 0);
    OPTENT3(0, "colormap",         OPT_FLAG, 
            NULL,                   &cmdlineP->colormap, 0);
    OPTENT3(0, "offset",           OPT_FLAG, 
            NULL,                   &cmdlineP->offset, 0);

    opt.opt_table = option_def;
    opt.short_allowed = FALSE; /* We have some short (old-fashioned) options */
    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */

    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
        /* Uses and sets argc, argv, and some of *cmdline_p and others. */

    if (depthSpec) {
        if (cmdlineP->depth != 1 && cmdlineP->depth != 2 
            && cmdlineP->depth != 4 && cmdlineP->depth != 8
            && cmdlineP->depth != 16)
            pm_error("invalid value for -depth: %u.  Valid values are "
                     "1, 2, 4, 8, and 16", cmdlineP->depth);
    } else
        cmdlineP->depth = 0;
    if (maxdepthSpec) {
        if (cmdlineP->maxdepth != 1 && cmdlineP->maxdepth != 2 
            && cmdlineP->maxdepth != 4 && cmdlineP->maxdepth != 8
            && cmdlineP->maxdepth != 16)
            pm_error("invalid value for -maxdepth: %u.  Valid values are "
                     "1, 2, 4, 8, and 16", cmdlineP->maxdepth);
    } else
        cmdlineP->maxdepth = 0;

    if (depthSpec && maxdepthSpec && 
        cmdlineP->depth > cmdlineP->maxdepth)
        pm_error("-depth value (%u) is greater than -maxdepth (%u) value.",
                 cmdlineP->depth, cmdlineP->maxdepth);

    if (!transSpec)
        cmdlineP->transparent = NULL;

    if (cmdlineP->offset &&
        (cmdlineP->rle_compression || cmdlineP->scanline_compression))
        pm_error("You can't specify -offset with -rle_compression or "
                 "-scanline_compression."); 

    if (argc-1 > 1)
        pm_error("This program takes at most 1 argument: the file name.  "
                 "You specified %d", argc-1);
    else if (argc-1 > 0) 
        cmdlineP->inputFilespec = argv[1];
    else
        cmdlineP->inputFilespec = "-";
}



static void
determinePalmFormat(unsigned int   const cols, 
                    unsigned int   const rows, 
                    xelval         const maxval, 
                    int            const format, 
                    xel **         const xels,
                    unsigned int   const specified_bpp,
                    unsigned int   const max_bpp, 
                    bool           const custom_colormap,
                    bool           const verbose,
                    unsigned int * const bppP, 
                    unsigned int * const maxmaxvalP, 
                    bool *         const directColorP, 
                    Colormap *     const colormapP) {

    if (PNM_FORMAT_TYPE(format) == PBM_TYPE) {
        if (custom_colormap)
            pm_error("You specified -colormap with a black and white input "
                     "image.  -colormap is valid only with color.");
        if (specified_bpp)
            *bppP = specified_bpp;
        else
            *bppP = 1;    /* no point in wasting bits */
        *maxmaxvalP = 1;
        *directColorP = FALSE;
        *colormapP = NULL;
        if (verbose)
            pm_message("output is black and white");
    } else if (PNM_FORMAT_TYPE(format) == PGM_TYPE) {
        /* we can usually handle this one, but may not have enough
           pixels.  So check... */
        if (custom_colormap)
            pm_error("You specified -colormap with a black and white input"
                     "image.  -colormap is valid only with color.");
        if (specified_bpp)
            *bppP = specified_bpp;
        else if (max_bpp && (maxval >= (1 << max_bpp)))
            *bppP = max_bpp;
        else if (maxval > 16)
            *bppP = 4;
        else {
            /* scale to minimum number of bpp needed */
            for (*bppP = 1;  (1 << *bppP) < maxval;  *bppP *= 2)
                ;
        }
        if (*bppP > 4)
            *bppP = 4;
        if (verbose)
            pm_message("output is grayscale %d bits-per-pixel", *bppP);
        *maxmaxvalP = PGM_OVERALLMAXVAL;
        *directColorP = FALSE;
        *colormapP = NULL;
    } else if (PNM_FORMAT_TYPE(format) == PPM_TYPE) {

        /* We assume that we only get a PPM if the image cannot be
           represented as PBM or PGM.  There are two options here: either
           8-bit with a colormap, either the standard one or a custom one,
           or 16-bit direct color.  In the 8-bit case, if "custom_colormap"
           is specified (not recommended by Palm) we will put in our own
           colormap; otherwise we will assume that the colors have been
           mapped to the default Palm colormap by appropriate use of
           pnmquant.  We try for 8-bit color first, since it works on
           more PalmOS devices. */

        if ((specified_bpp == 16) || 
            (specified_bpp == 0 && max_bpp == 16)) {
            /* we do the 16-bit direct color */
            *directColorP = TRUE;
            *colormapP = NULL;
            *bppP = 16;
        } else if (!custom_colormap) {
            /* standard indexed 8-bit color */
            *colormapP = palmcolor_build_default_8bit_colormap();
            *bppP = 8;
            if (((specified_bpp != 0) && (specified_bpp != 8)) ||
                ((max_bpp != 0) && (max_bpp < 8)))
                pm_error("Must use depth of 8 for color pixmap without "
                         "custom color table.");
            *directColorP = FALSE;
            if (verbose)
                pm_message("Output is color with default colormap at 8 bpp");
        } else {
            /* indexed 8-bit color with a custom colormap */
            *colormapP = 
                palmcolor_build_custom_8bit_colormap(rows, cols, xels);
            for (*bppP = 1; (1 << *bppP) < (*colormapP)->ncolors; *bppP *= 2);
            if (specified_bpp != 0) {
                if (specified_bpp >= *bppP)
                    *bppP = specified_bpp;
                else
                    pm_error("Too many colors for specified depth.  "
                             "Use pnmquant to reduce.");
            } else if ((max_bpp != 0) && (max_bpp < *bppP)) {
                pm_error("Too many colors for specified max depth.  "
                         "Use pnmquant to reduce.");
            }
            *directColorP = FALSE;
            if (verbose)
                pm_message("Output is color with custom colormap "
                           "with %d colors at %d bpp", 
                           (*colormapP)->ncolors, *bppP);
        }
        *maxmaxvalP = PPM_OVERALLMAXVAL;
    } else {
        pm_error("unknown format 0x%x on input file\n", (unsigned) format);
    }
}


static const char * 
formatName(int const format) {
    
    const char * retval;

    switch(PNM_FORMAT_TYPE(format)) {
    case PBM_TYPE: retval = "black and white"; break;
    case PGM_TYPE: retval = "grayscale";       break;
    case PPM_TYPE: retval = "color";           break;
    default:       retval = "???";             break;
    }
    return retval;
}

        

static void
findTransparentColor(char *         const colorSpec, 
                     pixval         const newMaxval,
                     bool           const directColor, 
                     pixval         const maxval, 
                     Colormap       const colormap,
                     xel *          const transcolorP, 
                     unsigned int * const transindexP) {

    *transcolorP = ppm_parsecolor(colorSpec, maxval);
    if (!directColor) {
        Color_s const temp_color = 
            ((((PPM_GETR(*transcolorP)*newMaxval) / maxval) << 16) 
             | (((PPM_GETG(*transcolorP)*newMaxval) / maxval) << 8)
             | ((PPM_GETB(*transcolorP)*newMaxval) / maxval));
        Color const found = 
            (bsearch(&temp_color,
                     colormap->color_entries, colormap->ncolors,
                     sizeof(Color_s), palmcolor_compare_colors));
        if (!found) {
            pm_error("Specified transparent color %s not found "
                     "in colormap.", colorSpec);
        } else
            *transindexP = (*found >> 24) & 0xFF;
    }
}



static void
writeHeader(unsigned int const cols,
            unsigned int const rows,
            unsigned int const rowbytes,
            unsigned int const bpp,
            bool         const transparent,
            unsigned int const transindex,
            bool         const rleCompression,
            bool         const scanlineCompression,
            bool         const colormap,
            bool         const directColor,
            unsigned int const nextDepthOffset) {
/*----------------------------------------------------------------------------
   Write the Tbmp header.  This is 16 bytes; it does not include
   colormap.
-----------------------------------------------------------------------------*/
    unsigned short flags;
    int version;

    flags = 0;  /* initial value */
    if (transparent)
        flags |= PALM_HAS_TRANSPARENCY_FLAG;
    if (colormap)
        flags |= PALM_HAS_COLORMAP_FLAG;
    if (rleCompression || scanlineCompression)
        flags |= PALM_IS_COMPRESSED_FLAG;
    if (directColor)
        flags |= PALM_DIRECT_COLOR;

    if (cols > USHRT_MAX)
        pm_error("Too many columns for Palm pixmap: %u", cols);
    pm_writebigshort(stdout, cols);    /* width */
    if (rows > USHRT_MAX)
        pm_error("Too many columns for Palm pixmap: %u", rows);
    pm_writebigshort(stdout, rows);    /* height */
    if (rows > USHRT_MAX)
        pm_error("Too many bytes per row for Palm pixmap: %u", rowbytes);
    pm_writebigshort(stdout, rowbytes);
    pm_writebigshort(stdout, flags);
    assert(bpp <= UCHAR_MAX);
    fputc(bpp, stdout);

    /* we need to mark this as version 1 if we use more than 1 bpp,
       version 2 if we use compression or transparency 
    */

    version = (transparent || rleCompression || scanlineCompression) ? 
        2 : (((bpp > 1) || colormap) ? 1 : 0);
    fputc(version, stdout);

    if (nextDepthOffset > USHRT_MAX)
        pm_error("Image too large for Palm pixmap");

    pm_writebigshort(stdout, nextDepthOffset);

    assert(transindex <= UCHAR_MAX);
    fputc(transindex, stdout);  /* transparent index */

    if (rleCompression)
        fputc(PALM_COMPRESSION_RLE, stdout);
    else if (scanlineCompression)
        fputc(PALM_COMPRESSION_SCANLINE, stdout);
    else
        fputc(PALM_COMPRESSION_NONE, stdout);

    pm_writebigshort(stdout, 0);   /* reserved by Palm */
}



static void
writeColormap(bool         const explicitColormap,
              Colormap     const colormap,
              bool         const directColor,
              unsigned int const bpp,
              bool         const transparent,
              xel          const transcolor,
              xelval       const maxval) {
              
    /* if there's a colormap, write it out */
    if (explicitColormap) {
        unsigned int row;
        if (!colormap)
            pm_error("Internal error: user specified -colormap, but we did "
                     "not generate a colormap.");
        qsort (colormap->color_entries, colormap->ncolors,
               sizeof(Color_s), palmcolor_compare_indices);
        pm_writebigshort( stdout, colormap->ncolors );
        for (row = 0;  row < colormap->ncolors; ++row)
            pm_writebiglong (stdout, colormap->color_entries[row]);
        qsort (colormap->color_entries, colormap->ncolors,
               sizeof(Color_s), palmcolor_compare_colors);
    }

    if (directColor) {
        if (bpp == 16) {
            fputc(5, stdout);   /* # of bits of red */
            fputc(6, stdout);   /* # of bits of green */    
            fputc(5, stdout);   /* # of bits of blue */
            fputc(0, stdout);   /* reserved by Palm */
        } else
            pm_error("Don't know how to create %d bit DirectColor bitmaps.", 
                     bpp);
        if (transparent) {
            fputc(0, stdout);
            fputc((PPM_GETR(transcolor) * 255) / maxval, stdout);
            fputc((PPM_GETG(transcolor) * 255) / maxval, stdout);
            fputc((PPM_GETB(transcolor) * 255) / maxval, stdout);
        } else
            pm_writebiglong(stdout, 0);     /* no transparent color */
    }
}



static void
writeSize(bool const scanline_compression,
          bool const rle_compression) {
    
    unsigned int compressedSize = 0;

    /* Quick hack -- at least some readers can read these images.  The
       full job requires knowing at this point in the processing how big
       the compressed image will be.
    */
    if (scanline_compression || rle_compression) 
        pm_writebigshort(stdout, compressedSize); 
}



static void
computeRawRowDirectColor(const xel *     const xelrow,
                         unsigned int    const cols,
                         xelval          const maxval,
                         unsigned char * const rowdata) {

    unsigned int col;
    unsigned char *outptr;
    
    for (col = 0, outptr = rowdata; col < cols; ++col) {
        unsigned int const color = 
            ((((PPM_GETR(xelrow[col])*31)/maxval) << 11) |
             (((PPM_GETG(xelrow[col])*63)/maxval) << 5) |
             ((PPM_GETB(xelrow[col])*31)/maxval));
        *outptr++ = (color >> 8) & 0xFF;
        *outptr++ = color & 0xFF;
    }
}



static void
computeRawRowNonDirect(const xel *     const xelrow,
                       unsigned int    const cols,
                       xelval          const maxval,
                       unsigned int    const bpp,
                       Colormap        const colormap,
                       unsigned int    const newMaxval,
                       unsigned char * const rowdata) {

    unsigned int col;
    unsigned char *outptr;
    unsigned char outbyte;
        /* Accumulated bits to be output */
    unsigned char outbit;
        /* The lowest bit number we want to access for this pixel */

    outbyte = 0x00;
    outptr = rowdata;

    for (outbit = 8 - bpp, col = 0; col < cols; ++col) {
        unsigned int color;
        if (!colormap) {
            /* we assume grayscale, and use simple scaling */
            color = (PNM_GET1(xelrow[col]) * newMaxval)/maxval;
            if (color > newMaxval)
                pm_error("oops.  Bug in color re-calculation code.  "
                         "color of %u.", color);
            color = newMaxval - color; /* note grayscale maps are inverted */
        } else {
            Color_s const temp_color =
                ((((PPM_GETR(xelrow[col])*newMaxval)/maxval)<<16) 
                 | (((PPM_GETG(xelrow[col])*newMaxval)/maxval)<<8)
                 | (((PPM_GETB(xelrow[col])*newMaxval)/maxval)));
            Color const found = (bsearch (&temp_color,
                                          colormap->color_entries, 
                                          colormap->ncolors,
                                          sizeof(Color_s), 
                                          palmcolor_compare_colors));
            if (!found) {
                pm_error("Color %d:%d:%d not found in colormap.  "
                         "Try using pnmquant to reduce the "
                         "number of colors.",
                         PPM_GETR(xelrow[col]), 
                         PPM_GETG(xelrow[col]), 
                         PPM_GETB(xelrow[col]));
            }
            color = (*found >> 24) & 0xFF;
        }

        if (color > newMaxval)
            pm_error("oops.  Bug in color re-calculation code.  "
                     "color of %u.", color);
        outbyte |= (color << outbit);
        if (outbit == 0) {
            /* Bit buffer is full.  Flush to to rowdata. */
            *outptr++ = outbyte;
            outbyte = 0x00;
            outbit = 8 - bpp;
        } else
            outbit -= bpp;
    }
    if ((cols % (8 / bpp)) != 0) {
        /* Flush bits remaining in the bit buffer to rowdata */
        *outptr++ = outbyte;
    }
}



static void
scanlineCompressAndWriteRow(const unsigned char * const rowdata,
                            unsigned int          const rowbytes,
                            const unsigned char * const lastrow) {

    unsigned int col;
    for (col = 0;  col < rowbytes;  col += 8) {
        unsigned int const limit = MIN(rowbytes - col, 8);

        unsigned char outbit;
        unsigned char map;
        /* mask indicating which of the next 8 pixels are different
           from the previous row, and therefore present in the
           file immediately following the map byte.
        */
        unsigned char *outptr;
        unsigned char differentPixels[8];
            
        for (outbit = 0, map = 0x00, outptr = differentPixels;
             outbit < limit;  
             ++outbit) {
            if (!lastrow 
                || (lastrow[col + outbit] != rowdata[col + outbit])) {
                map |= (1 << (7 - outbit));
                *outptr++ = rowdata[col + outbit];
            }
        }
        putc(map, stdout);
        fwrite(differentPixels, (outptr - differentPixels), 1, stdout);
    }
}



static void
rleCompressAndWriteRow(const unsigned char * const rowdata,
                       unsigned int          const rowbytes) {
        
    unsigned int col;
    /* we output a count of the number of bytes a value is
       repeated, followed by that byte value 
    */
    col = 0;
    while (col < rowbytes) {
        unsigned int repeatcount;
        for (repeatcount = 1;  
             repeatcount < (rowbytes - col) && repeatcount < 255;  
             ++repeatcount)
            if (rowdata[col+repeatcount] != rowdata[col])
                break;
        putc(repeatcount, stdout);
        putc(rowdata[col], stdout);
        col += repeatcount;
    }
}



static void
writeRowFromRawRowdata(const unsigned char *  const rowdata,
                       unsigned int           const rowbytes,
                       bool                   const scanlineCompression,
                       bool                   const rleCompression,
                       const unsigned char *  const lastrow) {
/*----------------------------------------------------------------------------
   Starting with a raw (uncompressed) Palm raster line, do the required
   compression and write the row to stdout.
-----------------------------------------------------------------------------*/
    if (scanlineCompression)
        scanlineCompressAndWriteRow(rowdata, rowbytes, lastrow);
    else if (rleCompression)
        rleCompressAndWriteRow(rowdata, rowbytes);
    else 
        fwrite(rowdata, rowbytes, 1, stdout);
}



static void
writeRow(const xel *     const xelrow,
         unsigned int    const cols,
         xelval          const maxval,
         unsigned int    const rowbytes,
         unsigned int    const bpp,
         unsigned int    const newMaxval,
         bool            const scanlineCompression,
         bool            const rleCompression,
         bool            const directColor,
         Colormap        const colormap,
         unsigned char * const rowdata,
         unsigned char * const lastrow) {
/*----------------------------------------------------------------------------
   Write one row of the raster to stdout.

   'rowdata' is a work buffer 'rowbytes' in size.

   'lastrow' is the contents of the row most recently written (useful in
   doing compression), or NULL if there is no previous row.
-----------------------------------------------------------------------------*/
    if (directColor)
        computeRawRowDirectColor(xelrow, cols, maxval, rowdata);
    
    else 
        computeRawRowNonDirect(xelrow, cols, maxval, bpp, colormap, newMaxval,
                               rowdata);

    writeRowFromRawRowdata(rowdata, rowbytes, 
                           scanlineCompression, rleCompression,
                           lastrow);
}



static void 
writeRaster(xel **       const xels,
            unsigned int const cols,
            unsigned int const rows,
            xelval       const maxval,
            unsigned int const rowbytes,
            unsigned int const bpp,
            unsigned int const newMaxval,
            bool         const scanlineCompression,
            bool         const rleCompression,
            bool         const directColor,
            Colormap     const colormap) {
    
    unsigned char * rowdata;
    unsigned char * lastrow;
    unsigned int row;

    MALLOCARRAY_NOFAIL(rowdata, rowbytes);
    MALLOCARRAY_NOFAIL(lastrow, rowbytes);

    /* And write out the data. */
    for (row = 0; row < rows; ++row) {
        writeRow(xels[row], cols, maxval, rowbytes, bpp, newMaxval,
                 scanlineCompression, rleCompression,
                 directColor, colormap, rowdata, row > 0 ? lastrow : NULL);

        memcpy(lastrow, rowdata, rowbytes);
    }
    free(lastrow);
    free(rowdata);
}



int 
main( int argc, char **argv ) {
    struct cmdline_info cmdline; 
    FILE* ifp;
    xel** xels;
    xel transcolor;
    unsigned int transindex;
    int rows, cols;
    unsigned int rowbytes;
    xelval maxval;
    int format;
    unsigned int bpp;
    bool directColor;
    unsigned int maxmaxval;
    unsigned int newMaxval;
    Colormap colormap;
    unsigned int nextDepthOffset;
        /* Offset from the beginning of the image we write to the beginning
           of the next one, assuming User writes another one following this
           one.
        */
    unsigned int padBytesRequired;
        /* Number of bytes of padding we need to put after the image in
           order to align properly for User to add the next image to the
           stream.
        */
    unsigned int i;

    /* Parse default params */
    pnm_init(&argc, argv);

    parseCommandLine(argc, argv, &cmdline);

    ifp = pm_openr(cmdline.inputFilespec);

    xels = pnm_readpnm (ifp, &cols, &rows, &maxval, &format);
    pm_close(ifp);

    if (cmdline.verbose)
        pm_message("Input is %dx%d %s, maxval %d", 
                   cols, rows, formatName(format), maxval);
    
    determinePalmFormat(cols, rows, maxval, format, xels, 
                        cmdline.depth, cmdline.maxdepth, cmdline.colormap, 
                        cmdline.verbose, 
                        &bpp, &maxmaxval, &directColor, &colormap);

    newMaxval = (1 << bpp) - 1;

    if (cmdline.transparent) 
        findTransparentColor(cmdline.transparent, newMaxval, directColor,
                             maxval, colormap, &transcolor, &transindex);
    else 
        transindex = 0;

    rowbytes = ((cols + (16 / bpp -1)) / (16 / bpp)) * 2;    
        /* bytes per row - always a word boundary */

    if (cmdline.offset) {
        /* Offset is measured in 4-byte words (double words in
           Intel/Microsoft terminology).  Account for header,
           colormap, and raster size and round up 
        */
        unsigned int const rasterSize = rowbytes * rows;
        unsigned int const headerSize = 16;
        unsigned int const colormapSize =  (directColor ? 8 : 0) +
            (cmdline.colormap ? (2 + colormap->ncolors * 4) : 0);
        nextDepthOffset = (rasterSize + headerSize + colormapSize + 3) / 4;
        padBytesRequired = 4 - (rasterSize + headerSize + colormapSize)%4;
    } else {
        nextDepthOffset = 0;
        padBytesRequired = 0;
    }
    writeHeader(cols, rows, rowbytes, bpp, !!cmdline.transparent,
                transindex,
                cmdline.rle_compression, cmdline.scanline_compression,
                cmdline.colormap, directColor, nextDepthOffset);

    writeColormap(cmdline.colormap, colormap, directColor, bpp, 
                  !!cmdline.transparent, transcolor, maxval);

    writeSize(cmdline.scanline_compression, cmdline.rle_compression);

    writeRaster(xels, cols, rows, maxval, rowbytes, bpp, newMaxval,
                cmdline.scanline_compression, cmdline.rle_compression,
                directColor, colormap);

    for (i = 0; i < padBytesRequired; ++i)
        fputc(0x00, stdout);

    return 0;
}
