Subversion Repositories ipe_artfile_utils

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 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