Subversion Repositories ipe_artfile_utils

Compare Revisions

No changes between revisions

Regard whitespace Rev 1 → Rev 2

/trunk/Makefile
0,0 → 1,33
 
all: ipe_artfile_unpacker ipe_artfile_packer
 
ipe_artfile_unpacker: ipe_artfile_unpacker.c ipe_artfile_unpacker_ipe16.c ipe_artfile_unpacker_ipe32.c ipe16_lzw_decoder.c ipe32_lzw_decoder.c ipe16_bmpexport.c ipe32_bmpexport.c utils.c
gcc -std=c99 -Wall -c ipe_artfile_unpacker.c -o ipe_artfile_unpacker.o
gcc -std=c99 -Wall -c ipe_artfile_unpacker_ipe16.c -o ipe_artfile_unpacker_ipe16.o
gcc -std=c99 -Wall -c ipe_artfile_unpacker_ipe32.c -o ipe_artfile_unpacker_ipe32.o
gcc -std=c99 -Wall -c ipe16_lzw_decoder.c -o ipe16_lzw_decoder.o
gcc -std=c99 -Wall -c ipe32_lzw_decoder.c -o ipe32_lzw_decoder.o
gcc -std=c99 -Wall -c ipe16_bmpexport.c -o ipe16_bmpexport.o
gcc -std=c99 -Wall -c ipe32_bmpexport.c -o ipe32_bmpexport.o
gcc -std=c99 -Wall -c utils.c -o utils.o
gcc -o ipe_artfile_unpacker ipe_artfile_unpacker.o ipe_artfile_unpacker_ipe16.o ipe_artfile_unpacker_ipe32.o ipe16_lzw_decoder.o ipe32_lzw_decoder.o ipe16_bmpexport.o ipe32_bmpexport.o utils.o
rm *.o
 
ipe_artfile_packer: ipe_artfile_packer.c ipe_artfile_packer_ipe16_ba.c ipe_artfile_packer_ipe16_pip.c ipe_artfile_packer_ipe32.c ipe16_lzw_encoder.c ipe32_lzw_encoder.c ipe16_bmpimport.c ipe32_bmpimport.c utils.c
gcc -std=c99 -Wall -c ipe_artfile_packer.c -o ipe_artfile_packer.o
gcc -std=c99 -Wall -c ipe_artfile_packer_ipe16_ba.c -o ipe_artfile_packer_ipe16_ba.o
gcc -std=c99 -Wall -c ipe_artfile_packer_ipe16_pip.c -o ipe_artfile_packer_ipe16_pip.o
gcc -std=c99 -Wall -c ipe_artfile_packer_ipe32.c -o ipe_artfile_packer_ipe32.o
gcc -std=c99 -Wall -c ipe16_lzw_encoder.c -o ipe16_lzw_encoder.o
gcc -std=c99 -Wall -c ipe32_lzw_encoder.c -o ipe32_lzw_encoder.o
gcc -std=c99 -Wall -c ipe16_bmpimport.c -o ipe16_bmpimport.o
gcc -std=c99 -Wall -c ipe32_bmpimport.c -o ipe32_bmpimport.o
gcc -std=c99 -Wall -c utils.c -o utils.o
gcc -lm -o ipe_artfile_packer ipe_artfile_packer.o ipe_artfile_packer_ipe16_ba.o ipe_artfile_packer_ipe16_pip.o ipe_artfile_packer_ipe32.o ipe16_lzw_encoder.c ipe32_lzw_encoder.o ipe16_bmpimport.o ipe32_bmpimport.o utils.o
rm *.o
 
clean:
rm -f *.o
# TODO: if [ -f ... ] then rm
rm ipe_artfile_packer
rm ipe_artfile_unpacker
/trunk/TODO
0,0 → 1,6
 
Future: Allow several input arguments (e.g. *.ART)
 
Packer: Like the unpacker, implement a simulation mode (so that no ART file is written)
 
Please see also : grep -r "// TODO"
/trunk/artfile_overview.txt
0,0 → 1,56
 
ART FILES BY IMAGINATION PILOTS
Technical overview by Daniel Marschall 15 Feb 2018
 
 
VERSION 1.x (16-bit era, "IPE16")
=================================
 
Version 1.0 For game: "Blown Away" (1994)
* File signature "Art" + 20 NUL bytes
* Image names up to 23 characters
(unsure if the 23th byte must be a zero terminator)
* LZW implementation: GIF standard implementation
- variable code length between 9 and 12 bits
- clear code (256)
- end code (257)
* Pictures can only be 8-bit
* Each picture can either be LZW compressed (code "P")
or uncompressed (code "p", lower case)
* Decompressed/raw data: Top-down pixel data
* Attached: Optional palette (R,G,B), uncompressed.
(Sprites usually do not have a palette, so the game
will use the palette of the parent picture)
 
Version 1.1 For games: "Panic in the Park" (1995)
"Where's Waldo? At the Circus" (1995)
* Codes for compression/raw are now "Q" and "q" instead of
"P" and "p"
* Each picture has now a relative X- and Y-offsets for 'fine-tuning'.
These offsets cannot be negative, though.
 
VERSION 2.x (32-bit era, "IPE32")
=================================
 
Version 2.0 For games: "Where's Waldo? Exploring Geography" (1996)
"Eraser Turnabout" (1997)
"Virtual K'Nex" (1998)
* File signature "ART_DATA"
* Image names up to 8 characters;
no zero termination required after the 8 characters.
* LZW implementation by Mark R. Nelson and Shawn M. Regan
- variable code length between 9 and 13 bits
- clear code (256)
- end code (257)
* Pictures are divided into chunks. Each chunk can be compressed or
uncompressed. Limitations:
- Each compressed chunk (except the last one) must decode into
exactly 16382 (0x3FFE) bytes of uncompressed data.
- The size of the compressed data of a chunk must not exceed the
size of the original uncompressed data.
* The size of each chunk (raw or compressed) is limited
to max. 32767 Bytes (0x7FFF).
* Decompressed/raw data: Windows Bitmap (without file header);
therefore, theoretically every color depth/compression/etc. can be used.
For 8-bit pictures, the palette is included in the Bitmap.
* Re-using of parent palette is not possible.
Property changes:
Added: svn:mime-type
+text/plain
\ No newline at end of property
/trunk/artfile_structure_10.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes:
Added: svn:mime-type
+image/png
\ No newline at end of property
/trunk/artfile_structure_11.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes:
Added: svn:mime-type
+image/png
\ No newline at end of property
/trunk/artfile_structure_20.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes:
Added: svn:mime-type
+image/png
\ No newline at end of property
/trunk/bitmap.h
0,0 → 1,51
#ifndef __inc__bitmap
#define __inc__bitmap
 
#include <stdint.h>
 
// "BM"
#define BI_SIGNATURE 0x4D42
 
// see https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx
#define BYTE uint8_t
#define WORD uint16_t
#define DWORD uint32_t
#define LONG int32_t
 
// These parts were extracted from WinGDI.h and WinDef.h
 
#define BI_RGB 0
 
#pragma pack(push,2)
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER,*LPBITMAPFILEHEADER,*PBITMAPFILEHEADER;
#pragma pack(pop)
 
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER,*LPBITMAPINFOHEADER,*PBITMAPINFOHEADER;
 
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD,*LPRGBQUAD;
 
#endif // #ifndef __inc__bitmap
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/build_stable
0,0 → 1,25
#!/bin/bash
 
# Get the directory of this script (also works with symlinks)
# http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
 
cd "$DIR"
dirname=$( basename "$( pwd )" )
tmpfile=$( mktemp )
 
FILENAME="stable.tar.gz"
 
cd ..
tar cvfz "$tmpfile" --exclude="$dirname"/"$FILENAME" --exclude ".svn" -- "$dirname"
 
# otherwise: "the file has changed while reading"
mv "$tmpfile" "$dirname"/"$FILENAME"
 
chmod 644 "$dirname"/"$FILENAME"
Property changes:
Added: svn:executable
+*
\ No newline at end of property
/trunk/ipe16_artfile.h
0,0 → 1,85
/**
* ART files for Imagination Pilots Entertainment 16-bit games (IPE16)
* - Blown Away - The Interactive Game by Imagination Pilots (BA)
* - Panic in the Park - The Interactive Game by Imagination Pilots (PiP)
* - Where's Waldo? At the Circus (Waldo1)
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe16_artfile
#define __inc__ipe16_artfile
 
#include <stdint.h>
 
#define BA_COMPRESSIONTYPE_LZW 'P'
#define BA_COMPRESSIONTYPE_NONE 'p'
 
// PANIC.EXE offset 0x44365: Choice between "Q" and "q"
#define PIP_COMPRESSIONTYPE_LZW 'Q'
#define PIP_COMPRESSIONTYPE_NONE 'q'
 
#define IPE16_PALETTETYPE_ATTACHED 'X'
 
// Pictures with the type 'C' do not have a palette.
// They use the palette of their parent picture where they are embedded in
#define IPE16_PALETTETYPE_PARENT 'C'
 
#define IPE16_NAME_SIZE 23
 
#define IPE16_MAGIC_ART "Art"
#define IPE16_MAGIC_DUMMY '?'
 
#pragma pack(push, 1)
 
typedef struct tagIpe16FileHeader {
char magic[IPE16_NAME_SIZE]; // always "Art\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" (ignored by the game)
char dummy; // always '?' (ignored by the game)
uint32_t numHeaderEntries; // number of headers including this file header (number of follow-up Ipe16PictureEntryHeader entries plus one)
uint32_t totalFileSize; // total file size including this header (ignored by the game)
} Ipe16FileHeader;
 
typedef struct tagIpe16PictureEntryHeader {
char name[IPE16_NAME_SIZE]; // zero terminated string. case sensitive
char paletteType; // 'X' (0x58) = RGB palette attached
// 'C' (0x43) = no palette attached (for embedded picture, use palette of parent picture)
uint32_t offset; // offset to the picture (PictureHeader)
uint32_t size; // size of the picture (PictureHeader + picture data + optional palette)
} Ipe16PictureEntryHeader;
 
typedef struct tagBAPictureHeader {
char compressionType; // Compression type of the follow-up data (top down pixel data; the palette won't be compressed)
// 'P' (0x50, upper case) = LZW compression, more precisely:
// The LZW variant of the GIF specification,
// but without splitting the output data into chunks
// 'p' (0x70, lower case) = No compression
uint16_t width; // width of the picture
uint16_t height; // height of the picture
} BAPictureHeader;
 
typedef struct tagPipPictureHeader {
char compressionType; // Compression type of the follow-up data (top down pixel data; the palette won't be compressed)
// 'Q' (0x51, upper case) = LZW compression, more precisely:
// The LZW variant of the GIF specification,
// but without splitting the output data into chunks
// 'q' (0x71, lower case) = No compression
uint16_t offsetX; // Additional offsets for 'fine-tuning'
uint16_t offsetY;
uint16_t width; // width of the picture
uint16_t height; // height of the picture
} PipPictureHeader;
 
typedef struct tagIpe16ColorTableEntry {
uint8_t r;
uint8_t g;
uint8_t b;
} Ipe16ColorTableEntry;
 
typedef struct tagIpe16ColorTable {
Ipe16ColorTableEntry colors[256];
} Ipe16ColorTable;
 
#pragma pack(pop)
 
#endif // #ifndef __inc__ipe16_artfile
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe16_bmpexport.c
0,0 → 1,106
/**
* Bitmap Export for Imagination Pilots Entertainment 16-bit games (IPE16)
* - Blown Away - The Interactive Game by Imagination Pilots (BA)
* - Panic in the Park - The Interactive Game by Imagination Pilots (PiP)
* - Where's Waldo? At the Circus (Waldo1)
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
**/
 
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
 
#include "ipe16_bmpexport.h"
 
#define BMP_LINE_PADDING 4
 
// Windows 98 does not support top-down bitmaps, so we need to flip everything
#define USE_BOTTOMUP
 
RGBQUAD ipe16_rgb_to_rgbquad(Ipe16ColorTableEntry cte) {
RGBQUAD ret;
ret.rgbRed = cte.r;
ret.rgbGreen = cte.g;
ret.rgbBlue = cte.b;
ret.rgbReserved = 0;
return ret;
}
 
void ipe16_write_bmp(FILE* output, unsigned int width, unsigned int height, unsigned char* imagedata, size_t imagedata_len, Ipe16ColorTable ct) {
 
#ifdef USE_BOTTOMUP
const size_t bmpDataSize = width*height;
unsigned char* bmpData = (unsigned char*)malloc(bmpDataSize);
assert(bmpData != NULL);
int h;
for (h=0; h<height; ++h) {
int idx_dest = (height-1)-h;
int idx_src = h;
assert(idx_src*width >= 0);
assert(idx_dest*width >= 0);
memcpy(bmpData+idx_dest*width, imagedata+idx_src*width, width);
}
imagedata = bmpData;
#endif
 
// Each line must be padded to a multiple of 4
int pad = (BMP_LINE_PADDING - (width % BMP_LINE_PADDING)) % BMP_LINE_PADDING;
int newwidth = width+pad;
int newsize = newwidth * height;
unsigned char* padded_imagedata = (unsigned char*)malloc(newsize);
int i;
for (i=0; i<height; ++i) {
int offset = newwidth*i;
memcpy(&padded_imagedata[offset], imagedata, width);
memset(&padded_imagedata[offset+width], 0, pad);
imagedata += width;
}
 
// Color table in a bitmap is BGR0, while Blown Away uses RGB
const unsigned int NUM_COLORS = sizeof(ct.colors)/sizeof(ct.colors[0]);
RGBQUAD rgba_colortable[NUM_COLORS];
for (i=0; i<NUM_COLORS; ++i) {
rgba_colortable[i] = ipe16_rgb_to_rgbquad(ct.colors[i]);
}
 
BITMAPFILEHEADER bfh;
bfh.bfType = BI_SIGNATURE;
bfh.bfSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizeof(rgba_colortable)+newsize;
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizeof(rgba_colortable);
fwrite(&bfh, sizeof(bfh), 1, output);
 
BITMAPINFOHEADER bih;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = width;
#ifdef USE_BOTTOMUP
bih.biHeight = height; // (positive = "bottom-up"-Bitmap)
#else
bih.biHeight = -height; // (negative = "top-down"-Bitmap)
#endif
bih.biPlanes = 1;
bih.biBitCount = 8;
bih.biCompression = BI_RGB;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
fwrite(&bih, sizeof(bih), 1, output);
 
// Color table
fwrite(rgba_colortable, sizeof(rgba_colortable), 1, output);
 
// Image data
fwrite(padded_imagedata, newsize, 1, output);
 
free(padded_imagedata);
#ifdef USE_BOTTOMUP
free(bmpData);
#endif
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe16_bmpexport.h
0,0 → 1,23
/**
* Bitmap Export for Imagination Pilots Entertainment 16-bit games (IPE16)
* - Blown Away - The Interactive Game by Imagination Pilots (BA)
* - Panic in the Park - The Interactive Game by Imagination Pilots (PiP)
* - Where's Waldo? At the Circus (Waldo1)
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe16_bmpexport
#define __inc__ipe16_bmpexport
 
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
 
#include "bitmap.h"
#include "ipe16_artfile.h"
 
void ipe16_write_bmp(FILE* output, unsigned int width, unsigned int height, unsigned char* imagedata, size_t imagedata_len, Ipe16ColorTable ct);
 
#endif // #ifndef __inc__ipe16_bmpexport
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe16_bmpimport.c
0,0 → 1,108
/**
* Bitmap Import for Imagination Pilots Entertainment 16-bit games (IPE16)
* - Blown Away - The Interactive Game by Imagination Pilots (BA)
* - Panic in the Park - The Interactive Game by Imagination Pilots (PiP)
* - Where's Waldo? At the Circus (Waldo1)
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
**/
 
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
 
#include "bitmap.h"
#include "ipe16_bmpimport.h"
 
Ipe16ColorTableEntry rgbquad_to_ipe16_rgb(RGBQUAD rq) {
Ipe16ColorTableEntry cte;
cte.r = rq.rgbRed;
cte.g = rq.rgbGreen;
cte.b = rq.rgbBlue;
return cte;
}
 
bool ipe16_bmp_import(FILE* fibBitmap, Ipe16BmpImportData* result) {
BITMAPFILEHEADER bitmapFileHeader;
BITMAPINFOHEADER bitmapInfoHeader;
 
#define EXIT_ERROR(msg) { sprintf(result->error, msg); return false; }
 
fseek(fibBitmap, 0, SEEK_SET);
 
if (!fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, fibBitmap) ||
(bitmapFileHeader.bfType != BI_SIGNATURE) ||
!fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, fibBitmap)) {
EXIT_ERROR("Not a bitmap file");
}
 
