|
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 |
¬efileSpec, 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 |
}
|