]> git.street.me.uk Git - andy/viking.git/blame - src/compression.c
Use our standard yes/no dialog box and enable i18n of the string.
[andy/viking.git] / src / compression.c
CommitLineData
ba048e02
RN
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2/*
3 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 *
5 * Copyright (C) 2003-2008, Evan Battaglia <gtoevan@gmx.net>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2013, Rob Norris <rw_norris@hotmail.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#ifdef HAVE_LIBZ
30#include <zlib.h>
31#endif
32
a3697549
RN
33#ifdef HAVE_BZLIB_H
34#include <bzlib.h>
35#endif
ba048e02 36
a3697549 37#include "compression.h"
d61ec067 38#include "string.h"
a3697549
RN
39#include <gio/gio.h>
40#include <glib/gstdio.h>
ba048e02
RN
41
42#ifdef HAVE_LIBZ
43/* return size of unzip data or 0 if failed */
ba048e02
RN
44static guint uncompress_data(void *uncompressed_buffer, guint uncompressed_size, void *compressed_data, guint compressed_size)
45{
46 z_stream stream;
47 int err;
48
49 stream.next_in = compressed_data;
50 stream.avail_in = compressed_size;
51 stream.next_out = uncompressed_buffer;
52 stream.avail_out = uncompressed_size;
53 stream.zalloc = (alloc_func)0;
54 stream.zfree = (free_func)0;
55 stream.opaque = (voidpf)0;
56
57 /* negative windowBits to inflateInit2 means "no header" */
58 if ((err = inflateInit2(&stream, -MAX_WBITS)) != Z_OK) {
59 g_warning("%s(): inflateInit2 failed", __PRETTY_FUNCTION__);
60 return 0;
61 }
62
63 err = inflate(&stream, Z_FINISH);
64 if ((err != Z_OK) && (err != Z_STREAM_END) && stream.msg) {
65 g_warning("%s() inflate failed err=%d \"%s\"", __PRETTY_FUNCTION__, err, stream.msg == NULL ? "unknown" : stream.msg);
66 inflateEnd(&stream);
67 return 0;
68 }
69
70 inflateEnd(&stream);
71 return(stream.total_out);
72}
73#endif
74
6a8c970e
RN
75/**
76 * unzip_file:
77 * @zip_file: pointer to start of compressed data
78 * @unzip_size: the size of the compressed data block
79 *
80 * Returns a pointer to uncompressed data (maybe NULL)
81 */
ba048e02
RN
82void *unzip_file(gchar *zip_file, gulong *unzip_size)
83{
84 void *unzip_data = NULL;
85#ifndef HAVE_LIBZ
86 goto end;
87#else
88 gchar *zip_data;
ac150134 89 // See http://en.wikipedia.org/wiki/Zip_(file_format)
ba048e02
RN
90 struct _lfh {
91 guint32 sig;
92 guint16 extract_version;
93 guint16 flags;
94 guint16 comp_method;
95 guint16 time;
96 guint16 date;
97 guint32 crc_32;
98 guint32 compressed_size;
99 guint32 uncompressed_size;
100 guint16 filename_len;
101 guint16 extra_field_len;
ac150134 102 } __attribute__ ((gcc_struct,__packed__)) *local_file_header = NULL;
ba048e02 103
ac150134
RN
104 if ( sizeof(struct _lfh) != 30 ) {
105 g_critical ("Incorrect internal zip header size, should be 30 but is %zd", sizeof(struct _lfh) );
106 goto end;
107 }
ba048e02
RN
108
109 local_file_header = (struct _lfh *) zip_file;
110 if (GUINT32_FROM_LE(local_file_header->sig) != 0x04034b50) {
ac150134 111 g_warning("%s(): wrong format (%d)", __PRETTY_FUNCTION__, GUINT32_FROM_LE(local_file_header->sig));
ba048e02
RN
112 g_free(unzip_data);
113 goto end;
114 }
115
116 zip_data = zip_file + sizeof(struct _lfh)
117 + GUINT16_FROM_LE(local_file_header->filename_len)
118 + GUINT16_FROM_LE(local_file_header->extra_field_len);
119 gulong uncompressed_size = GUINT32_FROM_LE(local_file_header->uncompressed_size);
120 unzip_data = g_malloc(uncompressed_size);
121
cf0d008e
RN
122 // Protection against malloc failures
123 // ATM not normally been checking malloc failures in Viking but sometimes using zip files can be quite large
124 // (e.g. when using DEMs) so more potential for failure.
125 if ( !unzip_data )
126 goto end;
127
d61ec067
RN
128 g_debug ("%s: method %d: from size %d to %ld", __FUNCTION__, GUINT16_FROM_LE(local_file_header->comp_method), GUINT32_FROM_LE(local_file_header->compressed_size), uncompressed_size);
129
130 if ( GUINT16_FROM_LE(local_file_header->comp_method) == 0 &&
131 (uncompressed_size == GUINT32_FROM_LE(local_file_header->compressed_size)) ) {
132 // Stored only - no need to 'uncompress'
133 // Thus just copy
134 memcpy ( unzip_data, zip_data, uncompressed_size );
135 *unzip_size = uncompressed_size;
136 goto end;
137 }
138
ba048e02
RN
139 if (!(*unzip_size = uncompress_data(unzip_data, uncompressed_size, zip_data, GUINT32_FROM_LE(local_file_header->compressed_size)))) {
140 g_free(unzip_data);
141 unzip_data = NULL;
142 goto end;
143 }
144
145#endif
146end:
147 return(unzip_data);
148}
149
a3697549
RN
150/**
151 * uncompress_bzip2:
152 * @name: The name of the file to attempt to decompress
153 *
154 * Returns: The name of the uncompressed file (in a temporary location) or NULL
155 * free the returned name after use.
156 *
157 * Also see: http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html
158 */
159gchar* uncompress_bzip2 ( gchar *name )
160{
161#ifdef HAVE_BZLIB_H
7184955f
RN
162 g_debug ( "%s: bzip2 %s", __FUNCTION__, BZ2_bzlibVersion() );
163
acb81075 164 FILE *ff = g_fopen ( name, "rb" );
a3697549
RN
165 if ( !ff )
166 return NULL;
167
168 int bzerror;
169 BZFILE* bf = BZ2_bzReadOpen ( &bzerror, ff, 0, 0, NULL, 0 ); // This should take care of the bz2 file header
170 if ( bzerror != BZ_OK ) {
171 BZ2_bzReadClose ( &bzerror, bf );
172 // handle error
563ef442 173 g_warning ( "%s: BZ ReadOpen error on %s", __FUNCTION__, name );
a3697549
RN
174 return NULL;
175 }
176
177 GFileIOStream *gios;
178 GError *error = NULL;
563ef442
RN
179 gchar *tmpname = NULL;
180#if GLIB_CHECK_VERSION(2,32,0)
a3697549 181 GFile *gf = g_file_new_tmp ( "vik-bz2-tmp.XXXXXX", &gios, &error );
563ef442
RN
182 tmpname = g_file_get_path (gf);
183#else
184 gint fd = g_file_open_tmp ( "vik-bz2-tmp.XXXXXX", &tmpname, &error );
185 if ( error ) {
186 g_warning ( error->message );
187 g_error_free ( error );
188 return NULL;
189 }
190 gios = g_file_open_readwrite ( g_file_new_for_path (tmpname), NULL, &error );
191 if ( error ) {
192 g_warning ( error->message );
193 g_error_free ( error );
194 return NULL;
195 }
196#endif
a3697549
RN
197
198 GOutputStream *gos = g_io_stream_get_output_stream ( G_IO_STREAM(gios) );
199
200 // Process in arbitary sized chunks
201 char buf[4096];
202 bzerror = BZ_OK;
7184955f 203 int nBuf = 0;
a3697549
RN
204 // Now process the actual compression data
205 while ( bzerror == BZ_OK ) {
206 nBuf = BZ2_bzRead ( &bzerror, bf, buf, 4096 );
207 if ( bzerror == BZ_OK || bzerror == BZ_STREAM_END) {
208 // do something with buf[0 .. nBuf-1]
209 if ( g_output_stream_write ( gos, buf, nBuf, NULL, &error ) < 0 ) {
210 g_critical ( "Couldn't write bz2 tmp %s file due to %s", tmpname, error->message );
211 g_error_free (error);
212 BZ2_bzReadClose ( &bzerror, bf );
213 goto end;
214 }
215 }
216 }
217 if ( bzerror != BZ_STREAM_END ) {
a3697549 218 // handle error...
7184955f 219 g_warning ( "%s: BZ error :( %d. read %d", __FUNCTION__, bzerror, nBuf );
a3697549 220 }
563ef442
RN
221 BZ2_bzReadClose ( &bzerror, bf );
222 g_output_stream_close ( gos, NULL, &error );
a3697549
RN
223
224 end:
acb81075
RN
225 g_object_unref ( gios );
226 fclose ( ff );
227
a3697549
RN
228 return tmpname;
229#else
230 return NULL;
231#endif
232}