if (bitmapInfoHeader.biCompression != BI_RGB) {
EXIT_ERROR("At the moment, only uncompressed files can be read.");
}
 
if (bitmapInfoHeader.biBitCount != 8) {
EXIT_ERROR("The color depth has to be 8 bpp.");
}
 
Ipe16ColorTable* ct = malloc(sizeof(Ipe16ColorTable));
#define NUM_COLORS 256
RGBQUAD rgba_colortable[NUM_COLORS];
if (!fread(&rgba_colortable, sizeof(rgba_colortable), 1, fibBitmap)) {
EXIT_ERROR("Error reading color table.");
}
int i;
for (i=0; i<NUM_COLORS; ++i) {
Ipe16ColorTableEntry x;
x = rgbquad_to_ipe16_rgb(rgba_colortable[i]);
ct->colors[i] = x;
}
 
fseek(fibBitmap, bitmapFileHeader.bfOffBits, SEEK_SET);
 
const uint32_t realwidth = bitmapInfoHeader.biWidth;
const uint32_t realheight = abs(bitmapInfoHeader.biHeight);
const size_t bmpDataSize = realwidth*realheight;
unsigned char* bmpData = (unsigned char*)malloc(bmpDataSize);
assert(bmpData != NULL);
const int padded_width = (bitmapInfoHeader.biWidth + 3) & ~0x03; // http://stackoverflow.com/a/2022194/3544341
unsigned char* bmpLine = (unsigned char*)malloc(padded_width);
assert(bmpLine != NULL);
int h;
for (h=0; h<realheight; ++h) {
if (fread(bmpLine, padded_width, 1, fibBitmap) != 1) {
free(bmpLine);
free(bmpData);
free(ct);
EXIT_ERROR("Error while reading pixel data.");
}
int idx;
if (bitmapInfoHeader.biHeight > 0) {
// "bottom-up" (most usual)
// Convert to "top-down":
idx = (realheight-1)-h;
} else {
// "top-down"
idx = h;
}
assert(idx*realwidth >= 0);
memcpy(bmpData+idx*realwidth, bmpLine, realwidth);
}
free(bmpLine);
 
result->colorTable = ct;
result->bmpData = bmpData;
result->bmpDataSize = bmpDataSize;
result->width = realwidth;
result->height = realheight;
result->error[0] = 0;
return true;
}
 
