Subversion Repositories ipe_artfile_utils

Rev

Blame | Last modification | View Log | RSS feed

  1. /**
  2.  * LZW Encoder for Imagination Pilots Entertainment 16-bit games (IPE16)
  3.  * - Blown Away - The Interactive Game by Imagination Pilots (BA)
  4.  * - Panic in the Park - The Interactive Game by Imagination Pilots (PiP)
  5.  * - Where's Waldo? At the Circus (Waldo1)
  6.  * ART file packer and unpacker by Daniel Marschall, ViaThinkSoft (C) 2014-2018
  7.  * Revision: 2018-02-15
  8.  *
  9.  * The code is based on "Cross platform GIF source code" (c) L. Patrick
  10.  * http://www.cs.usyd.edu.au/~graphapp/package/src/libgif/gif.c
  11.  * It was simplified and modified to encode IPE16-LZW instead of GIF-LZW.
  12.  * The game uses exactly the compressed stream as defined in the GIF standard,
  13.  * but the compressed stream is not divided into chunks.
  14.  **/
  15.  
  16. #include "ipe16_lzw_encoder.h"
  17. #include "utils.h"
  18.  
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <stdbool.h>
  22.  
  23. Ipe16LZWEncoder* new_ipe16lzw_encoder(void) {
  24.         return (Ipe16LZWEncoder*)app_zero_alloc(sizeof(Ipe16LZWEncoder));
  25. }
  26.  
  27. void del_ipe16lzw_encoder(Ipe16LZWEncoder* encoder) {
  28.         free(encoder);
  29. }
  30.  
  31. void ipe16lzw_write_code(FILE* outFile, Ipe16LZWEncoder* encoder, int code) {
  32.         if (code == FLUSH_OUTPUT) {
  33.                 /* write all remaining data */
  34.                 while (encoder->shift_state > 0) {
  35.                         fputc(encoder->shift_data & 0xff, outFile);
  36.                         encoder->shift_data >>= 8;
  37.                         encoder->shift_state -= 8;
  38.                 }
  39.                 encoder->shift_state = 0;
  40.         } else {
  41.                 encoder->shift_data |= ((long) code) << encoder->shift_state;
  42.                 encoder->shift_state += encoder->running_bits;
  43.  
  44.                 while (encoder->shift_state >= 8) {
  45.                         /* write full bytes */
  46.                         fputc(encoder->shift_data & 0xff, outFile);
  47.                         encoder->shift_data >>= 8;
  48.                         encoder->shift_state -= 8;
  49.                 }
  50.         }
  51.  
  52.         if (encoder->running_code >= encoder->max_code_plus_one && code <= LZ_MAX_CODE) {
  53.                 encoder->max_code_plus_one = 1 << ++encoder->running_bits;
  54.         }
  55. }
  56.  
  57. static void ipe16lzw_clear_hash_table(unsigned long* hash_table) {
  58.         int i;
  59.         for (i=0; i<HT_SIZE; i++)  {
  60.                 hash_table[i] = 0xFFFFFFFFL;
  61.         }
  62. }
  63.  
  64. void ipe16lzw_init_encoder(Ipe16LZWEncoder* encoder) {
  65.         encoder->running_code = FIRST_CODE;
  66.         encoder->running_bits = LZ_MIN_BITS;
  67.         encoder->max_code_plus_one = 1 << encoder->running_bits;
  68.         encoder->shift_state  = 0;
  69.         encoder->shift_data   = 0;
  70. }
  71.  
  72. static int ipe16lzw_hash_key(unsigned long key) {
  73.         return ((key >> 12) ^ key) & HT_KEY_MASK;
  74. }
  75.  
  76. static int ipe16lzw_lookup_hash(unsigned long* hash_table, unsigned long key) {
  77.         int hkey = ipe16lzw_hash_key(key);
  78.         unsigned long htkey;
  79.  
  80.         while ((htkey = HT_GET_KEY(hash_table[hkey])) != 0xFFFFFL) {
  81.                 if (key == htkey) {
  82.                         return HT_GET_CODE(hash_table[hkey]);
  83.                 }
  84.                 hkey = (hkey + 1) & HT_KEY_MASK;
  85.         }
  86.  
  87.         return -1;
  88. }
  89.  
  90. static void ipe16lzw_add_hash_entry(unsigned long* hash_table, unsigned long key, int code) {
  91.         int hkey = ipe16lzw_hash_key(key);
  92.  
  93.         while (HT_GET_KEY(hash_table[hkey]) != 0xFFFFFL) {
  94.                 hkey = (hkey + 1) & HT_KEY_MASK;
  95.         }
  96.         hash_table[hkey] = HT_PUT_KEY(key) | HT_PUT_CODE(code);
  97. }
  98.  
  99. void ipe16lzw_encode(FILE* outFile, Ipe16LZWEncoder* encoder, unsigned char* input, int inputLength) {
  100.         int i = 0, current_code, new_code;
  101.         unsigned long new_key;
  102.         unsigned char pixval;
  103.        
  104.         /* Init stuff */
  105.         ipe16lzw_init_encoder(encoder);
  106.         ipe16lzw_clear_hash_table(encoder->hash_table);
  107.         ipe16lzw_write_code(outFile, encoder, CLEAR_CODE);     
  108.  
  109.         if (inputLength == 0) return;
  110.         current_code = input[i++];
  111.  
  112.         while (i < inputLength) {
  113.                 pixval = input[i++]; /* Fetch next pixel from stream */
  114.  
  115.                 new_key = (((unsigned long) current_code) << 8) + pixval;
  116.                 if ((new_code = ipe16lzw_lookup_hash(encoder->hash_table, new_key)) >= 0) {
  117.                         current_code = new_code;
  118.                 } else {
  119.                         ipe16lzw_write_code(outFile, encoder, current_code);
  120.                         current_code = pixval;
  121.  
  122.                         if (encoder->running_code >= LZ_MAX_CODE) {
  123.                                 ipe16lzw_write_code(outFile, encoder, CLEAR_CODE);
  124.                                 encoder->running_code = FIRST_CODE;
  125.                                 encoder->running_bits = LZ_MIN_BITS;
  126.                                 encoder->max_code_plus_one = 1 << encoder->running_bits;
  127.                                 ipe16lzw_clear_hash_table(encoder->hash_table);
  128.                         } else {
  129.                                 /* Put this unique key with its relative code in hash table */
  130.                                 ipe16lzw_add_hash_entry(encoder->hash_table, new_key, encoder->running_code++);
  131.                         }
  132.                 }
  133.         }
  134.  
  135.         /* Flush */
  136.         ipe16lzw_write_code(outFile, encoder, current_code);
  137.         ipe16lzw_write_code(outFile, encoder, END_CODE);
  138.         ipe16lzw_write_code(outFile, encoder, FLUSH_OUTPUT);
  139. }
  140.  
  141.