"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / tests / test-msvba-zip.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <gsf/gsf-utils.h>
4 #include <gsf/gsf-msole-utils.h>
5 #include <gsf/gsf-input-stdio.h>
6 #include <gsf/gsf-output-stdio.h>
7
8 #define VBA_COMPRESSION_WINDOW 4096
9
10 #define HEADER_SIZE 3
11
12 /* Brute force and ugliness ! */
13 typedef struct {
14         guint8  inblock[VBA_COMPRESSION_WINDOW];
15         guint   length;
16         guint   pos;
17         guint8  shift;
18
19         guint8     mask;
20         GString   *outstr;
21
22         GsfOutput *output;
23 } CompressBuf;
24
25 #define DEBUG
26
27 static char
28 byte_to_char (guint8 data)
29 {
30         return data >= 0x20 && data < 126 ? data : '.';
31 }
32
33 static gint
34 get_shift (guint cur_pos)
35 {
36         int shift;
37
38         if (cur_pos <= 0x80) {
39                 if (cur_pos <= 0x20)
40                         shift = (cur_pos <= 0x10) ? 12 : 11;
41                 else
42                         shift = (cur_pos <= 0x40) ? 10 : 9;
43         } else {
44                 if (cur_pos <= 0x200)
45                         shift = (cur_pos <= 0x100) ? 8 : 7;
46                 else if (cur_pos <= 0x800)
47                         shift = (cur_pos <= 0x400) ? 6 : 5;
48                 else
49                         shift = 4;
50         }
51
52         return shift;
53 }
54
55 static gint
56 find_match (CompressBuf *buf, guint pos, guint *len)
57 {
58         gint i;
59         guint max_match = (1 << get_shift (pos)) - 1;
60
61         /* FIXME: the MS impl. does different to a linear search here
62            and is not very good at this either; is happy to get much
63            worse matches; perhaps some single-entry match lookup table ?
64            it seems to ~regularly truncate strings, and get earlier
65            / later matches of equivalent length with no predictability
66            ( hashing ? ).
67         */
68         for (i = pos - 1; i >= 0; i--) {
69                 guint j;
70
71                 for (j = 0; j < buf->length - pos && j < pos; j++)
72                         if (buf->inblock[pos + j] != buf->inblock[i + j])
73                                 break;
74
75                 if (j >= 3) {
76                         *len = MIN (j, max_match);
77                         return i;
78                 }
79         }
80         return -1;
81 }
82
83 static void
84 output_data (CompressBuf *buf, guint8 *data, gboolean compressed)
85 {
86         if (compressed) {
87                 buf->mask |= 1 << buf->shift;
88                 g_string_append_c (buf->outstr, data [0]);
89                 g_string_append_c (buf->outstr, data [1]);
90         } else
91                 g_string_append_c (buf->outstr, data [0]);
92
93         buf->shift++;
94         if (buf->shift == 8) {
95                 guint i;
96
97                 gsf_output_write (buf->output, 1, &buf->mask);
98                 gsf_output_write (buf->output, buf->outstr->len, buf->outstr->str);
99
100 #ifdef DEBUG            
101                 fprintf (stderr, "Block: 0x%x '", buf->mask);
102                 for (i = 0; i < buf->outstr->len; i++)
103                         fprintf (stderr, "%c", byte_to_char (buf->outstr->str[i]));
104                 fprintf (stderr, "'\n");
105 #endif
106
107                 buf->mask = 0;
108                 buf->shift = 0;
109                 g_string_set_size (buf->outstr, 0);
110         }
111 }
112
113 static void
114 output_match (CompressBuf *buf, guint cur_pos, guint pos, guint len)
115 {
116         int shift, token, distance;
117         guint8 data[2];
118
119         shift = get_shift (cur_pos);
120
121         distance = cur_pos - pos - 1;
122
123         /* Window size issue !? - get a better match later with a larger window !? */
124
125         token = (distance << shift) + ((len - 3) & ((1<<(shift + 1))-1));
126         data[0] = token & 0xff;
127         data[1] = token >> 8;
128
129 #ifdef DEBUG            
130         fprintf (stderr, "shift %d, [0x%x(%d) - %d], len %d, distance %d bytes %.2x %.2x\n",
131                  shift, cur_pos, cur_pos, pos, len, distance,
132                  data[0], data[1]);
133         if (cur_pos + len >= (1u<<shift))
134                 fprintf (stderr, "Overlaps boundary\n");
135 #endif
136
137         output_data (buf, data, TRUE);
138 }
139
140 static void
141 compress_block (CompressBuf *buf)
142 {
143         guint pos, len;
144         gint  match;
145
146         for (pos = 0; pos < buf->length;) {
147                 if ((match = find_match (buf, pos, &len)) >= 0) {
148                         output_match (buf, pos, match, len);
149                         pos += len;
150                 } else
151                         output_data (buf, &(buf->inblock[pos++]), FALSE);
152         }
153 }
154
155 static void
156 do_compress (GsfInput *input, GsfOutput *output)
157 {
158         CompressBuf real_buf, *buf;
159         GString *string;
160         guint8   data[HEADER_SIZE];
161         int      length;
162
163         buf = &real_buf;
164         memset (buf, 0, sizeof (CompressBuf));
165         buf->output = output;
166         buf->outstr = g_string_sized_new (20);
167
168         data[0] = 0x01;
169         data[1] = 0x00;
170         data[2] = 0xb0;
171         gsf_output_write (buf->output, 3, data); /* dummy padding */
172
173         string = g_string_sized_new (64);
174
175         while (gsf_input_remaining (input) > 0) {
176                 buf->length = MIN (gsf_input_remaining (input), VBA_COMPRESSION_WINDOW);
177                 if (!gsf_input_read (input, buf->length, buf->inblock))
178                         g_error ("Failed to read %d bytes\n", buf->length);
179                 compress_block (buf);
180         }
181
182         if (buf->outstr->len) {
183                 gsf_output_write (buf->output, 1, &buf->mask);
184                 gsf_output_write (buf->output, buf->outstr->len, buf->outstr->str);
185         }
186
187         length = gsf_output_size (buf->output) - 3 - 1;
188         if (length > 0x0c0c) /* TESTME: is this really right ? */
189                 length = 0x0c0c;
190         data[1] = length & 0xff;
191         data[2] |= (length >> 8);
192         gsf_output_seek (output, 0, G_SEEK_SET);
193         gsf_output_write (buf->output, 3, data); /* real data */
194 }
195
196 static void
197 do_decompress (GsfInput *input, GsfOutput *output)
198 {
199         gboolean err = FALSE;
200         guint8   data[HEADER_SIZE];
201         GByteArray *decompressed;
202         int      size, comp_len;
203
204         err |= !gsf_input_read (input, HEADER_SIZE, data);
205         if (data [0] != 0x01)
206                 fprintf (stderr, "Odd pre-amble byte 0x%x\n", data[0]);
207         if ((data [2] & 0xf0) != 0xb0)
208                 fprintf (stderr, "Odd high nibble 0x%x\n", (data[2] & 0xf0));
209         comp_len = ((data[2] & 0x0f) << 8) + data[1];
210         if (comp_len + 1 != gsf_input_size (input) - 3)
211                 fprintf (stderr, "Size mismatch %d %d\n",
212                          comp_len + 1, (int) (gsf_input_size (input) - 3));
213
214         decompressed = gsf_msole_inflate (input, 3);
215         if (!decompressed)
216                 fprintf (stderr, "Failed to decompress\n");
217
218         size = decompressed->len;
219         err |= !gsf_output_write (output, size,
220                 g_byte_array_free (decompressed, FALSE));
221
222         if (err)
223                 fprintf (stderr, "I/O error\n");
224 }
225
226 #define MAP(a,b)
227
228 static void
229 decode_dir (GsfInput *input)
230 {
231         gboolean err = FALSE;
232         guint8 data[6];
233         int    op_count = 0;
234
235         while (gsf_input_remaining (input) && !err) {
236                 unsigned i;
237                 guint16 op;
238                 guint32 length;
239                 gboolean ascii = FALSE;
240                 gboolean unicode = FALSE;
241                 gboolean offset = FALSE;
242
243                 err |= !gsf_input_read (input, 6, data);
244
245                 op     = GSF_LE_GET_GUINT16 (&data[0]);
246                 length = GSF_LE_GET_GUINT32 (&data[2]);
247
248                 if (op == 9) {
249                         fprintf (stderr, "** Quirk fix **\n");
250                         length += 2;
251                 }
252                 
253                 /* Special nasties / up-stream bugs */
254                 switch (op) {
255                 case 0x4:
256                 case 0x16:
257                 case 0x19:
258                 case 0x1a:
259                         ascii = TRUE;
260                         break;
261                 case 0x32:
262                 case 0x3e:
263                 case 0x47:
264                         unicode = TRUE;
265                         break;
266                 case 0x31:
267                         offset = TRUE;
268                         break;
269                 default:
270                         break;
271                 }
272
273                 fprintf (stderr, "0x%.6x Op %3d 0x%.2x, length %3d: '",
274                         (int)gsf_input_tell (input), op_count, op, length);
275
276                 if (length > gsf_input_remaining (input)) {
277                         fprintf (stderr, "Broken - foo !\n");
278                         length = MIN (64, gsf_input_remaining (input));
279                         err = TRUE;
280                 }
281
282                 if (ascii || unicode) {
283                         int advance = ascii ? 1 : 2;
284                         /* quick and dirty for now */
285                         for (i = 0 ; i < length; i += advance) {
286                                 guint8 ug;
287                                 err |= !gsf_input_read (input, advance, &ug);
288                                 fprintf (stderr, "%c", byte_to_char (ug));
289                         }
290                         fprintf (stderr, "' - '%s", ascii ? "Ascii" : "Unicode");
291                 } else if (offset) {
292                         gint8 data[4];
293                         guint32 offset;
294                         g_assert (length == 4);
295                         err |= !gsf_input_read (input, 4, data);
296                         offset = GSF_LE_GET_GUINT32 (data);
297                         fprintf (stderr, "0x%.8x' - 'Offset", offset);
298                 } else {
299                         GString *chars = g_string_new ("");
300
301                         for (i = 0 ; i < length; i++) {
302                                 guint8 ug;
303                                 err |= !gsf_input_read (input, 1, &ug);
304                                 fprintf (stderr, "%.2x ", ug);
305                                 g_string_append_printf (chars, "%c", byte_to_char (ug));
306                         }
307                         fprintf (stderr, "' - '%s", chars->str);
308                         g_string_free (chars, TRUE);
309                 }
310                 fprintf (stderr, "'\n");
311
312                 op_count++;
313         }
314 }
315
316 int
317 main (int argc, char *argv[])
318 {
319         int i;
320         char const *src = NULL;
321         char const *dest = NULL;
322         GsfInput *input;
323         GsfOutput *output;
324         GError   *error = NULL;
325
326         /* options */
327         gboolean dir = FALSE;
328         gboolean compress = FALSE;
329
330         gsf_init ();
331
332         for (i = 1; i < argc; i++) {
333                 if (argv[i][0] == '-') {
334                         switch (argv[i][1]) {
335                         case 'c':
336                                 compress = TRUE;
337                                 break;
338                         case 'd':
339                                 dir = TRUE;
340                                 break;
341                         default:
342                                 fprintf (stderr, "Unknown option '%s'\n", argv[i]);
343                                 return 1;
344                                 break;
345                         }
346                 } else if (!src)
347                         src = argv[i];
348                 else
349                         dest = argv[i];
350         }
351
352         if (!src || (!dir && !dest)) {
353                 fprintf (stderr, "%s: [-c(ompress)] <infile> <outfile>\n", argv[0]);
354                 fprintf (stderr, "%s: [-d(ecode dir)] <infile>\n", argv[0]);
355                 return 1;
356         }
357
358         input = gsf_input_stdio_new (src, &error);
359         if (dir)
360                 decode_dir (input);
361         else {
362                 output = gsf_output_stdio_new (dest, &error);
363                 if (!input || !output) {
364                         fprintf (stderr, "Failed to open input(%p)/output(%p): '%s'\n",
365                                  input, output, error ? error->message : "<NoMsg>");
366                         return 1;
367                 }
368                 
369                 if (compress)
370                         do_compress (input, output);
371                 else
372                         do_decompress (input, output);
373
374                 g_object_unref (output);
375         }
376
377         g_object_unref (input);
378
379         gsf_shutdown ();
380
381         return 0;
382 }