void ipe16_free_bmpimport_result(Ipe16BmpImportData *res) {
if (res->colorTable) free(res->colorTable);
if (res->bmpData) free(res->bmpData);
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe16_bmpimport.h
0,0 → 1,34
/**
* Bitmap Import for Imagination Pilots Entertainment 16-bit games (IPE16)
* - Blown Away - The Interactive Game by Imagination Pilots (BA)
* - Panic in the Park - The Interactive Game by Imagination Pilots (PiP)
* - Where's Waldo? At the Circus (Waldo1)
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe16_bmpimport
#define __inc__ipe16_bmpimport
 
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
 
#include "bitmap.h"
#include "ipe16_artfile.h"
 
typedef struct tagIpe16BmpImportData {
Ipe16ColorTable* colorTable;
unsigned char* bmpData;
size_t bmpDataSize;
unsigned int width;
unsigned int height;
char error[255];
} Ipe16BmpImportData;
 
bool ipe16_bmp_import(FILE* fibBitmap, Ipe16BmpImportData* result);
void ipe16_free_bmpimport_result(Ipe16BmpImportData *res);
 
#endif // #ifndef __inc__ipe16_bmpimport
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe16_lzw_decoder.c
0,0 → 1,179
/**
* LZW Decoder for Imagination Pilots Entertainment 16-bit games (IPE16)
* - Blown Away - The Interactive Game by Imagination Pilots (BA)
* - Panic in the Park - The Interactive Game by Imagination Pilots (PiP)
* - Where's Waldo? At the Circus (Waldo1)
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
*
* The code is based on "Cross platform GIF source code" (c) L. Patrick
* http://www.cs.usyd.edu.au/~graphapp/package/src/libgif/gif.c
* It was simplified and modified to encode IPE16-LZW instead of GIF-LZW.
* The game uses exactly the compressed stream as defined in the GIF standard,
* but the compressed stream is not divided into chunks.
**/
 
#include "ipe16_lzw_decoder.h"
#include "utils.h"
 
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
 
Ipe16LZWDecoder* new_ipe16lzw_decoder(void) {
return (Ipe16LZWDecoder*)app_zero_alloc(sizeof(Ipe16LZWDecoder));
}
 
void del_ipe16lzw_decoder(Ipe16LZWDecoder* decoder) {
free(decoder);
}
 
void ipe16lzw_init_decoder(Ipe16LZWDecoder* decoder) {
decoder->running_code = FIRST_CODE;
decoder->running_bits = LZ_MIN_BITS;
decoder->max_code_plus_one = 1 << decoder->running_bits;
decoder->shift_state = 0;
decoder->shift_data = 0;
 
int i;
for (i = 0; i <= LZ_MAX_CODE; i++) {
decoder->prefix[i] = NO_SUCH_CODE;
}
}
 
int ipe16lzw_read_code(FILE* inFile, Ipe16LZWDecoder* decoder) {
int code;
unsigned char next_byte;
static int code_masks[] = {
0x0000, 0x0001, 0x0003, 0x0007,
0x000f, 0x001f, 0x003f, 0x007f,
0x00ff, 0x01ff, 0x03ff, 0x07ff,
0x0fff
};
 
while (decoder->shift_state < decoder->running_bits) {
next_byte = read_byte(inFile);
decoder->shift_data |=
((unsigned long) next_byte) << decoder->shift_state;
decoder->shift_state += 8;
}
 
code = decoder->shift_data & code_masks[decoder->running_bits];
 
decoder->shift_data >>= decoder->running_bits;
decoder->shift_state -= decoder->running_bits;
 
if (++decoder->running_code > decoder->max_code_plus_one
&& decoder->running_bits < LZ_MAX_BITS) {
decoder->max_code_plus_one <<= 1;
decoder->running_bits++;
}
 
return code;
}
 
static int ipe16lzw_trace_prefix(unsigned int* prefix, int code, int clear_code) {
int i = 0;
 
while (code > clear_code && i++ <= LZ_MAX_CODE) {
code = prefix[code];
}
return code;
}
 
// We don't do unsigned, because we want to have <0 as error result
/*unsigned*/ int ipe16lzw_decode(FILE* inFile, Ipe16LZWDecoder* decoder, unsigned char* output, int outputLength) {
int i = 0, j;
int current_code;
int current_prefix;
int stack_ptr = 0;
int prev_code = NO_SUCH_CODE;
unsigned char* stack;
unsigned int* prefix;
unsigned int* suffix;
unsigned int bytes_written = 0;
 
ipe16lzw_init_decoder(decoder);
 
prefix = decoder->prefix;
suffix = decoder->suffix;
stack = decoder->stack;
 
/* Pop the stack */
while (stack_ptr != 0 && i < outputLength) {
output[i++] = stack[--stack_ptr];
//if (i > bytes_written) bytes_written = i;
++bytes_written;
}
 
while (i < outputLength) {
current_code = ipe16lzw_read_code(inFile, decoder);
 
if (current_code == END_CODE) {
if (i != outputLength - 1) // || decoder->pixel_count != 0
return -1; /* unexpected eof */
i++;
} else if (current_code == CLEAR_CODE) {
for (j = 0; j <= LZ_MAX_CODE; j++) {
prefix[j] = NO_SUCH_CODE;
}
decoder->running_code = FIRST_CODE;
decoder->running_bits = LZ_MIN_BITS;
decoder->max_code_plus_one = 1 << decoder->running_bits;
prev_code = NO_SUCH_CODE;
} else {
if (current_code < CLEAR_CODE) {
output[i++] = current_code;
//if (i > bytes_written) bytes_written = i;
++bytes_written;
} else {
if ((current_code < 0) || (current_code > LZ_MAX_CODE))
return -2; /* image defect */
if (prefix[current_code] == NO_SUCH_CODE) {
if (current_code == decoder->running_code - 2) {
current_prefix = prev_code;
suffix[decoder->running_code - 2]
= stack[stack_ptr++]
= ipe16lzw_trace_prefix(prefix, prev_code, CLEAR_CODE);
} else {
return -3; /* image defect */
}
} else {
current_prefix = current_code;
}
j = 0;
while (j++ <= LZ_MAX_CODE && current_prefix > CLEAR_CODE && current_prefix <= LZ_MAX_CODE) {
stack[stack_ptr++] = suffix[current_prefix];
current_prefix = prefix[current_prefix];
}
if (j >= LZ_MAX_CODE || current_prefix > LZ_MAX_CODE)
return -4; /* image defect */
 
stack[stack_ptr++] = current_prefix;
 
while (stack_ptr != 0 && i < outputLength) {
output[i++] = stack[--stack_ptr];
//if (i > bytes_written) bytes_written = i;
++bytes_written;
}
}
if (prev_code != NO_SUCH_CODE) {
if ((decoder->running_code < 2) ||
(decoder->running_code > LZ_MAX_CODE+2))
return -5; /* image defect */
prefix[decoder->running_code - 2] = prev_code;
 
if (current_code == decoder->running_code - 2) {
suffix[decoder->running_code - 2]
= ipe16lzw_trace_prefix(prefix, prev_code, CLEAR_CODE);
} else {
suffix[decoder->running_code - 2]
= ipe16lzw_trace_prefix(prefix, current_code, CLEAR_CODE);
}
}
prev_code = current_code;
}
}
 
return bytes_written;
}
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe16_lzw_decoder.h
0,0 → 1,48
/**
* LZW Decoder for Imagination Pilots Entertainment 16-bit games (IPE16)
* - Blown Away - The Interactive Game by Imagination Pilots (BA)
* - Panic in the Park - The Interactive Game by Imagination Pilots (PiP)
* - Where's Waldo? At the Circus (Waldo1)
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
*
* The code is based on "Cross platform GIF source code" (c) L. Patrick
* http://www.cs.usyd.edu.au/~graphapp/package/src/libgif/gif.c
* It was simplified and modified to encode IPE16-LZW instead of GIF-LZW.
* The game uses exactly the compressed stream as defined in the GIF standard,
* but the compressed stream is not divided into chunks.
**/
 
#ifndef __inc__ipe16_lzw_decoder
#define __inc__ipe16_lzw_decoder
 
#include <stdio.h>
#include <stdbool.h>
 
#define LZ_MIN_BITS 9
#define LZ_MAX_BITS 12
 
#define LZ_MAX_CODE 4095 /* Largest 12 bit code */
#define NO_SUCH_CODE 4098 /* Impossible code = empty */
 
#define CLEAR_CODE 256
#define END_CODE 257
#define FIRST_CODE 258
 
typedef struct tagIpe16LZWDecoder {
int running_code;
int running_bits;
int max_code_plus_one;
int shift_state;
unsigned long shift_data;
unsigned char stack[LZ_MAX_CODE+1];
unsigned int suffix[LZ_MAX_CODE+1];
unsigned int prefix[LZ_MAX_CODE+1];
} Ipe16LZWDecoder;
 
Ipe16LZWDecoder* new_ipe16lzw_decoder(void);
void del_ipe16lzw_decoder(Ipe16LZWDecoder* decoder);
/*unsigned*/ int ipe16lzw_decode(FILE* outFile, Ipe16LZWDecoder *decoder, unsigned char *input, int inputLength);
 
#endif // #ifndef __inc__ipe16_lzw_decoder
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe16_lzw_encoder.c
0,0 → 1,140
/**
* LZW Encoder for Imagination Pilots Entertainment 16-bit games (IPE16)
* - Blown Away - The Interactive Game by Imagination Pilots (BA)
* - Panic in the Park - The Interactive Game by Imagination Pilots (PiP)
* - Where's Waldo? At the Circus (Waldo1)
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
*
* The code is based on "Cross platform GIF source code" (c) L. Patrick
* http://www.cs.usyd.edu.au/~graphapp/package/src/libgif/gif.c
* It was simplified and modified to encode IPE16-LZW instead of GIF-LZW.
* The game uses exactly the compressed stream as defined in the GIF standard,
* but the compressed stream is not divided into chunks.
**/
 
#include "ipe16_lzw_encoder.h"
#include "utils.h"
 
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
 
Ipe16LZWEncoder* new_ipe16lzw_encoder(void) {
return (Ipe16LZWEncoder*)app_zero_alloc(sizeof(Ipe16LZWEncoder));
}
 
void del_ipe16lzw_encoder(Ipe16LZWEncoder* encoder) {
free(encoder);
}
 
void ipe16lzw_write_code(FILE* outFile, Ipe16LZWEncoder* encoder, int code) {
if (code == FLUSH_OUTPUT) {
/* write all remaining data */
while (encoder->shift_state > 0) {
fputc(encoder->shift_data & 0xff, outFile);
encoder->shift_data >>= 8;
encoder->shift_state -= 8;
}
encoder->shift_state = 0;
} else {
encoder->shift_data |= ((long) code) << encoder->shift_state;
encoder->shift_state += encoder->running_bits;
 
while (encoder->shift_state >= 8) {
/* write full bytes */
fputc(encoder->shift_data & 0xff, outFile);
encoder->shift_data >>= 8;
encoder->shift_state -= 8;
}
}
 
if (encoder->running_code >= encoder->max_code_plus_one && code <= LZ_MAX_CODE) {
encoder->max_code_plus_one = 1 << ++encoder->running_bits;
}
}
 
static void ipe16lzw_clear_hash_table(unsigned long* hash_table) {
int i;
for (i=0; i<HT_SIZE; i++) {
hash_table[i] = 0xFFFFFFFFL;
}
}
 
void ipe16lzw_init_encoder(Ipe16LZWEncoder* encoder) {
encoder->running_code = FIRST_CODE;
encoder->running_bits = LZ_MIN_BITS;
encoder->max_code_plus_one = 1 << encoder->running_bits;
encoder->shift_state = 0;
encoder->shift_data = 0;
}
 
static int ipe16lzw_hash_key(unsigned long key) {
return ((key >> 12) ^ key) & HT_KEY_MASK;
}
 
static int ipe16lzw_lookup_hash(unsigned long* hash_table, unsigned long key) {
int hkey = ipe16lzw_hash_key(key);
unsigned long htkey;
 
while ((htkey = HT_GET_KEY(hash_table[hkey])) != 0xFFFFFL) {
if (key == htkey) {
return HT_GET_CODE(hash_table[hkey]);
}
hkey = (hkey + 1) & HT_KEY_MASK;
}
 
return -1;
}
 
static void ipe16lzw_add_hash_entry(unsigned long* hash_table, unsigned long key, int code) {
int hkey = ipe16lzw_hash_key(key);
 
while (HT_GET_KEY(hash_table[hkey]) != 0xFFFFFL) {
hkey = (hkey + 1) & HT_KEY_MASK;
}
hash_table[hkey] = HT_PUT_KEY(key) | HT_PUT_CODE(code);
}
 
void ipe16lzw_encode(FILE* outFile, Ipe16LZWEncoder* encoder, unsigned char* input, int inputLength) {
int i = 0, current_code, new_code;
unsigned long new_key;
unsigned char pixval;
/* Init stuff */
ipe16lzw_init_encoder(encoder);
ipe16lzw_clear_hash_table(encoder->hash_table);
ipe16lzw_write_code(outFile, encoder, CLEAR_CODE);
 
if (inputLength == 0) return;
current_code = input[i++];
 
while (i < inputLength) {
pixval = input[i++]; /* Fetch next pixel from stream */
 
new_key = (((unsigned long) current_code) << 8) + pixval;
if ((new_code = ipe16lzw_lookup_hash(encoder->hash_table, new_key)) >= 0) {
current_code = new_code;
} else {
ipe16lzw_write_code(outFile, encoder, current_code);
current_code = pixval;
 
if (encoder->running_code >= LZ_MAX_CODE) {
ipe16lzw_write_code(outFile, encoder, CLEAR_CODE);
encoder->running_code = FIRST_CODE;
encoder->running_bits = LZ_MIN_BITS;
encoder->max_code_plus_one = 1 << encoder->running_bits;
ipe16lzw_clear_hash_table(encoder->hash_table);
} else {
/* Put this unique key with its relative code in hash table */
ipe16lzw_add_hash_entry(encoder->hash_table, new_key, encoder->running_code++);
}
}
}
 
/* Flush */
ipe16lzw_write_code(outFile, encoder, current_code);
ipe16lzw_write_code(outFile, encoder, END_CODE);
ipe16lzw_write_code(outFile, encoder, FLUSH_OUTPUT);
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe16_lzw_encoder.h
0,0 → 1,53
/**
* LZW Encoder for Imagination Pilots Entertainment 16-bit games (IPE16)
* - Blown Away - The Interactive Game by Imagination Pilots (BA)
* - Panic in the Park - The Interactive Game by Imagination Pilots (PiP)
* - Where's Waldo? At the Circus (Waldo1)
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
*
* The code is based on "Cross platform GIF source code" (c) L. Patrick
* http://www.cs.usyd.edu.au/~graphapp/package/src/libgif/gif.c
* It was simplified and modified to encode IPE16-LZW instead of GIF-LZW.
* The game uses exactly the compressed stream as defined in the GIF standard,
* but the compressed stream is not divided into chunks.
**/
 
#ifndef __inc__ipe16_lzw_encoder
#define __inc__ipe16_lzw_encoder
 
#include <stdio.h>
#include <stdbool.h>
 
#define LZ_MIN_BITS 9
 
#define LZ_MAX_CODE 4095 /* Largest 12 bit code */
#define FLUSH_OUTPUT 4096 /* Impossible code = flush */
 
#define HT_SIZE 8192 /* 13 bit hash table size */
#define HT_KEY_MASK 0x1FFF /* 13 bit key mask */
 
#define CLEAR_CODE 256
#define END_CODE 257
#define FIRST_CODE 258
 
#define HT_GET_KEY(x) ((x) >> 12)
#define HT_GET_CODE(x) ((x) & 0x0FFF)
#define HT_PUT_KEY(x) ((x) << 12)
#define HT_PUT_CODE(x) ((x) & 0x0FFF)
 
typedef struct tagIpe16LZWEncoder {
int running_code;
int running_bits;
int max_code_plus_one;
int shift_state;
unsigned long shift_data;
unsigned long hash_table[HT_SIZE];
} Ipe16LZWEncoder;
 
Ipe16LZWEncoder* new_ipe16lzw_encoder(void);
void del_ipe16lzw_encoder(Ipe16LZWEncoder* encoder);
void ipe16lzw_encode(FILE* outFile, Ipe16LZWEncoder* encoder, unsigned char* input, int inputLength);
 
#endif // #ifndef __inc__ipe16_lzw_encoder
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe32_artfile.h
0,0 → 1,49
/**
* ART files for Imagination Pilots Entertainment 32-bit games (IPE32)
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2018
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe32_artfile
#define __inc__ipe32_artfile
 
#include <stdint.h>
 
#define IPE32_COMPRESSIONTYPE_LZW 0
#define IPE32_COMPRESSIONTYPE_NONE 1
 
#define IPE32_NAME_SIZE 8
 
#define IPE32_MAGIC_ART "ART_DATA"
 
#pragma pack(push, 1)
 
typedef struct tagIpe32FileHeader {
char magic[IPE32_NAME_SIZE]; // always "ART_DATA"
uint32_t totalHeaderSize; // size of all headers (file header and all picture headers). headerSize/16 = numberPictures
uint32_t reserved; // always 0
} Ipe32FileHeader;
 
typedef struct tagIpe32PictureEntryHeader {
char name[IPE32_NAME_SIZE];
uint32_t offset; // offset to the picture (Ipe32PictureHeader)
uint32_t uncompressedSize; // size of the picture (picture data + palette)
} Ipe32PictureEntryHeader;
 
/*
typedef struct tagIpe32PictureChunk {
unsigned compressionType : 1; // Compression type of the follow-up data
// 0 = LZW-like compression (special implementation)
// 1 = None
unsigned chunkDataSize : 15; // size of the chunk data
//char data[];
} Ipe32PictureChunk;
*/
 
#pragma pack(pop)
 
#endif // #ifndef __inc__ipe32_artfile
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe32_bmpexport.c
0,0 → 1,29
/**
* Bitmap Export for Imagination Pilots Entertainment 32-bit games (IPE32)
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
**/
 
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
 
#include "bitmap.h"
#include "ipe32_bmpexport.h"
 
void ipe32_write_bmp(FILE* output, unsigned char* imagedata, size_t imagedata_len) {
BITMAPFILEHEADER bh={0};
bh.bfType = BI_SIGNATURE;
bh.bfSize = sizeof(bh) + imagedata_len;
bh.bfReserved1 = 0;
bh.bfReserved2 = 0;
bh.bfOffBits = 0x436;
 
fwrite(&bh, 1, sizeof(bh), output);
fwrite(imagedata, 1, imagedata_len, output);
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe32_bmpexport.h
0,0 → 1,20
/**
* Bitmap Export for Imagination Pilots Entertainment 32-bit games (IPE32)
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe32_bmpexport
#define __inc__ipe32_bmpexport
 
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
 
void ipe32_write_bmp(FILE* output, unsigned char* imagedata, size_t imagedata_len);
 
#endif // #ifndef __inc__ipe32_bmpexport
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe32_bmpimport.c
0,0 → 1,83
/**
* Bitmap Import for Imagination Pilots Entertainment 32-bit games (IPE32)
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
**/
 
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
 
#include "bitmap.h"
#include "utils.h"
#include "ipe32_bmpimport.h"
 
#define BI_BITFIELDS 3
 
bool bmp_has_gap1(BITMAPFILEHEADER bitmapFileHeader, BITMAPINFOHEADER bitmapInfoHeader) {
int expected = 0;
expected = sizeof(bitmapFileHeader) + bitmapInfoHeader.biSize;
 
if (bitmapInfoHeader.biCompression == BI_BITFIELDS) {
expected += 3*sizeof(DWORD);
}
 
int numColorEntries;
if (bitmapInfoHeader.biClrUsed == 0) {
if ((bitmapInfoHeader.biBitCount == 1) || (bitmapInfoHeader.biBitCount == 4) || (bitmapInfoHeader.biBitCount == 8)) {
numColorEntries = pow(2, bitmapInfoHeader.biBitCount);
} else {
numColorEntries = 0;
}
} else {
numColorEntries = bitmapInfoHeader.biClrUsed;
}
expected += sizeof(RGBQUAD)*numColorEntries;
 
return bitmapFileHeader.bfOffBits != expected;
}
 
bool ipe32_bmp_import(FILE* fibBitmap, Ipe32BmpImportData* result) {
BITMAPFILEHEADER bitmapFileHeader;
BITMAPINFOHEADER bitmapInfoHeader;
 
#define EXIT_ERROR(msg) { sprintf(result->error, msg); return false; }
 
fseek(fibBitmap, 0, SEEK_SET);
 
if (!fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, fibBitmap) ||
(bitmapFileHeader.bfType != BI_SIGNATURE) ||
!fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, fibBitmap)) {
EXIT_ERROR("Not a bitmap file");
}
 
if (bmp_has_gap1(bitmapFileHeader, bitmapInfoHeader)) {
EXIT_ERROR("Picture may not have a gap between header and bitmap data");
}
 
// TODO: Find out if the game has limitations in regards to compression, top-down/bottom-up, Bitmap version etc.
/*
if (bitmapInfoHeader.biCompression != BI_RGB) {
EXIT_ERROR("At the moment, only uncompressed files can be read.");
}
 
if (bitmapInfoHeader.biBitCount != 8) {
EXIT_ERROR("The color depth has to be 8 bpp.");
}
*/
 
fseek(fibBitmap, sizeof(bitmapFileHeader), SEEK_SET);
 
result->error[0] = 0;
result->dataSize = file_size(fibBitmap)-sizeof(bitmapFileHeader);
return true;
}
 
void ipe32_free_bmpimport_result(Ipe32BmpImportData *res) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe32_bmpimport.h
0,0 → 1,27
/**
* Bitmap Import for Imagination Pilots Entertainment 32-bit games (IPE32)
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe32_bmpimport
#define __inc__ipe32_bmpimport
 
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
 
typedef struct tagIpe32BmpImportData {
size_t dataSize;
char error[255];
} Ipe32BmpImportData;
 
bool ipe32_bmp_import(FILE* fibBitmap, Ipe32BmpImportData* result);
void ipe32_free_bmpimport_result(Ipe32BmpImportData *res);
 
#endif // #ifndef __inc__ipe32_bmpimport
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe32_lzw_decoder.c
0,0 → 1,159
/**
* LZW Decoder for Imagination Pilots Entertainment 32-bit games (IPE32)
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2018
* Revision: 2018-02-15
*
* Based on : Basic LZW Data Compression program published in DDJ October 1989 issue.
* by Mark R. Nelson
* http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/DDJ/1989/8910/8910b/8910b.htm
* Updated by: Shawn M. Regan, January 1990
* http://mirror.bagelwood.com/textfiles/computers/regan.lst
* Updated by: Daniel Marschall, 11 February 2018
* https://misc.daniel-marschall.de/code/c/lzw.c
* Changed for IPE32: - Simplified
* - Thread safe
* - MAX_BITS = 13
**/
 
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
 
#include "utils.h"
#include "ipe32_lzw_decoder.h"
 
#define INIT_BITS 9
#define MAX_BITS 13 /* Do not exceed 14 with this program */
 
#if MAX_BITS == 14 /* Set the table size. Must be a prime */
#define TABLE_SIZE 18041 /* number somewhat larger than 2^MAX_BITS.*/
#elif MAX_BITS == 13
#define TABLE_SIZE 9029
#else
#define TABLE_SIZE 5021
#endif
 
#define CLEAR_TABLE 256 /* Code to flush the string table */
#define TERMINATOR 257 /* To mark EOF Condition, instead of MAX_VALUE */
#define FIRST_CODE 258 /* First available code for code_value table */
 
#define MAXVAL(n) (( 1 <<( n )) -1) /* max_value formula macro */
 
unsigned char* decode_string(Ipe32LZWDecoder *decoder, unsigned char *buffer, unsigned int code) {
int i=0;
 
while (code > 255) {
*buffer++ = decoder->append_character[code];
code = decoder->prefix_code[code];
if (i++ >= 4000) {
// printf("Error during code expansion\n");
return NULL;
}
}
*buffer=code;
return(buffer);
}
 
unsigned input_code(Ipe32LZWDecoder *decoder, unsigned char* lzwInputBuffer, int* inputBufferPos) {
unsigned int return_value;
 
while (decoder->input_bit_count <= 24) {
decoder->input_bit_buffer |= lzwInputBuffer[(*inputBufferPos)] << (24 - decoder->input_bit_count);
(*inputBufferPos)++;
decoder->input_bit_count += 8;
}
return_value=decoder->input_bit_buffer >> (32-decoder->num_bits);
decoder->input_bit_buffer <<= decoder->num_bits;
decoder->input_bit_count -= decoder->num_bits;
return(return_value);
}
 
void reset_input_buffer(Ipe32LZWDecoder *decoder) {
decoder->input_bit_count = 0;
decoder->input_bit_buffer = 0;
}
 
void ipe32lzw_reset_decoder(Ipe32LZWDecoder *decoder) {
decoder->num_bits = INIT_BITS;
decoder->max_code = MAXVAL(decoder->num_bits); /* Initialize max_value & max_code */
 
// Make sure the input buffer is correctly flushed
reset_input_buffer(decoder);
}
 
void ipe32lzw_init_decoder(Ipe32LZWDecoder *decoder) {
decoder->prefix_code = malloc(TABLE_SIZE*sizeof(unsigned int));
decoder->append_character = malloc(TABLE_SIZE*sizeof(unsigned char));
ipe32lzw_reset_decoder(decoder);
}
 
// Returns: Bytes written or -1 when an error occurs
int ipe32lzw_decode(Ipe32LZWDecoder *decoder, unsigned char* outputBuffer, const size_t outpufBufferSize, unsigned char* lzwInputBuffer, const size_t maxReadBytes) {
unsigned int next_code=FIRST_CODE;
unsigned int new_code;
unsigned int old_code;
int character;
int clear_flag=1; /* Need to clear the code value array */
unsigned char *string;
 
int inputBufferPos = 0;
int outputBufferPos = 0;
#define OUTPUT(code) { if (outputBufferPos == outpufBufferSize) return -1; outputBuffer[outputBufferPos++] = code; }
 
ipe32lzw_reset_decoder(decoder);
 
while (1) {
if (inputBufferPos == maxReadBytes) return -1;
if ((new_code=input_code(decoder, lzwInputBuffer, &inputBufferPos)) == TERMINATOR) break;
 
if (clear_flag) { /* Initialize or Re-Initialize */
clear_flag=0;
old_code=new_code; /* The next three lines have been moved */
character=old_code; /* from the original */
OUTPUT(old_code);
continue;
}
if (new_code == CLEAR_TABLE) { /* Clear string table */
clear_flag=1;
decoder->num_bits=INIT_BITS;
next_code=FIRST_CODE;
decoder->max_code = MAXVAL(decoder->num_bits);
continue;
}
if (new_code >= next_code) { /* Check for string+char+string */
*decoder->decode_stack=character;
string = decode_string(decoder, decoder->decode_stack+1,old_code);
} else {
string = decode_string(decoder, decoder->decode_stack,new_code);
}
if (string == NULL) return -1;
 
character = *string; /* Output decoded string in reverse */
while (string >= decoder->decode_stack) {
OUTPUT(*string--);
}
 
if (next_code <= decoder->max_code) { /* Add to string table if not full */
decoder->prefix_code[next_code]=old_code;
decoder->append_character[next_code++]=character;
if (next_code == decoder->max_code && decoder->num_bits < MAX_BITS) {
decoder->max_code = MAXVAL(++decoder->num_bits);
}
}
old_code=new_code;
}
 
return outputBufferPos;
}
 
void ipe32lzw_free_decoder(Ipe32LZWDecoder *decoder) {
free(decoder->prefix_code);
free(decoder->append_character);
}
 
Ipe32LZWDecoder* new_ipe32lzw_decoder(void) {
return (Ipe32LZWDecoder*)app_zero_alloc(sizeof(Ipe32LZWDecoder));
}
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe32_lzw_decoder.h
0,0 → 1,38
/**
* LZW Decoder for Imagination Pilots Entertainment 32-bit games (IPE32)
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2018
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe32_lzw_decoder
#define __inc__ipe32_lzw_decoder
 
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
 
typedef struct tagIpe32LZWDecoder {
unsigned int *prefix_code; /* This array holds the prefix codes */
unsigned char *append_character; /* This array holds the appended chars */
unsigned char decode_stack[4000]; /* This array holds the decoded string */
 
int num_bits; /* Starting with 9 bit codes */
int max_code; /* old MAX_CODE */
 
int input_bit_count;
uint32_t input_bit_buffer;
} Ipe32LZWDecoder;
 
