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 |