]> git.street.me.uk Git - andy/viking.git/blobdiff - src/compression.c
vikcoord does not depend on GTK
[andy/viking.git] / src / compression.c
index 7b064f34982fc66dee52884f30ab456041738d1f..291e6c2b00cb2707095a67318194fc64d3dd2bd6 100644 (file)
 #include <zlib.h>
 #endif
 
-#include "compression.h"
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
 
+#include "compression.h"
+#include "string.h"
+#include <gio/gio.h>
+#include <glib/gstdio.h>
 
 #ifdef HAVE_LIBZ
 /* return size of unzip data or 0 if failed */
-/* can be made generic to uncompress zip, gzip, bzip2 data */
 static guint uncompress_data(void *uncompressed_buffer, guint uncompressed_size, void *compressed_data, guint compressed_size)
 {
        z_stream stream;
@@ -67,6 +72,13 @@ static guint uncompress_data(void *uncompressed_buffer, guint uncompressed_size,
 }
 #endif
 
+/**
+ * unzip_file:
+ * @zip_file:   pointer to start of compressed data
+ * @unzip_size: the size of the compressed data block
+ *
+ * Returns a pointer to uncompressed data (maybe NULL)
+ */
 void *unzip_file(gchar *zip_file, gulong *unzip_size)
 {
        void *unzip_data = NULL;
@@ -74,6 +86,7 @@ void *unzip_file(gchar *zip_file, gulong *unzip_size)
        goto end;
 #else
        gchar *zip_data;
+       // See http://en.wikipedia.org/wiki/Zip_(file_format)
        struct _lfh {
                guint32 sig;
                guint16 extract_version;
@@ -86,12 +99,16 @@ void *unzip_file(gchar *zip_file, gulong *unzip_size)
                guint32 uncompressed_size;
                guint16 filename_len;
                guint16 extra_field_len;
-       }  __attribute__ ((__packed__)) *local_file_header = NULL;
+       }  __attribute__ ((gcc_struct,__packed__)) *local_file_header = NULL;
 
+       if ( sizeof(struct _lfh) != 30 ) {
+               g_critical ("Incorrect internal zip header size, should be 30 but is %zd", sizeof(struct _lfh) );
+               goto end;
+       }
 
        local_file_header = (struct _lfh *) zip_file;
        if (GUINT32_FROM_LE(local_file_header->sig) != 0x04034b50) {
-               g_warning("%s(): wrong format", __PRETTY_FUNCTION__);
+               g_warning("%s(): wrong format (%d)", __PRETTY_FUNCTION__, GUINT32_FROM_LE(local_file_header->sig));
                g_free(unzip_data);
                goto end;
        }
@@ -102,6 +119,23 @@ void *unzip_file(gchar *zip_file, gulong *unzip_size)
        gulong uncompressed_size = GUINT32_FROM_LE(local_file_header->uncompressed_size);
        unzip_data = g_malloc(uncompressed_size);
 
+       // Protection against malloc failures
+       // ATM not normally been checking malloc failures in Viking but sometimes using zip files can be quite large
+       //  (e.g. when using DEMs) so more potential for failure.
+       if ( !unzip_data )
+               goto end;
+
+       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);
+
+       if ( GUINT16_FROM_LE(local_file_header->comp_method) == 0 &&
+               (uncompressed_size == GUINT32_FROM_LE(local_file_header->compressed_size)) ) {
+               // Stored only - no need to 'uncompress'
+               // Thus just copy
+               memcpy ( unzip_data, zip_data, uncompressed_size );
+               *unzip_size = uncompressed_size;
+               goto end;
+       }
+
        if (!(*unzip_size = uncompress_data(unzip_data, uncompressed_size, zip_data, GUINT32_FROM_LE(local_file_header->compressed_size)))) {
                g_free(unzip_data);
                unzip_data = NULL;
@@ -113,3 +147,86 @@ end:
        return(unzip_data);
 }
 
+/**
+ * uncompress_bzip2:
+ * @name: The name of the file to attempt to decompress
+ *
+ * Returns: The name of the uncompressed file (in a temporary location) or NULL
+ *   free the returned name after use.
+ *
+ * Also see: http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html
+ */
+gchar* uncompress_bzip2 ( gchar *name )
+{
+#ifdef HAVE_BZLIB_H
+       g_debug ( "%s: bzip2 %s", __FUNCTION__, BZ2_bzlibVersion() );
+
+       FILE *ff = g_fopen ( name, "rb" );
+       if ( !ff )
+               return NULL;
+
+       int     bzerror;
+       BZFILE* bf = BZ2_bzReadOpen ( &bzerror, ff, 0, 0, NULL, 0 ); // This should take care of the bz2 file header
+       if ( bzerror != BZ_OK ) {
+               BZ2_bzReadClose ( &bzerror, bf );
+               // handle error
+               g_warning ( "%s: BZ ReadOpen error on %s", __FUNCTION__, name );
+               return NULL;
+       }
+
+       GFileIOStream *gios;
+       GError *error = NULL;
+       gchar *tmpname = NULL;
+#if GLIB_CHECK_VERSION(2,32,0)
+       GFile *gf = g_file_new_tmp ( "vik-bz2-tmp.XXXXXX", &gios, &error );
+       tmpname = g_file_get_path (gf);
+#else
+       gint fd = g_file_open_tmp ( "vik-bz2-tmp.XXXXXX", &tmpname, &error );
+       if ( error ) {
+               g_warning ( error->message );
+               g_error_free ( error );
+               return NULL;
+       }
+       gios = g_file_open_readwrite ( g_file_new_for_path (tmpname), NULL, &error );
+       if ( error ) {
+               g_warning ( error->message );
+               g_error_free ( error );
+               return NULL;
+       }
+#endif
+
+       GOutputStream *gos = g_io_stream_get_output_stream ( G_IO_STREAM(gios) );
+
+       // Process in arbitary sized chunks
+       char buf[4096];
+       bzerror = BZ_OK;
+       int nBuf = 0;
+       // Now process the actual compression data
+       while ( bzerror == BZ_OK ) {
+               nBuf = BZ2_bzRead ( &bzerror, bf, buf, 4096 );
+               if ( bzerror == BZ_OK || bzerror == BZ_STREAM_END) {
+                       // do something with buf[0 .. nBuf-1]
+                       if ( g_output_stream_write ( gos, buf, nBuf, NULL, &error ) < 0 ) {
+                               g_critical ( "Couldn't write bz2 tmp %s file due to %s", tmpname, error->message );
+                               g_error_free (error);
+                               BZ2_bzReadClose ( &bzerror, bf );
+                               goto end;
+                       }
+               }
+       }
+       if ( bzerror != BZ_STREAM_END ) {
+               // handle error...
+               g_warning ( "%s: BZ error :( %d. read %d", __FUNCTION__, bzerror, nBuf );
+       }
+       BZ2_bzReadClose ( &bzerror, bf );
+       g_output_stream_close ( gos, NULL, &error );
+
+ end:
+       g_object_unref ( gios );
+       fclose ( ff );
+
+       return tmpname;
+#else
+       return NULL;
+#endif
+}