// Returns: Bytes written or -1 when an error occurs
int ipe32lzw_decode(Ipe32LZWDecoder *decoder, unsigned char* outputBuffer, const size_t outpufBufferSize, unsigned char* lzwInputBuffer, const size_t maxReadBytes);
 
Ipe32LZWDecoder* new_ipe32lzw_decoder(void);
void ipe32lzw_init_decoder(Ipe32LZWDecoder *decoder);
void ipe32lzw_free_decoder(Ipe32LZWDecoder *decoder);
 
#endif // #ifndef __inc__ipe32_lzw_decoder
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe32_lzw_decoder.old
Cannot display: file marked as a binary type.
svn:mime-type = application/x-trash
Property changes:
Added: svn:mime-type
+application/x-trash
\ No newline at end of property
/trunk/ipe32_lzw_encoder.c
0,0 → 1,192
/**
* LZW Encoder for Imagination Pilots Entertainment 32-bit games (IPE32)
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2018
* Revision: 2018-02-15
*
* Based on : Basic LZW Data Compression program published in DDJ October 1989 issue.
* by Mark R. Nelson
* http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/DDJ/1989/8910/8910b/8910b.htm
* Updated by: Shawn M. Regan, January 1990
* http://mirror.bagelwood.com/textfiles/computers/regan.lst
* Updated by: Daniel Marschall, 11 February 2018
* https://misc.daniel-marschall.de/code/c/lzw.c
* Changed for IPE32: - Simplified
* - Thread safe
* - MAX_BITS = 13
**/
 
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
 
#include "utils.h"
#include "ipe32_lzw_encoder.h"
 
#define INIT_BITS 9
#define MAX_BITS 13 /* Do not exceed 14 with this program */
#define HASHING_SHIFT (MAX_BITS - 8)
 
#if MAX_BITS == 14 /* Set the table size. Must be a prime */
#define TABLE_SIZE 18041 /* number somewhat larger than 2^MAX_BITS.*/
#elif MAX_BITS == 13
#define TABLE_SIZE 9029
#else
#define TABLE_SIZE 5021
#endif
 
#define CLEAR_TABLE 256 /* Code to flush the string table */
#define TERMINATOR 257 /* To mark EOF Condition, instead of MAX_VALUE */
#define FIRST_CODE 258 /* First available code for code_value table */
#define CHECK_TIME 100 /* Check comp ratio every CHECK_TIME chars input */
 
#define MAXVAL(n) (( 1 <<( n )) -1) /* max_value formula macro */
 
unsigned int find_match(Ipe32LZWEncoder *encoder, int hash_prefix, unsigned int hash_character) {
int index, offset;
 
index = (hash_character << HASHING_SHIFT) ^ hash_prefix;
 
offset = (index == 0) ? 1 : TABLE_SIZE - index;
 
while (1) {
if (encoder->code_value[index] == -1) {
return(index);
}
if (encoder->prefix_code[index] == hash_prefix && encoder->append_character[index] == hash_character) {
return(index);
}
index -= offset;
if (index < 0) {
index += TABLE_SIZE;
}
}
}
 
void output_code(Ipe32LZWEncoder *encoder, unsigned int code, unsigned char* outBuf, size_t* compressedPos) {
encoder->output_bit_buffer |= (uint32_t) code << (32 - encoder->num_bits - encoder->output_bit_count);
encoder->output_bit_count += encoder->num_bits;
while (encoder->output_bit_count >= 8) {
outBuf[(*compressedPos)] = encoder->output_bit_buffer >> 24; // putc(output_bit_buffer >> 24, output);
(*compressedPos)++;
 
encoder->output_bit_buffer <<= 8;
encoder->output_bit_count -= 8;
encoder->bytes_out++;
}
}
 
void reset_output_buffer(Ipe32LZWEncoder *encoder) {
encoder->output_bit_count = 0;
encoder->output_bit_buffer = 0;
}
 
void ipe32lzw_reset_encoder(Ipe32LZWEncoder *encoder) {
encoder->num_bits = INIT_BITS;
encoder->max_code = MAXVAL(encoder->num_bits); /* Initialize max_value & max_code */
encoder->bytes_in = 0;
encoder->bytes_out = 0;
encoder->checkpoint = CHECK_TIME;
 
// For some reason, the output buffer doesn't get correctly flushed when
// a new compression is started. So I made the static symbols global
// and clear them here.
reset_output_buffer(encoder);
}
 
// Returns: Bytes written, or -1 if compression failed
int ipe32lzw_encode(Ipe32LZWEncoder *encoder, unsigned char* compressedData, const size_t compressedBufLen, unsigned char* uncompressedData, const size_t uncompressedSize) {
unsigned int next_code=FIRST_CODE;
unsigned int index;
int i, /* All purpose integer */
ratio_new, /* New compression ratio as a percentage */
ratio_old=100; /* Original ratio at 100% */
 
ipe32lzw_reset_encoder(encoder);
 
for (i=0; i<TABLE_SIZE; ++i) { /* Initialize the string table first */
encoder->code_value[i]=-1;
}
 
/* Get the first code */
if (compressedBufLen == 0) return -1;
unsigned int string_code = uncompressedData[0]; //string_code=getc(input);
size_t uncompressedPos = 1;
size_t compressedPos = 0;
#define OUTPUT(code) { if (compressedPos == compressedBufLen) return -1; output_code(encoder,code,compressedData,&compressedPos); }
 
/* This is the main compression loop. Notice when the table is full we try
* to increment the code size. Only when num_bits == MAX_BITS and the code
* value table is full do we start to monitor the compression ratio.
*/
while (uncompressedPos < uncompressedSize) { // while((character=getc(input)) != (unsigned)EOF) {
unsigned int character = uncompressedData[uncompressedPos++];
 
++encoder->bytes_in;
index=find_match(encoder,string_code,character);
if (encoder->code_value[index] != -1) {
string_code=encoder->code_value[index];
} else {
if (next_code <= encoder->max_code) {
encoder->code_value[index]=next_code++;
encoder->prefix_code[index]=string_code;
encoder->append_character[index]=character;
}
OUTPUT(string_code); /* Send out current code */
string_code=character;
if (next_code > encoder->max_code) { /* Is table Full? */
if (encoder->num_bits < MAX_BITS) { /* Any more bits? */
encoder->max_code = MAXVAL(++encoder->num_bits); /* Increment code size then */
} else if (encoder->bytes_in > encoder->checkpoint) { /* At checkpoint? */
if (encoder->num_bits == MAX_BITS) {
ratio_new = encoder->bytes_out*100/encoder->bytes_in; /* New compression ratio */
if (ratio_new > ratio_old) { /* Has ratio degraded? */
OUTPUT(CLEAR_TABLE); /* YES,flush string table */
encoder->num_bits=INIT_BITS;
next_code=FIRST_CODE; /* Reset to FIRST_CODE */
encoder->max_code = MAXVAL(encoder->num_bits); /* Re-Initialize this stuff */
encoder->bytes_in = encoder->bytes_out = 0;
ratio_old = 100; /* Reset compression ratio */
for (i=0; i<TABLE_SIZE; ++i) { /* Reset code value array */
encoder->code_value[i]=-1;
}
} else { /* NO, then save new */
ratio_old = ratio_new; /* compression ratio */
}
}
encoder->checkpoint = encoder->bytes_in + CHECK_TIME; /* Set new checkpoint */
}
}
}
}
OUTPUT(string_code); /* Output the last code */
if (next_code == encoder->max_code) { /* Handles special case for bit */
++encoder->num_bits; /* increment on EOF */
}
OUTPUT(TERMINATOR); /* Output the end of buffer code */
OUTPUT(0); /* Flush the output buffer */
OUTPUT(0);
OUTPUT(0);
 
return compressedPos;
}
 
void ipe32lzw_init_encoder(Ipe32LZWEncoder *encoder) {
/* The three buffers for the compression phase. */
encoder->code_value=malloc(TABLE_SIZE*sizeof(unsigned int));
encoder->prefix_code=malloc(TABLE_SIZE*sizeof(unsigned int));
encoder->append_character=malloc(TABLE_SIZE*sizeof(unsigned char));
ipe32lzw_reset_encoder(encoder);
}
 
void ipe32lzw_free_encoder(Ipe32LZWEncoder *encoder) {
free(encoder->code_value); /* Needed only for compression */
free(encoder->prefix_code);
free(encoder->append_character);
}
 
Ipe32LZWEncoder* new_ipe32lzw_encoder(void) {
return (Ipe32LZWEncoder*)app_zero_alloc(sizeof(Ipe32LZWEncoder));
}
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe32_lzw_encoder.h
0,0 → 1,39
/**
* LZW Encoder for Imagination Pilots Entertainment 32-bit games (IPE32)
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2018
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe32_lzw_encoder
#define __inc__ipe32_lzw_encoder
 
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
 
typedef struct tagIpe32LZWEncoder {
int *code_value; /* This is the code value array */
unsigned int *prefix_code; /* This array holds the prefix codes */
unsigned char *append_character; /* This array holds the appended chars */
 
int num_bits; /* Starting with 9 bit codes */
uint32_t bytes_in,bytes_out; /* Used to monitor compression ratio */
int max_code; /* old MAX_CODE */
uint32_t checkpoint; /* For compression ratio monitoring */
 
int output_bit_count;
uint32_t output_bit_buffer;
} Ipe32LZWEncoder;
 
// Returns: Bytes written, or -1 if compression failed
int ipe32lzw_encode(Ipe32LZWEncoder *encoder, unsigned char* compressedData, const size_t compressedBufLen, unsigned char* uncompressedData, const size_t uncompressedSize);
 
Ipe32LZWEncoder* new_ipe32lzw_encoder(void);
void ipe32lzw_init_encoder(Ipe32LZWEncoder *encoder);
void ipe32lzw_free_encoder(Ipe32LZWEncoder *encoder);
 
#endif // #ifndef __inc__ipe32_lzw_encoder
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe_artfile_packer
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Added: svn:executable
+*
\ No newline at end of property
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/ipe_artfile_packer.c
0,0 → 1,107
/**
* ART file packer by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Blown Away - The Interactive Game by Imagination Pilots
* - Panic in the Park - The Interactive Game by Imagination Pilots
* - Where's Waldo? At the Circus
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* Revision: 2018-02-21
**/
 
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <getopt.h>
 
#include "ipe_artfile_packer_ipe16_ba.h"
#include "ipe_artfile_packer_ipe16_pip.h"
#include "ipe_artfile_packer_ipe32.h"
 
#define VERSION "2018-02-21"
 
void print_syntax() {
fprintf(stderr, "Syntax: [-v] -t <type> -i <input dir> -o <output artfile>\n");
fprintf(stderr, " -t : ba (Blown Away)\n");
fprintf(stderr, " pip (Panic in the Park)\n");
fprintf(stderr, " waldo (Where's Waldo? At the Circus)\n");
fprintf(stderr, " waldo2 (Where's Waldo? Exploring Geography)\n");
fprintf(stderr, " eraser (Eraser Turnabout)\n");
fprintf(stderr, " knex (Virtual K'Nex)\n");
fprintf(stderr, " -v : verbose output\n");
}
 
#define GAME_UNKNOWN 0
#define GAME_BA 1
#define GAME_PIP 2
#define GAME_WALDO_CIRCUS 3
#define GAME_WALDO_GEOGRAPHY 4
#define GAME_ERASER 5
#define GAME_KNEX 6
 
int main(int argc, char *argv[]) {
int verbosity = 0;
char* szSrcFolder = "";
char* szArtFile = "";
int c;
 
#define PRINT_SYNTAX { print_syntax(); return 0; }
 
int game = GAME_UNKNOWN;
 
while ((c = getopt(argc, argv, "Vvi:o:t:")) != -1) {
switch (c) {
case 't':
if (strcmp(optarg, "ba") == 0) game = GAME_BA;
if (strcmp(optarg, "pip") == 0) game = GAME_PIP;
if (strcmp(optarg, "waldo") == 0) game = GAME_WALDO_CIRCUS;
if (strcmp(optarg, "waldo2") == 0) game = GAME_WALDO_GEOGRAPHY;
if (strcmp(optarg, "eraser") == 0) game = GAME_ERASER;
if (strcmp(optarg, "knex") == 0) game = GAME_KNEX;
break;
case 'v':
verbosity++;
break;
case 'V':
fprintf(stdout, "IPE artfile packer, revision %s\n", VERSION);
return 0;
case 'i':
szSrcFolder = optarg;
break;
case 'o':
szArtFile = optarg;
break;
case '?':
PRINT_SYNTAX;
break;
}
}
if (optind < argc) PRINT_SYNTAX;
if (game == GAME_UNKNOWN) {
fprintf(stderr, "Please specify the game\n");
PRINT_SYNTAX;
}
 
if (strlen(szArtFile) == 0) PRINT_SYNTAX;
if (strlen(szSrcFolder) == 0) PRINT_SYNTAX;
 
FILE* fobArt = fopen(szArtFile, "wb");
 
switch (game) {
case GAME_BA:
return ba_pack_art(szSrcFolder, fobArt, verbosity) ? 0 : 1;
break;
case GAME_PIP:
case GAME_WALDO_CIRCUS:
return pip_pack_art(szSrcFolder, fobArt, verbosity) ? 0 : 1;
break;
case GAME_WALDO_GEOGRAPHY:
case GAME_ERASER:
case GAME_KNEX:
return ipe32_pack_art(szSrcFolder, fobArt, verbosity) ? 0 : 1;
break;
}
}
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe_artfile_packer.dev
0,0 → 1,182
[Project]
FileName=ipe_artfile_packer.dev
Name=Artfile Packer
UnitCount=12
Type=1
Ver=2
ObjFiles=
Includes=
Libs=
PrivateResource=
ResourceIncludes=
MakeIncludes=
Compiler=
CppCompiler=
Linker=
IsCpp=0
Icon=
ExeOutput=
ObjectOutput=
OverrideOutput=0
OverrideOutputName=ipe_artfile_packer.exe
HostApplication=
Folders=
CommandLine=
UseCustomMakefile=0
CustomMakefile=
IncludeVersionInfo=0
SupportXPThemes=0
CompilerSet=0
CompilerSettings=0000000000000000000000000
LogOutput=
LogOutputEnabled=0
 
[VersionInfo]
Major=0
Minor=1
Release=1
Build=1
LanguageID=1033
CharsetID=1252
CompanyName=
FileVersion=0.1.1.1
FileDescription=Developed using the Dev-C++ IDE
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=
AutoIncBuildNr=0
SyncProduct=0
 
[Unit1]
FileName=ipe_artfile_packer.c
CompileCpp=0
Folder=BA Artfile Packer
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit2]
FileName=ipe16_artfile.h
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit3]
FileName=ipe32_artfile.h
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit4]
FileName=bitmap.h
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit5]
FileName=ipe32_lzw_encoder.c
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit7]
FileName=ipe16_bmpimport.c
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit8]
FileName=utils.c
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit9]
FileName=ipe32_bmpimport.c
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit9]
FileName=ipe_artfile_packer_ipe16_ba.c
CompileCpp=0
Folder=BA Artfile Packer
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit10]
FileName=ipe_artfile_packer_ipe16_pip.c
CompileCpp=0
Folder=BA Artfile Packer
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit11]
FileName=ipe_artfile_packer_ipe32.c
CompileCpp=0
Folder=BA Artfile Packer
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit12]
FileName=ipe32_bmpimport.c
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit6]
FileName=ipe16_lzw_encoder.c
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
/trunk/ipe_artfile_packer.exe
Cannot display: file marked as a binary type.
svn:mime-type = application/x-msdos-program
Property changes:
Added: svn:mime-type
+application/x-msdos-program
\ No newline at end of property
/trunk/ipe_artfile_packer.layout
0,0 → 1,48
[Editor_0]
CursorCol=13
CursorRow=3
TopLine=1
LeftChar=1
[Editor_2]
CursorCol=1
CursorRow=47
TopLine=16
LeftChar=1
[Editor_1]
CursorCol=20
CursorRow=69
TopLine=1
LeftChar=1
[Editor_6]
CursorCol=39
CursorRow=83
TopLine=64
LeftChar=1
[Editor_7]
CursorCol=7
CursorRow=17
TopLine=1
LeftChar=1
[Editor_3]
CursorCol=21
CursorRow=19
TopLine=1
LeftChar=1
[Editors]
Order=0
Focused=0
[Editor_5]
CursorCol=1
CursorRow=8
TopLine=83
LeftChar=1
[Editor_8]
CursorCol=42
CursorRow=97
TopLine=120
LeftChar=1
[Editor_4]
CursorCol=13
CursorRow=144
TopLine=120
LeftChar=1
/trunk/ipe_artfile_packer_ipe16_ba.c
0,0 → 1,167
/**
* ART file packer by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Blown Away - The Interactive Game by Imagination Pilots
* Revision: 2018-02-15
**/
 
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
 
#include "ipe_artfile_packer_ipe16_ba.h"
 
#include "ipe16_artfile.h"
#include "ipe16_bmpimport.h"
#include "ipe16_lzw_encoder.h"
 
#define MAX_FILE 256
 
bool ba_pack_art(const char* szSrcFolder, FILE* fobArt, const int verbosity) {
bool bEverythingOK = true;
 
char szIndexFilename[MAX_FILE];
sprintf(szIndexFilename, "%s/index.txt", szSrcFolder);
FILE* fitIndex = fopen(szIndexFilename, "rt");
if (!fitIndex) {
fprintf(stderr, "Cannot open %s\n", szIndexFilename);
return false;
}
 
#define MAX_LINE 1024
char line[MAX_LINE];
int cItems = 0;
while (fgets(line, sizeof(line), fitIndex)) {
if (strlen(line) == 0) continue;
++cItems;
}
if (verbosity >= 1) printf("%s contains %d entries\n", szIndexFilename, cItems); // TODO: don't print double /
 
Ipe16FileHeader bfh;
memset(&bfh, 0x00, sizeof(bfh));
strcpy(bfh.magic, IPE16_MAGIC_ART);
bfh.dummy = IPE16_MAGIC_DUMMY;
bfh.numHeaderEntries = cItems+1;
 
Ipe16PictureEntryHeader peh[cItems];
memset(&peh, 0x00, sizeof(peh));
 
BAPictureHeader ph[cItems];
memset(&ph, 0x00, sizeof(ph));
 
// We need to write the (still empty) headers, so we can use ftell() to determine the offsets correctly
// These headers are currently just dummies. They will be rewritten after all pictures are processed
fwrite(&bfh, sizeof(bfh), 1, fobArt);
fwrite(&peh, sizeof(peh), 1, fobArt);
 
fseek(fitIndex, 0, SEEK_SET);
int curItem = 0;
Ipe16LZWEncoder* lzwEncoder = NULL;
while (fgets(line, sizeof(line), fitIndex)) {
// If something fails, we discard the item, but continue in building the file!
#define FAIL_CONTINUE { memset(&peh[curItem], 0x00, sizeof(peh[curItem])); bEverythingOK=false; continue; }
 
const char* szDelimiters = " \t\r\n";
char* szPaletteType = strtok(&line[0], szDelimiters);
char* szCompressionType = strtok(NULL, szDelimiters);
char* szName = strtok(NULL, szDelimiters);
char* szFilename = strtok(NULL, szDelimiters);
 
if (strlen(szPaletteType) != 1) {
fprintf(stderr, "ERROR: Palette type (argument 1) at line %d is not valid (must be 1 char)\n", curItem+1);
FAIL_CONTINUE;
}
const char chPaletteType = *szPaletteType;
 
if ((chPaletteType != IPE16_PALETTETYPE_ATTACHED) && (chPaletteType != IPE16_PALETTETYPE_PARENT)) {
fprintf(stderr, "ERROR: Unknown palette type '%c' at line %d\n", chPaletteType, curItem+1);
FAIL_CONTINUE;
}
 
if (strlen(szCompressionType) != 1) {
fprintf(stderr, "ERROR: Compression type (argument 2) at line %d is not valid (must be 1 char)\n", curItem+1);
FAIL_CONTINUE;
}
const char chCompressionType = *szCompressionType;
 
if (strlen(szName) > IPE16_NAME_SIZE) {
fprintf(stderr, "ERROR: Name %s is too long (max %d chars allowed)\n", szName, IPE16_NAME_SIZE);
FAIL_CONTINUE;
}
 
strcpy(peh[curItem].name, szName);
peh[curItem].paletteType = chPaletteType;
peh[curItem].offset = ftell(fobArt);
peh[curItem].size = 0; // will be increased later
 
if (verbosity >= 1) printf("Process %s at offset %x\n", szName, peh[curItem].offset);
 
// Read bitmap
 
const bool colorTableExisting = (chPaletteType == IPE16_PALETTETYPE_ATTACHED);
 
char szBitmapFilename[MAX_FILE];
sprintf(szBitmapFilename, "%s/%s", szSrcFolder, szFilename);
FILE* fibBitmap = fopen(szBitmapFilename, "rb");
if (!fibBitmap) {
fprintf(stderr, "ERROR: cannot open '%s'\n", szFilename);
FAIL_CONTINUE;
}
 
Ipe16BmpImportData result={0};
if (!ipe16_bmp_import(fibBitmap, &result)) {
fprintf(stderr, "Error at %s: %s\n", szFilename, result.error);
fclose(fibBitmap);
ipe16_free_bmpimport_result(&result);
FAIL_CONTINUE;
}
 
ph[curItem].compressionType = chCompressionType;
ph[curItem].width = result.width;
ph[curItem].height = result.height;
fwrite(&ph[curItem], sizeof(ph[curItem]), 1, fobArt);
peh[curItem].size += sizeof(ph[curItem]);
 
// Write picture data
 
size_t tmpBefore = ftell(fobArt);
if (chCompressionType == BA_COMPRESSIONTYPE_LZW) {
if (!lzwEncoder) lzwEncoder = new_ipe16lzw_encoder();
ipe16lzw_encode(fobArt, lzwEncoder, result.bmpData, result.bmpDataSize);
} else if (chCompressionType == BA_COMPRESSIONTYPE_NONE) {
fwrite(result.bmpData, result.bmpDataSize, 1, fobArt);
} else {
fprintf(stderr, "Unknown compression type '%c' at line %d\n", chCompressionType, curItem+1);
fclose(fibBitmap);
ipe16_free_bmpimport_result(&result);
FAIL_CONTINUE;
}
peh[curItem].size += ftell(fobArt)-tmpBefore;
 
if (colorTableExisting) {
fwrite(result.colorTable, sizeof(*result.colorTable), 1, fobArt);
peh[curItem].size += sizeof(*result.colorTable);
}
 
// Free and continue
 
fclose(fibBitmap);
ipe16_free_bmpimport_result(&result);
 
++curItem;
}
if (lzwEncoder) del_ipe16lzw_encoder(lzwEncoder);
fclose(fitIndex);
 
bfh.totalFileSize = ftell(fobArt);
 
fseek(fobArt, 0, SEEK_SET);
fwrite(&bfh, sizeof(bfh), 1, fobArt);
fwrite(&peh, sizeof(peh), 1, fobArt);
 
fclose(fobArt);
 
return bEverythingOK;
}
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe_artfile_packer_ipe16_ba.h
0,0 → 1,16
/**
* ART file packer by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Blown Away - The Interactive Game by Imagination Pilots
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe_artfile_packer_ipe16_ba
#define __inc__ipe_artfile_packer_ipe16_ba
 
#include <stdio.h>
#include <stdbool.h>
 
bool ba_pack_art(const char* szSrcFolder, FILE* fobArt, const int verbosity);
 
#endif // #ifndef __inc__ipe_artfile_packer_ipe16_ba
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe_artfile_packer_ipe16_pip.c
0,0 → 1,173
/**
* ART file packer by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Panic in the Park - The Interactive Game by Imagination Pilots
* - Where's Waldo? At the Circus (Waldo1)
* Revision: 2018-02-15
**/
 
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
 
#include "ipe_artfile_packer_ipe16_pip.h"
#include "ipe16_artfile.h"
#include "ipe16_bmpimport.h"
#include "ipe16_lzw_encoder.h"
 
#define MAX_FILE 256
 
bool pip_pack_art(const char* szSrcFolder, FILE* fobArt, const int verbosity) {
bool bEverythingOK = true;
 
char szIndexFilename[MAX_FILE];
sprintf(szIndexFilename, "%s/index.txt", szSrcFolder);
FILE* fitIndex = fopen(szIndexFilename, "rt");
if (!fitIndex) {
fprintf(stderr, "Cannot open %s\n", szIndexFilename);
return false;
}
 
#define MAX_LINE 1024
char line[MAX_LINE];
int cItems = 0;
while (fgets(line, sizeof(line), fitIndex)) {
if (strlen(line) == 0) continue;
++cItems;
}
if (verbosity >= 1) printf("%s contains %d entries\n", szIndexFilename, cItems); // TODO: don't print double /
 
Ipe16FileHeader bfh;
memset(&bfh, 0x00, sizeof(bfh));
strcpy(bfh.magic, IPE16_MAGIC_ART);
bfh.dummy = IPE16_MAGIC_DUMMY;
bfh.numHeaderEntries = cItems+1;
 
Ipe16PictureEntryHeader peh[cItems];
memset(&peh, 0x00, sizeof(peh));
 
PipPictureHeader ph[cItems];
memset(&ph, 0x00, sizeof(ph));
 
// We need to write the (still empty) headers, so we can use ftell() to determine the offsets correctly
// These headers are currently just dummies. They will be rewritten after all pictures are processed
fwrite(&bfh, sizeof(bfh), 1, fobArt);
fwrite(&peh, sizeof(peh), 1, fobArt);
 
fseek(fitIndex, 0, SEEK_SET);
int curItem = 0;
Ipe16LZWEncoder* lzwEncoder = NULL;
while (fgets(line, sizeof(line), fitIndex)) {
// If something fails, we discard the item, but continue in building the file!
#define FAIL_CONTINUE { memset(&peh[curItem], 0x00, sizeof(peh[curItem])); bEverythingOK=false; continue; }
 
const char* szDelimiters = " \t\r\n";
char* szPaletteType = strtok(&line[0], szDelimiters);
char* szCompressionType = strtok(NULL, szDelimiters);
char* szName = strtok(NULL, szDelimiters);
char* szFilename = strtok(NULL, szDelimiters);
char* szOffsetX = strtok(NULL, szDelimiters);
int iOffsetX = (szOffsetX != NULL) ? atoi(szOffsetX) : 0;
char* szOffsetY = strtok(NULL, szDelimiters);
int iOffsetY = (szOffsetY != NULL) ? atoi(szOffsetY) : 0;
 
if (strlen(szPaletteType) != 1) {
fprintf(stderr, "ERROR: Palette type (argument 1) at line %d is not valid (must be 1 char)\n", curItem+1);
FAIL_CONTINUE;
}
const char chPaletteType = *szPaletteType;
 
if ((chPaletteType != IPE16_PALETTETYPE_ATTACHED) && (chPaletteType != IPE16_PALETTETYPE_PARENT)) {
fprintf(stderr, "ERROR: Unknown palette type '%c' at line %d\n", chPaletteType, curItem+1);
FAIL_CONTINUE;
}
 
if (strlen(szCompressionType) != 1) {
fprintf(stderr, "ERROR: Compression type (argument 2) at line %d is not valid (must be 1 char)\n", curItem+1);
FAIL_CONTINUE;
}
const char chCompressionType = *szCompressionType;
 
if (strlen(szName) > IPE16_NAME_SIZE) {
fprintf(stderr, "ERROR: Name %s is too long (max %d chars allowed)\n", szName, IPE16_NAME_SIZE);
FAIL_CONTINUE;
}
 
strcpy(peh[curItem].name, szName);
peh[curItem].paletteType = chPaletteType;
peh[curItem].offset = ftell(fobArt);
peh[curItem].size = 0; // will be increased later
 
if (verbosity >= 1) printf("Process %s at offset %x\n", szName, peh[curItem].offset);
 
// Read bitmap
 
const bool colorTableExisting = (chPaletteType == IPE16_PALETTETYPE_ATTACHED);
 
char szBitmapFilename[MAX_FILE];
sprintf(szBitmapFilename, "%s/%s", szSrcFolder, szFilename);
FILE* fibBitmap = fopen(szBitmapFilename, "rb");
if (!fibBitmap) {
fprintf(stderr, "ERROR: cannot open '%s'\n", szFilename);
FAIL_CONTINUE;
}
 
Ipe16BmpImportData result={0};
if (!ipe16_bmp_import(fibBitmap, &result)) {
fprintf(stderr, "Error at %s: %s\n", szFilename, result.error);
fclose(fibBitmap);
ipe16_free_bmpimport_result(&result);
FAIL_CONTINUE;
}
 
ph[curItem].compressionType = chCompressionType;
ph[curItem].offsetX = iOffsetX;
ph[curItem].offsetY = iOffsetY;
ph[curItem].width = result.width;
ph[curItem].height = result.height;
fwrite(&ph[curItem], sizeof(ph[curItem]), 1, fobArt);
peh[curItem].size += sizeof(ph[curItem]);
 
// Write picture data
 
size_t tmpBefore = ftell(fobArt);
if (chCompressionType == PIP_COMPRESSIONTYPE_LZW) {
if (!lzwEncoder) lzwEncoder = new_ipe16lzw_encoder();
ipe16lzw_encode(fobArt, lzwEncoder, result.bmpData, result.bmpDataSize);
} else if (chCompressionType == PIP_COMPRESSIONTYPE_NONE) {
fwrite(result.bmpData, result.bmpDataSize, 1, fobArt);
} else {
fprintf(stderr, "Unknown compression type '%c' at line %d\n", chCompressionType, curItem+1);
fclose(fibBitmap);
ipe16_free_bmpimport_result(&result);
FAIL_CONTINUE;
}
peh[curItem].size += ftell(fobArt)-tmpBefore;
 
if (colorTableExisting) {
fwrite(result.colorTable, sizeof(*result.colorTable), 1, fobArt);
peh[curItem].size += sizeof(*result.colorTable);
}
 
// Free and continue
 
fclose(fibBitmap);
ipe16_free_bmpimport_result(&result);
 
++curItem;
}
if (lzwEncoder) del_ipe16lzw_encoder(lzwEncoder);
fclose(fitIndex);
 
bfh.totalFileSize = ftell(fobArt);
 
fseek(fobArt, 0, SEEK_SET);
fwrite(&bfh, sizeof(bfh), 1, fobArt);
fwrite(&peh, sizeof(peh), 1, fobArt);
 
fclose(fobArt);
 
return bEverythingOK;
}
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe_artfile_packer_ipe16_pip.h
0,0 → 1,17
/**
* ART file packer by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Panic in the Park - The Interactive Game by Imagination Pilots
* - Where's Waldo? At the Circus (Waldo1)
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe_artfile_packer_ipe16_pip
#define __inc__ipe_artfile_packer_ipe16_pip
 
#include <stdio.h>
#include <stdbool.h>
 
bool pip_pack_art(const char* szSrcFolder, FILE* fobArt, const int verbosity);
 
#endif // #ifndef __inc__ipe_artfile_packer_ipe16_pip
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe_artfile_packer_ipe32.c
0,0 → 1,143
/**
* ART file packer by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* Revision: 2018-02-15
**/
 
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
 
#include "ipe_artfile_packer_ipe32.h"
#include "ipe32_artfile.h"
#include "ipe32_bmpimport.h"
#include "ipe32_lzw_encoder.h"
 
#define MAX_FILE 256
 
bool ipe32_pack_art(const char* szSrcFolder, FILE* fobArt, const int verbosity) {
bool bEverythingOK = true;
 
char szIndexFilename[MAX_FILE];
sprintf(szIndexFilename, "%s/index.txt", szSrcFolder);
FILE* fitIndex = fopen(szIndexFilename, "rt");
if (!fitIndex) {
fprintf(stderr, "Cannot open %s\n", szIndexFilename);
return false;
}
 
#define MAX_LINE 1024
char line[MAX_LINE];
int cItems = 0;
while (fgets(line, sizeof(line), fitIndex)) {
if (strlen(line) == 0) continue;
++cItems;
}
if (verbosity >= 1) printf("%s contains %d entries\n", szIndexFilename, cItems); // TODO: don't print double /
 
Ipe32FileHeader efh;
memset(&efh, 0x00, sizeof(efh));
strcpy(efh.magic, IPE32_MAGIC_ART);
efh.reserved = 0;
efh.totalHeaderSize = (cItems+1)*sizeof(efh);
 
Ipe32PictureEntryHeader peh[cItems];
memset(&peh, 0x00, sizeof(peh));
 
// These headers are currently just dummies. They will be rewritten after all pictures are processed
fwrite(&efh, sizeof(efh), 1, fobArt);
fwrite(&peh, sizeof(peh), 1, fobArt);
 
Ipe32LZWEncoder *encoder = new_ipe32lzw_encoder();
ipe32lzw_init_encoder(encoder);
fseek(fitIndex, 0, SEEK_SET);
int curItem = 0;
while (fgets(line, sizeof(line), fitIndex)) {
// If something fails, we discard the item, but continue in building the file!
#define FAIL_CONTINUE { memset(&peh[curItem], 0x00, sizeof(peh[curItem])); bEverythingOK=false; continue; }
 
const char* szDelimiters = " \t\r\n";
char* szName = strtok(&line[0], szDelimiters);
/* char* szNumCompressedChunks = */ strtok(NULL, szDelimiters);
/* char* szNumRawChunks = */ strtok(NULL, szDelimiters);
char* szFilename = strtok(NULL, szDelimiters);
 
if (strlen(szName) > IPE32_NAME_SIZE) {
fprintf(stderr, "ERROR: Name %s is too long (max %d chars allowed)\n", szName, IPE32_NAME_SIZE);
FAIL_CONTINUE;
}
 
char szBitmapFilename[MAX_FILE];
sprintf(szBitmapFilename, "%s/%s", szSrcFolder, szFilename);
FILE* fibBitmap = fopen(szBitmapFilename, "rb");
if (!fibBitmap) {
fprintf(stderr, "ERROR: cannot open '%s'\n", szFilename);
FAIL_CONTINUE;
}
 
Ipe32BmpImportData result={0};
if (!ipe32_bmp_import(fibBitmap, &result)) { // This function moves the file pointer to the bitmap info header
fprintf(stderr, "Error at %s: %s\n", szFilename, result.error);
fclose(fibBitmap);
ipe32_free_bmpimport_result(&result);
FAIL_CONTINUE;
}
 
strcpy(peh[curItem].name, szName);
peh[curItem].offset = ftell(fobArt);
peh[curItem].uncompressedSize = result.dataSize;
if (verbosity >= 1) printf("Process %s at offset %x\n", szName, peh[curItem].offset);
 
// Now write the chunks
 
int chunkNo = 0;
unsigned char uncompressedChunk[0x3FFE];
unsigned char compressedChunk[0x3FFE];
while (1) {
if (verbosity >= 2) fprintf(stdout, "Bitmap %s: Write chunk %d.\n", szFilename, chunkNo);
 
int uncompressedSize = fread(uncompressedChunk, 1, sizeof(uncompressedChunk), fibBitmap);
if (uncompressedSize == 0) break; // done
 
int compressedSize = ipe32lzw_encode(encoder, compressedChunk, sizeof(compressedChunk), uncompressedChunk, uncompressedSize);
 
uint16_t len;
 
if ((compressedSize == -1) || (compressedSize >= uncompressedSize)) {
// Choose uncompressed chunk
len = 0x8000 | uncompressedSize;
fwrite(&len, sizeof(len), 1, fobArt);
fwrite(uncompressedChunk, uncompressedSize, 1, fobArt);
} else {
// Choose compressed chunk
len = compressedSize;
fwrite(&len, sizeof(len), 1, fobArt);
fwrite(compressedChunk, compressedSize, 1, fobArt);
}
 
chunkNo++;
}
 
// Free and continue
 
fclose(fibBitmap);
ipe32_free_bmpimport_result(&result);
 
++curItem;
}
fclose(fitIndex);
ipe32lzw_free_encoder(encoder);
 
fseek(fobArt, 0, SEEK_SET);
fwrite(&efh, sizeof(efh), 1, fobArt);
fwrite(&peh, sizeof(peh), 1, fobArt);
 
fclose(fobArt);
 
return bEverythingOK;
}
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe_artfile_packer_ipe32.h
0,0 → 1,19
/**
* ART file packer by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe_artfile_packer_ipe32
#define __inc__ipe_artfile_packer_ipe32
 
#include <stdio.h>
#include <stdbool.h>
 
bool ipe32_pack_art(const char* szSrcFolder, FILE* fobArt, const int verbosity);
 
#endif // #ifndef __inc__ipe_artfile_packer_ipe32
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe_artfile_unpacker
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Added: svn:executable
+*
\ No newline at end of property
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/ipe_artfile_unpacker.c
0,0 → 1,87
/**
* ART file unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Blown Away - The Interactive Game by Imagination Pilots
* - Panic in the Park - The Interactive Game by Imagination Pilots
* - Where's Waldo? At the Circus (Waldo1)
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* Revision: 2018-02-15
**/
 
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <getopt.h>
 
#include "ipe16_artfile.h"
#include "ipe32_artfile.h"
 
#include "ipe_artfile_unpacker_ipe16.h"
#include "ipe_artfile_unpacker_ipe32.h"
 
#define VERSION "2018-02-15"
 
void print_syntax() {
fprintf(stderr, "Syntax: -v [-o <outputdir>] -i <artfile>\n");
fprintf(stderr, " -v : verbose output\n");
fprintf(stderr, "Runs in simulation mode if no output directory is defined.\n");
}
 
int main(int argc, char *argv[]) {
int verbosity = 0;
char* szOutputDir = "";
char* szArtFile = "";
int c;
 
#define PRINT_SYNTAX { print_syntax(); return 0; }
 
while ((c = getopt(argc, argv, "Vvi:o:")) != -1) {
switch (c) {
case 'v':
verbosity++;
break;
case 'V':
fprintf(stdout, "IPE Artfile unpacker, revision %s\n", VERSION);
return 0;
case 'i':
szArtFile = optarg;
break;
case 'o':
szOutputDir = optarg;
break;
case '?':
PRINT_SYNTAX;
break;
}
}
if (optind < argc) PRINT_SYNTAX;
 
if (strlen(szArtFile) == 0) PRINT_SYNTAX;
 
FILE* fibArt = fopen(szArtFile, "rb");
if (!fibArt) {
fprintf(stderr, "FATAL: Cannot open %s\n", szArtFile);
return 1;
}
 
char signature[9]={0};
if (fread(&signature, 8, 1, fibArt) != 1) {
fprintf(stderr, "FATAL: Cannot read signature of %s\n", szArtFile);
return 1;
}
if (strcmp(signature, IPE32_MAGIC_ART) == 0) {
if (verbosity >= 1) fprintf(stdout, "%s: Detected file as IPE32 (Waldo2/Eraser/K'Nex) art file\n", szArtFile);
return ipe32_extract_art_to_folder(fibArt, szOutputDir, verbosity) ? 0 : 1;
} else if (strcmp(signature, IPE16_MAGIC_ART) == 0) {
if (verbosity >= 1) fprintf(stdout, "%s: Detected file as IPE16 (BA/PiP/Waldo1) art file\n", szArtFile);
return ipe16_extract_art_to_folder(fibArt, szOutputDir, verbosity) ? 0 : 1;
} else {
fprintf(stderr, "FATAL: %s is not a valid ART file of Imagination Pilots!\n", szArtFile);
return 1;
}
 
fclose(fibArt);
}
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe_artfile_unpacker.dev
0,0 → 1,182
[Project]
FileName=ipe_artfile_unpacker.dev
Name=Artfile Unpacker
UnitCount=11
Type=1
Ver=2
ObjFiles=
Includes=
Libs=
PrivateResource=
ResourceIncludes=
MakeIncludes=
Compiler=
CppCompiler=
Linker=
IsCpp=0
Icon=
ExeOutput=
ObjectOutput=
OverrideOutput=0
OverrideOutputName=ipe_artfile_unpacker.exe
HostApplication=
Folders=
CommandLine=
UseCustomMakefile=0
CustomMakefile=
IncludeVersionInfo=0
SupportXPThemes=0
CompilerSet=0
CompilerSettings=0000000000000000000000000
LogOutput=
LogOutputEnabled=0
 
[VersionInfo]
Major=0
Minor=1
Release=1
Build=1
LanguageID=1033
CharsetID=1252
CompanyName=
FileVersion=0.1.1.1
FileDescription=Developed using the Dev-C++ IDE
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=
AutoIncBuildNr=0
SyncProduct=0
 
[Unit1]
FileName=ipe_artfile_unpacker.c
CompileCpp=0
Folder=BA Artfile Unpacker
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit2]
FileName=ipe16_artfile.h
CompileCpp=0
Folder=BA Artfile Unpacker
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit3]
FileName=ipe16_bmpexport.c
CompileCpp=0
Folder=BA Artfile Unpacker
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit6]
FileName=ipe32_lzw_decoder.c
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit8]
FileName=utils.c
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit9]
FileName=ipe_artfile_unpacker_ipe16.c
CompileCpp=0
Folder=BA Artfile Unpacker
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit10]
FileName=ipe_artfile_unpacker_ipe32.c
CompileCpp=0
Folder=BA Artfile Unpacker
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit11]
FileName=ipe32_bmpexport.c
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit12]
FileName=ipe_artfile_unpacker_ipe16.c
CompileCpp=0
Folder=BA Artfile Unpacker
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit13]
FileName=ipe_artfile_unpacker_ipe32.c
CompileCpp=0
Folder=BA Artfile Unpacker
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit4]
FileName=ipe16_lzw_decoder.c
CompileCpp=0
Folder=BA Artfile Unpacker
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit5]
FileName=ipe32_artfile.h
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
[Unit7]
FileName=bitmap.h
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
 
/trunk/ipe_artfile_unpacker.exe
Cannot display: file marked as a binary type.
svn:mime-type = application/x-msdos-program
Property changes:
Added: svn:mime-type
+application/x-msdos-program
\ No newline at end of property
/trunk/ipe_artfile_unpacker.layout
0,0 → 1,53
[Editor_6]
CursorCol=29
CursorRow=8
TopLine=1
LeftChar=1
[Editor_7]
CursorCol=19
CursorRow=8
TopLine=1
LeftChar=1
[Editor_4]
CursorCol=35
CursorRow=9
TopLine=1
LeftChar=1
[Editor_5]
CursorCol=35
CursorRow=54
TopLine=44
LeftChar=1
[Editor_3]
CursorCol=1
CursorRow=9
TopLine=1
LeftChar=1
[Editor_2]
CursorCol=5
CursorRow=51
TopLine=50
LeftChar=1
[Editor_1]
CursorCol=20
CursorRow=13
TopLine=1
LeftChar=1
[Editor_0]
CursorCol=1
CursorRow=1
TopLine=1
LeftChar=1
[Editor_8]
CursorCol=1
CursorRow=8
TopLine=1
LeftChar=1
[Editor_9]
CursorCol=17
CursorRow=4
TopLine=1
LeftChar=1
[Editors]
Order=1
Focused=1
/trunk/ipe_artfile_unpacker_ipe16.c
0,0 → 1,299
/**
* ART file unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Blown Away - The Interactive Game by Imagination Pilots
* - Panic in the Park - The Interactive Game by Imagination Pilots
* - Where's Waldo? At the Circus (Waldo1)
* Revision: 2018-02-15
**/
 
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <getopt.h>
 
#include "ipe16_bmpexport.h"
#include "ipe16_artfile.h"
#include "ipe16_lzw_decoder.h"
 
#include "utils.h"
 
#define MAX_FILE 256
 
void ipe16_generate_gray_table(Ipe16ColorTable *ct) {
int i;
for (i=0; i<=0xFF; ++i) {
ct->colors[i].r = i;
ct->colors[i].g = i;
ct->colors[i].b = i;
}
}
 
bool ipe16_extract_art_to_folder(FILE* fibArt, const char* szDestFolder, const int verbosity) {
bool bEverythingOK = true;
 
fseek(fibArt, 0, SEEK_SET);
 
Ipe16FileHeader bfh;
if (fread(&bfh, sizeof(bfh), 1, fibArt) != 1) {
fprintf(stderr, "FATAL: Cannot read Ipe16FileHeader. It is probably not an art file.\n");
return false;
}
 
// The "super header" has some different meanings of the fields
// Name and Type are hardcoded
// startOffset is the number of header entries (including the super header)
// length is the complete file size
const size_t fileSize = file_size(fibArt);
if ((strcmp(bfh.magic, IPE16_MAGIC_ART) != 0) || // better memcpy over all 23 bytes?
(bfh.dummy != IPE16_MAGIC_DUMMY) ||
(bfh.totalFileSize != fileSize)) {
fprintf(stderr, "FATAL: Something does not seem to be correct with this art file's header. It is probably not an art file.\n");
return false;
}
 
Ipe16LZWDecoder* lzwDecoder = NULL;
 
FILE* fotIndex = NULL;
if (strlen(szDestFolder) > 0) {
char szIndexFilename[MAX_FILE];
sprintf(szIndexFilename, "%s/index.txt", szDestFolder);
fotIndex = fopen(szIndexFilename, "wt");
if (!fotIndex) {
fprintf(stderr, "FATAL: Cannot open %s for writing\n", szIndexFilename);
return false;
}
}
 
const int numPictures = bfh.numHeaderEntries - 1;
char knownNames[numPictures][IPE16_NAME_SIZE];
memset(&knownNames[0][0], 0, numPictures*IPE16_NAME_SIZE);
int iPicNo;
for (iPicNo=0; iPicNo<numPictures; ++iPicNo) {
Ipe16PictureEntryHeader peh;
if (fread(&peh, sizeof(peh), 1, fibArt) != 1) {
fprintf(stderr, "FATAL: Cannot read Ipe16PictureEntryHeader.\n");
return false;
}
 
// Begin duplicate check
memcpy(&knownNames[iPicNo][0], peh.name, IPE16_NAME_SIZE);
int iCopyNumber = 0;
int j;
for (j=0; j<=iPicNo; ++j) {
// TODO: should we rather use strcmp() in IPE16?
if (memcmp(&knownNames[j][0], peh.name, IPE16_NAME_SIZE) == 0) ++iCopyNumber;
}
assert(iCopyNumber > 0);
// End duplicate check
 
// in the English version of Blown Away (not the Special Edition), there is a header entry
// with the fields seh.name='', peh.paletteType=0x00, seh.offset[end of file], seh.size=0
// ignore it
if (strlen(peh.name) == 0) continue;
if (peh.size == 0) continue;
 
if (strlen(peh.name) > IPE16_NAME_SIZE-1) {
fprintf(stderr, "FATAL: szName at picture %d is breaking the boundaries. The file is probably corrupt.\n", iPicNo);
return false;
}
 
size_t headersPos = ftell(fibArt);
#define FAIL_CONTINUE { bEverythingOK = false; fseek(fibArt, headersPos, SEEK_SET); continue; }
 
if (fseek(fibArt, peh.offset, SEEK_SET) != 0) {
fprintf(stderr, "ERROR: Error jumping to offset defined for %s\n", peh.name);
FAIL_CONTINUE;
}
 
if (peh.offset+peh.size > fileSize) {
fprintf(stderr, "ERROR: Defined size of %s exceeds file size\n", peh.name);
FAIL_CONTINUE;
}
 
unsigned char compressionType;
int bakPos = ftell(fibArt);
fread(&compressionType, sizeof(compressionType), 1, fibArt);
fseek(fibArt, bakPos, SEEK_SET);
 
if ((compressionType == BA_COMPRESSIONTYPE_LZW) || (compressionType == BA_COMPRESSIONTYPE_NONE)) {
BAPictureHeader ph;
if (fread(&ph, sizeof(ph), 1, fibArt) != 1) {
fprintf(stderr, "ERROR: Cannot read BAPictureHeader of %s\n", peh.name);
FAIL_CONTINUE;
}
 
size_t imagedata_len = ph.width * ph.height;
unsigned char* imagedata = (unsigned char*)malloc(imagedata_len);
 
Ipe16ColorTable ct;
if (peh.paletteType == IPE16_PALETTETYPE_ATTACHED) {
if ((fseek(fibArt, peh.offset+peh.size-sizeof(ct), SEEK_SET) != 0) ||
(fread(&ct, sizeof(ct), 1, fibArt) != 1) ||
(fseek(fibArt, peh.offset+sizeof(ph), SEEK_SET) != 0)) {
fprintf(stderr, "ERROR: Cannot read palette of %s\n", peh.name);
FAIL_CONTINUE;
}
} else if (peh.paletteType == IPE16_PALETTETYPE_PARENT) {
ipe16_generate_gray_table(&ct);
} else {
fprintf(stderr, "ERROR: Unknown palette type 0x%x at %s\n", peh.paletteType, peh.name);
FAIL_CONTINUE;
}
 
unsigned int bytes_written, expected_uncompressed_len;
switch (ph.compressionType) {
case BA_COMPRESSIONTYPE_LZW:
if (!lzwDecoder) lzwDecoder = new_ipe16lzw_decoder();
bytes_written = ipe16lzw_decode(fibArt, lzwDecoder, imagedata, imagedata_len);
if (bytes_written < 0) {
fprintf(stderr, "ERROR: LZW decompression error at %s\n", peh.name);
FAIL_CONTINUE;
}
if (bytes_written != imagedata_len) {
fprintf(stderr, "ERROR: Image dimensions and decompressed data size does not match for %s\n", peh.name);
FAIL_CONTINUE;
}
 
break;
case BA_COMPRESSIONTYPE_NONE:
expected_uncompressed_len = imagedata_len;
if (peh.paletteType == IPE16_PALETTETYPE_ATTACHED) expected_uncompressed_len += sizeof(ct);
expected_uncompressed_len += sizeof(ph.compressionType)+sizeof(ph.width)+sizeof(ph.height);
if (expected_uncompressed_len != peh.size) {
fprintf(stderr, "ERROR: Image dimensions/palette (%d) and defined memory size (%d) does not match for %s\n", expected_uncompressed_len, peh.size, peh.name);
FAIL_CONTINUE;
}
fread(imagedata, imagedata_len, 1, fibArt); // no error checking, because filesize was already checked
break;
}
 
char szBitmapFilename[MAX_FILE];
if (iCopyNumber == 1) {
sprintf(szBitmapFilename, "%s.bmp", sanitize_filename(peh.name));
} else {
sprintf(szBitmapFilename, "%s__%d.bmp", sanitize_filename(peh.name), iCopyNumber);
}
 
if (strlen(szDestFolder) > 0) {
char szAbsoluteBitmapFilename[MAX_FILE];
sprintf(szAbsoluteBitmapFilename, "%s/%s", szDestFolder, szBitmapFilename);
FILE* fobBitmap = fopen(szAbsoluteBitmapFilename, "wb");
if (!fobBitmap) {
fprintf(stderr, "FATAL: Cannot open %s for writing\n", szAbsoluteBitmapFilename);
FAIL_CONTINUE;
}
ipe16_write_bmp(fobBitmap, ph.width, ph.height, imagedata, imagedata_len, ct);
fclose(fobBitmap);
}
 
if (fotIndex) {
// We require this index file for 2 reasons
// 1. Our packer tool can then know what to pack
// 2. The packer tool can know which picture would have a palette appended and which one does not
// The index file won't be written in simulation mode (when no output directory is defined)
fprintf(fotIndex, "%c %c %s %s\n", peh.paletteType, ph.compressionType, peh.name, szBitmapFilename);
}
if (verbosity >= 1) {
fprintf(stdout, "%c %c %s %s\n", peh.paletteType, ph.compressionType, peh.name, szBitmapFilename);
}
 
free(imagedata);
} else if ((compressionType == PIP_COMPRESSIONTYPE_LZW) || (compressionType == PIP_COMPRESSIONTYPE_NONE)) {
PipPictureHeader ph;
if (fread(&ph, sizeof(ph), 1, fibArt) != 1) {
fprintf(stderr, "ERROR: Cannot read PipPictureHeader of %s\n", peh.name);
FAIL_CONTINUE;
}
 
size_t imagedata_len = ph.width * ph.height;
unsigned char* imagedata = (unsigned char*)malloc(imagedata_len);
 
Ipe16ColorTable ct;
if (peh.paletteType == IPE16_PALETTETYPE_ATTACHED) {
if ((fseek(fibArt, peh.offset+peh.size-sizeof(ct), SEEK_SET) != 0) ||
(fread(&ct, sizeof(ct), 1, fibArt) != 1) ||
(fseek(fibArt, peh.offset+sizeof(ph), SEEK_SET) != 0)) {
fprintf(stderr, "ERROR: Cannot read palette of %s\n", peh.name);
FAIL_CONTINUE;
}
} else if (peh.paletteType == IPE16_PALETTETYPE_PARENT) {
ipe16_generate_gray_table(&ct);
} else {
fprintf(stderr, "ERROR: Unknown palette type 0x%x at %s\n", peh.paletteType, peh.name);
FAIL_CONTINUE;
}
 
unsigned int bytes_written, expected_uncompressed_len;
switch (ph.compressionType) {
case PIP_COMPRESSIONTYPE_LZW:
if (!lzwDecoder) lzwDecoder = new_ipe16lzw_decoder();
bytes_written = ipe16lzw_decode(fibArt, lzwDecoder, imagedata, imagedata_len);
if (bytes_written < 0) {
fprintf(stderr, "ERROR: LZW decompression error at %s\n", peh.name);
FAIL_CONTINUE;
}
if (bytes_written != imagedata_len) {
fprintf(stderr, "ERROR: Image dimensions and decompressed data size does not match for %s\n", peh.name);
FAIL_CONTINUE;
}
 
break;
case PIP_COMPRESSIONTYPE_NONE:
expected_uncompressed_len = imagedata_len;
if (peh.paletteType == IPE16_PALETTETYPE_ATTACHED) expected_uncompressed_len += sizeof(ct);
expected_uncompressed_len += sizeof(ph.compressionType)+sizeof(ph.offsetX)+sizeof(ph.offsetY)+sizeof(ph.width)+sizeof(ph.height);
if (expected_uncompressed_len != peh.size) {
fprintf(stderr, "ERROR: Image dimensions/palette (%d) and defined memory size (%d) does not match for %s\n", expected_uncompressed_len, peh.size, peh.name);
FAIL_CONTINUE;
}
fread(imagedata, imagedata_len, 1, fibArt); // no error checking, because filesize was already checked
break;
}
 
char szBitmapFilename[MAX_FILE];
if (iCopyNumber == 1) {
sprintf(szBitmapFilename, "%s.bmp", sanitize_filename(peh.name));
} else {
sprintf(szBitmapFilename, "%s__%d.bmp", sanitize_filename(peh.name), iCopyNumber);
}
 
if (strlen(szDestFolder) > 0) {
char szAbsoluteBitmapFilename[MAX_FILE];
sprintf(szAbsoluteBitmapFilename, "%s/%s", szDestFolder, szBitmapFilename);
FILE* fobBitmap = fopen(szAbsoluteBitmapFilename, "wb");
if (!fobBitmap) {
fprintf(stderr, "FATAL: Cannot open %s for writing\n", szAbsoluteBitmapFilename);
FAIL_CONTINUE;
}
ipe16_write_bmp(fobBitmap, ph.width, ph.height, imagedata, imagedata_len, ct);
fclose(fobBitmap);
}
 
if (fotIndex) {
// We require this index file for 2 reasons
// 1. Our packer tool can then know what to pack
// 2. The packer tool can know which picture would have a palette appended and which one does not
// The index file won't be written in simulation mode (when no output directory is defined)
fprintf(fotIndex, "%c %c %s %s %d %d\n", peh.paletteType, ph.compressionType, peh.name, szBitmapFilename, ph.offsetX, ph.offsetY);
}
if (verbosity >= 1) {
fprintf(stdout, "%c %c %s %s %d %d\n", peh.paletteType, ph.compressionType, peh.name, szBitmapFilename, ph.offsetX, ph.offsetY);
}
 
free(imagedata);
} else {
fprintf(stderr, "ERROR: Unknown compression type 0x%x at %s\n", compressionType, peh.name);
FAIL_CONTINUE;
}
 
fseek(fibArt, headersPos, SEEK_SET); // we were already there, so we don't need to check for errors
}
 
if (lzwDecoder) del_ipe16lzw_decoder(lzwDecoder);
 
if (strlen(szDestFolder) > 0) fclose(fotIndex);
 
return bEverythingOK;
}
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe_artfile_unpacker_ipe16.h
0,0 → 1,18
/**
* ART file unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Blown Away - The Interactive Game by Imagination Pilots
* - Panic in the Park - The Interactive Game by Imagination Pilots
* - Where's Waldo? At the Circus (Waldo1)
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe_artfile_packer_ipe16
#define __inc__ipe_artfile_packer_ipe16
 
#include <stdio.h>
#include <stdbool.h>
 
bool ipe16_extract_art_to_folder(FILE* fibArt, const char* szDestFolder, const int verbosity);
 
#endif // #ifndef __inc__ipe_artfile_packer_ipe16
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/ipe_artfile_unpacker_ipe32.c
0,0 → 1,197
/**
* ART file unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* Revision: 2018-02-15
**/
 
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <getopt.h>
 
#include "ipe32_bmpexport.h"
#include "ipe32_artfile.h"
#include "ipe32_lzw_decoder.h"
 
#include "utils.h"
 
#define MAX_FILE 256
 
typedef struct tagIpe32ReadPictureResult {
uint32_t writtenBytes;
uint32_t numCompressedChunks;
uint32_t numRawChunks;
} Ipe32ReadPictureResult;
 
Ipe32ReadPictureResult ipe32_read_picture(FILE* hFile, unsigned char* outbuf, const int outputBufLength, bool bVerbose) {
unsigned char* lzwbuf = (unsigned char*)malloc(0x8000);
int availableOutputBytes = outputBufLength;
 
Ipe32ReadPictureResult res;
res.numCompressedChunks = 0;
res.numRawChunks = 0;
res.writtenBytes = 0;
 
Ipe32LZWDecoder *decoder = new_ipe32lzw_decoder();
ipe32lzw_init_decoder(decoder);
if (outputBufLength != 0) {
int chunkNo = 0;
do {
uint16_t len;
fread(&len, 1, 2, hFile);
 
uint16_t writtenBytes;
if (len < 0x8000) {
fread(lzwbuf, 1, len, hFile);
res.numCompressedChunks++;
if (bVerbose) fprintf(stdout, "Chunk %d (compressed, length: %d) ...\n", chunkNo, len);
 
// Requirement 1: Each chunk (except the last one) MUST have 0x3FFE of uncompressed data
uint16_t expectedOutputSize = availableOutputBytes > 0x3FFE ? 0x3FFE : availableOutputBytes;
 
// Requirement 2: The size of the uncompressed data must not exceed the size of the compressed data
size_t maxReadBytes = expectedOutputSize;
 
writtenBytes = ipe32lzw_decode(decoder, outbuf, outputBufLength, lzwbuf, maxReadBytes); // returns bytes written, or -1
 
if (writtenBytes == -1) {
fprintf(stderr, "ERROR: Fatal error during decompression of chunk %d!\n", chunkNo);
break;
}
 
if (writtenBytes != expectedOutputSize) {
fprintf(stderr, "ERROR: Chunk %d decompressed %d bytes, but %d bytes are expected!\n", chunkNo, writtenBytes, expectedOutputSize);
break;
}
} else {
len &= 0x7FFF;
res.numRawChunks++;
if (bVerbose) fprintf(stdout, "Chunk %d (raw, length: %d) ...\n", chunkNo, len);
fread(outbuf, 1, len, hFile);
writtenBytes = len;
}
outbuf += writtenBytes;
availableOutputBytes -= writtenBytes;
chunkNo++;
} while (availableOutputBytes != 0);
}
ipe32lzw_free_decoder(decoder);
 
free(lzwbuf);
res.writtenBytes = outputBufLength-availableOutputBytes;
return res;
}
 
bool ipe32_extract_art_to_folder(FILE* fibArt, const char* szDestFolder, const int verbosity) {
bool bEverythingOK = true;
 
fseek(fibArt, 0, SEEK_SET);
 
Ipe32FileHeader efh;
if (fread(&efh, sizeof(efh), 1, fibArt) != 1) {
fprintf(stderr, "FATAL: Cannot read Ipe32FileHeader. It is probably not an art file.\n");
return false;
}
 
// Check if the super header is correct
if ((memcmp(efh.magic, IPE32_MAGIC_ART, 8) != 0) || (efh.reserved != 0)) {
fprintf(stderr, "FATAL: Something does not seem to be correct with this art file's header. It is probably not an art file.\n");
return false;
}
 
FILE* fotIndex = NULL;
if (strlen(szDestFolder) > 0) {
char szIndexFilename[MAX_FILE];
sprintf(szIndexFilename, "%s/index.txt", szDestFolder);
fotIndex = fopen(szIndexFilename, "wt");
if (!fotIndex) {
fprintf(stderr, "FATAL: Cannot open %s for writing\n", szIndexFilename);
return false;
}
}
 
const int numPictures = efh.totalHeaderSize/sizeof(efh) - 1;
char knownNames[numPictures][IPE32_NAME_SIZE];
memset(knownNames, 0, numPictures*IPE32_NAME_SIZE);
int iPicNo;
for (iPicNo=0; iPicNo<numPictures; ++iPicNo) {
Ipe32PictureEntryHeader peh;
if (fread(&peh, sizeof(peh), 1, fibArt) != 1) {
fprintf(stderr, "FATAL: Cannot read Ipe32PictureEntryHeader.\n");
return false;
}
 
char szName[IPE32_NAME_SIZE+1]={0};
memcpy(szName, peh.name, IPE32_NAME_SIZE);
 
// Begin duplicate check
// In ERASER, there are a few pictures which have the same identifier, in the same ART file!
memcpy(&knownNames[iPicNo][0], peh.name, IPE32_NAME_SIZE);
int iCopyNumber = 0;
int j;
for (j=0; j<=iPicNo; ++j) {
if (memcmp(&knownNames[j][0], peh.name, IPE32_NAME_SIZE) == 0) ++iCopyNumber;
}
assert(iCopyNumber > 0);
// End duplicate check
 
if (verbosity >= 2) fprintf(stdout, "Extracting %s (expected file size: %d bytes) ...\n", szName, peh.uncompressedSize);
 
size_t headersPos = ftell(fibArt);
#define FAIL_CONTINUE { bEverythingOK = false; fseek(fibArt, headersPos, SEEK_SET); continue; }
 
if (fseek(fibArt, peh.offset, SEEK_SET) != 0) {
fprintf(stderr, "ERROR: Error jumping to offset defined for %s\n", szName);
FAIL_CONTINUE;
}
 
int outputBufLen = peh.uncompressedSize;
unsigned char* outputBuf = (unsigned char*)malloc(outputBufLen);
 
char szBitmapFilename[MAX_FILE];
if (iCopyNumber == 1) {
sprintf(szBitmapFilename, "%s.bmp", sanitize_filename(szName));
} else {
sprintf(szBitmapFilename, "%s__%d.bmp", sanitize_filename(szName), iCopyNumber);
}
 
Ipe32ReadPictureResult res = ipe32_read_picture(fibArt, outputBuf, outputBufLen, verbosity >= 2);
if (res.writtenBytes != outputBufLen) {
fprintf(stderr, "FATAL: Error reading picture %s (compression failure?)\n", szName);
FAIL_CONTINUE;
}
 
if (strlen(szDestFolder) > 0) {
char szAbsoluteBitmapFilename[MAX_FILE];
sprintf(szAbsoluteBitmapFilename, "%s/%s", szDestFolder, szBitmapFilename);
FILE* fobBitmap = fopen(szAbsoluteBitmapFilename, "wb");
if (!fobBitmap) {
fprintf(stderr, "FATAL: Cannot open %s for writing\n", szAbsoluteBitmapFilename);
FAIL_CONTINUE;
}
ipe32_write_bmp(fobBitmap, outputBuf, outputBufLen);
fclose(fobBitmap);
}
 
free(outputBuf);
 
if (fotIndex) {
// We require this index file so that our packer tool can know what to pack
// The index file won't be written in simulation mode (when no output directory is defined)
fprintf(fotIndex, "%s %d(C) %d(R) %s\n", szName, res.numCompressedChunks, res.numRawChunks, szBitmapFilename);
}
if (verbosity >= 1) {
fprintf(stdout, "%s %d(C) %d(R) %s\n", szName, res.numCompressedChunks, res.numRawChunks, szBitmapFilename);
}
 
fseek(fibArt, headersPos, SEEK_SET); // we were already there, so we don't need to check for errors
}
 
if (strlen(szDestFolder) > 0) fclose(fotIndex);
 
return bEverythingOK;
}
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/ipe_artfile_unpacker_ipe32.h
0,0 → 1,18
/**
* ART file unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
* Supports:
* - Where's Waldo? Exploring Geography
* - Eraser Turnabout by Imagination Pilots
* - Virtual K'Nex by Imagination Pilots
* Revision: 2018-02-15
**/
 
#ifndef __inc__ipe_artfile_unpacker_ipe32
#define __inc__ipe_artfile_unpacker_ipe32
 
#include <stdio.h>
#include <stdbool.h>
 
bool ipe32_extract_art_to_folder(FILE* fibArt, const char* szDestFolder, const int verbosity);
 
#endif // #ifndef __inc__ipe_artfile_packer_ipe32
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/stable.tar.gz
Cannot display: file marked as a binary type.
svn:mime-type = application/gzip
Property changes:
Added: svn:mime-type
+application/gzip
\ No newline at end of property
/trunk/test/ba_test/MENU.bmp
Cannot display: file marked as a binary type.
svn:mime-type = image/x-ms-bmp
Property changes:
Added: svn:mime-type
+image/x-ms-bmp
\ No newline at end of property
/trunk/test/ba_test/index.txt
0,0 → 1,0
X P MENU MENU.bmp
Property changes:
Added: svn:mime-type
+text/plain
\ No newline at end of property
/trunk/test/eraser_test/CHRBDOSS.bmp
Cannot display: file marked as a binary type.
svn:mime-type = image/x-ms-bmp
Property changes:
Added: svn:mime-type
+image/x-ms-bmp
\ No newline at end of property
/trunk/test/eraser_test/index.txt
0,0 → 1,0
CHRBDOSS 19(C) 0(R) CHRBDOSS.bmp
Property changes:
Added: svn:mime-type
+text/plain
\ No newline at end of property
/trunk/test/pip_test/CCES2S.bmp
Cannot display: file marked as a binary type.
svn:mime-type = image/x-ms-bmp
Property changes:
Added: svn:mime-type
+image/x-ms-bmp
\ No newline at end of property
/trunk/test/pip_test/index.txt
0,0 → 1,0
X Q CCES2S CCES2S.bmp 0 0
Property changes:
Added: svn:mime-type
+text/plain
\ No newline at end of property
/trunk/test/test.sh
0,0 → 1,72
#!/bin/bash
 
DIR=$( dirname "$0" )
cd "$DIR"
 
if [ -d out_test ]; then
rm -Rf out_test
fi
if [ -f eraser_test.art ]; then
rm -f eraser_test.art
fi
 
# ---
 
if [ -f ba_test.art ]; then
rm -f ba_test.art
fi
../ipe_artfile_packer -v -i ba_test -o ba_test.art -t ba
if [ -f ba_test.art ]; then
mkdir out_test
../ipe_artfile_unpacker -v -i ba_test.art -o out_test
fi
diff ba_test/MENU.bmp out_test/MENU.bmp
RES=$?
#if [ $RES -ne 0 ]; then
# exit 1
#fi
echo "DIFF Result (BA): $RES"
if [ -d out_test ]; then
rm -Rf out_test
fi
if [ -f ba_test.art ]; then
rm -f ba_test.art
fi
echo "------------------------"
 
../ipe_artfile_packer -v -i pip_test -o pip_test.art -t pip
if [ -f pip_test.art ]; then
mkdir out_test
../ipe_artfile_unpacker -v -i pip_test.art -o out_test
fi
diff pip_test/CCES2S.bmp out_test/CCES2S.bmp
RES=$?
#if [ $RES -ne 0 ]; then
# exit
#fi
echo "DIFF Result (PiP): $RES"
if [ -d out_test ]; then
rm -Rf out_test
fi
if [ -f pip_test.art ]; then
rm -f pip_test.art
fi
echo "------------------------"
 
../ipe_artfile_packer -v -i eraser_test -o eraser_test.art -t eraser
if [ -f eraser_test.art ]; then
mkdir out_test
../ipe_artfile_unpacker -v -i eraser_test.art -o out_test
fi
diff eraser_test/CHRBDOSS.bmp out_test/CHRBDOSS.bmp
RES=$?
#if [ $RES -ne 0 ]; then
# exit
#fi
echo "DIFF Result (Eraser): $RES"
if [ -d out_test ]; then
rm -Rf out_test
fi
if [ -f eraser_test.art ]; then
rm -f eraser_test.art
fi
Property changes:
Added: svn:executable
+*
\ No newline at end of property
Added: svn:mime-type
+text/x-sh
\ No newline at end of property
/trunk/test_h/INFO
0,0 → 1,2
 
These tests test if the header files can be compiled standalone
/trunk/test_h/test.sh
0,0 → 1,25
#!/bin/bash
 
DIR=$( dirname "$0" )
cd "$DIR"
 
gcc --std=c99 test_bitmap.c
gcc --std=c99 test_utils.c
gcc --std=c99 test_ipe16_artfile.c
gcc --std=c99 test_ipe16_bmpexport.c
gcc --std=c99 test_ipe16_bmpimport.c
gcc --std=c99 test_ipe16_lzw_encoder.c
gcc --std=c99 test_ipe16_lzw_decoder.c
gcc --std=c99 test_ipe32_artfile.c
gcc --std=c99 test_ipe32_bmpexport.c
gcc --std=c99 test_ipe32_bmpimport.c
gcc --std=c99 test_ipe32_lzw_encoder.c
gcc --std=c99 test_ipe32_lzw_decoder.c
gcc --std=c99 test_ipe_artfile_packer_ipe16_ba.c
gcc --std=c99 test_ipe_artfile_packer_ipe16_pip.c
gcc --std=c99 test_ipe_artfile_packer_ipe32.c
gcc --std=c99 test_ipe_artfile_unpacker_ipe16.c
gcc --std=c99 test_ipe_artfile_unpacker_ipe32.c
 
rm a.out
 
Property changes:
Added: svn:executable
+*
\ No newline at end of property
Added: svn:mime-type
+text/x-sh
\ No newline at end of property
/trunk/test_h/test_bitmap.c
0,0 → 1,5
#include "../bitmap.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe16_artfile.c
0,0 → 1,5
#include "../ipe16_artfile.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe16_bmpexport.c
0,0 → 1,5
#include "../ipe16_bmpexport.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe16_bmpimport.c
0,0 → 1,5
#include "../ipe16_bmpimport.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe16_lzw_decoder.c
0,0 → 1,5
#include "../ipe16_lzw_decoder.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe16_lzw_encoder.c
0,0 → 1,5
#include "../ipe16_lzw_encoder.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe32_artfile.c
0,0 → 1,5
#include "../ipe32_artfile.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe32_bmpexport.c
0,0 → 1,5
#include "../ipe32_bmpexport.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe32_bmpimport.c
0,0 → 1,5
#include "../ipe32_bmpimport.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe32_lzw_decoder.c
0,0 → 1,5
#include "../ipe32_lzw_decoder.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe32_lzw_encoder.c
0,0 → 1,5
#include "../ipe32_lzw_encoder.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe_artfile_packer_ipe16_ba.c
0,0 → 1,5
#include "../ipe_artfile_packer_ipe16_ba.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe_artfile_packer_ipe16_pip.c
0,0 → 1,5
#include "../ipe_artfile_packer_ipe16_pip.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe_artfile_packer_ipe32.c
0,0 → 1,5
#include "../ipe_artfile_packer_ipe32.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe_artfile_unpacker_ipe16.c
0,0 → 1,5
#include "../ipe_artfile_unpacker_ipe16.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_ipe_artfile_unpacker_ipe32.c
0,0 → 1,5
#include "../ipe_artfile_unpacker_ipe32.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/test_h/test_utils.c
0,0 → 1,5
#include "../utils.h"
 
int main(int argc, char *argv[]) {
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/utils.c
0,0 → 1,33
#include <stdlib.h>
#include <stdio.h>
 
#include "utils.h"
 
size_t file_size(FILE* fp) {
size_t pos = ftell(fp);
fseek(fp, 0, SEEK_END);
size_t len = ftell(fp);
fseek(fp, pos, SEEK_SET);
return len;
}
 
char* sanitize_filename(char* picname) {
// Remove invalid windows characters
// TODO: not yet implemented. not necessary for the regular ART files, though
return picname;
}
 
void* app_zero_alloc(long bytes) {
char* ptr = (char*)malloc(bytes);
int i;
for (i=0; i<bytes; ++i) ptr[i] = 0;
return (void*)ptr;
}
 
unsigned char read_byte(FILE *file) {
int ch = getc(file);
if (ch == EOF)
ch = 0;
return ch;
}
 
Property changes:
Added: svn:mime-type
+text/x-csrc
\ No newline at end of property
/trunk/utils.h
0,0 → 1,12
#ifndef __inc__utils
#define __inc__utils
 
#include <stdio.h>
 
size_t file_size(FILE* fp);
char* sanitize_filename(char* picname);
void* app_zero_alloc(long bytes);
unsigned char read_byte(FILE *file);
 
#endif // #ifndef __inc__utils
 
Property changes:
Added: svn:mime-type
+text/x-chdr
\ No newline at end of property
/trunk/variable_prefixes.txt
0,0 → 1,7
My variable prefixes:
 
fit = text file input handle ("rt")
fib = binary file input handle ("rb")
 
fot = text file output handle ("ot")
fob = binary file output handle ("ob")
Property changes:
Added: svn:mime-type
+text/plain
\ No newline at end of property