diff options
Diffstat (limited to 'navit/maptool/zip.c')
| -rw-r--r-- | navit/maptool/zip.c | 148 |
1 files changed, 139 insertions, 9 deletions
diff --git a/navit/maptool/zip.c b/navit/maptool/zip.c index f352e6353..9d034df71 100644 --- a/navit/maptool/zip.c +++ b/navit/maptool/zip.c @@ -17,6 +17,7 @@ * Boston, MA 02110-1301, USA. */ +#include <glib.h> #include <zlib.h> #include <string.h> #include <stdlib.h> @@ -24,6 +25,7 @@ #include "maptool.h" #include "config.h" #include "zipfile.h" +#include "attr.h" #ifdef HAVE_LIBCRYPTO #include <openssl/sha.h> @@ -51,7 +53,119 @@ struct zip_info { MD5_CTX md5_ctx; #endif int md5; + struct zip_hashed_cd *reference_map; }; +/* + * Helper structure to zip file handle with hashtable of local file header pointers +*/ +struct zip_hashed_cd { + struct file *fi; + /* hash table to access zip_lfh offsets by file name */ + GHashTable *lfh_offsets; +}; + +void +zip_set_reference_map(struct zip_info *zip_info, struct zip_hashed_cd *zhc) +{ + zip_info->reference_map=zhc; +} + +/** + * @brief fill zip_hashed_cd structure with references to tile file local file headers. + * @param map_filename name of file holding reference map + * @return Pointer to allocated and filled zip_hashed_cd structure. Free with zip_hashed_cd_free(). + */ +struct zip_hashed_cd * +zip_hashed_cd_new(char *map_filename) +{ + struct zip_eoc *eoc; + struct zip64_eoc *eoc64; + struct zip_cd *cd; + struct attr a={attr_cache, {0}}; + struct attr *opts[]={&a, NULL}; + struct zip_hashed_cd *zhc=g_new(struct zip_hashed_cd,1); + unsigned long long offset=0; + zhc->fi=file_create(map_filename,opts); + if(!zhc->fi) { + g_free(zhc); + return NULL; + } + eoc=zipfile_read_eoc(zhc->fi); + if(!eoc) { + file_destroy(zhc->fi); + g_free(zhc); + return NULL; + } + + eoc64=zipfile_read_eoc64(zhc->fi); + + zhc->lfh_offsets=g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + + while((cd=zipfile_read_cd(zhc->fi, eoc, eoc64, offset,-1))!=NULL) { + char *filename=g_strndup(cd->zipcfn, cd->zipcfnl); + if(!g_hash_table_insert(zhc->lfh_offsets, filename, (void*)zipfile_lfh_offset(cd))) + dbg(lvl_error,"Duplicate tile name: %s\n", filename); + offset+=sizeof(*cd)+zipfile_cd_name_and_extra_len(cd); + file_data_free(zhc->fi, (unsigned char *)cd); + } + + file_data_free(zhc->fi, (unsigned char *)eoc64); + file_data_free(zhc->fi, (unsigned char *)eoc); + + return zhc; +} + +void +zip_hashed_cd_free(struct zip_hashed_cd *zhc) +{ + if(zhc) { + file_destroy(zhc->fi); + g_hash_table_destroy(zhc->lfh_offsets); + g_free(zhc); + } +} + +/** + * @brief Adjust timestamp of a given file if it was already known in the reference version of mapfile. + * @param filename name of tile file being examined + * @param new_data uncompressed contents of the tile file being written + * @param new_len uncompressed size of the tile file being written + * @param zhc pointer to structure used to access reference map + * @param @out date pointer to variable containing date to adjust + * @param @out time pointer to variable containing time to adjust + * @return nothing. + */ +static void +adjust_timestamp(char *filename, void *new_data, int new_len, struct zip_hashed_cd *zhc, short *date, short *time) +{ + long long offset; + struct zip_lfh *lfh; + + if( !zhc || !zhc->fi || !zhc->lfh_offsets ) + return; + + if( !g_hash_table_lookup_extended(zhc->lfh_offsets, filename, NULL, (void **)&offset)) { + dbg(lvl_info,"Tile not found in reference map: %s\n",filename); + return; + } + + if( (lfh=zipfile_read_lfh(zhc->fi,offset)) == NULL ) { + dbg(lvl_error,"Error reading tile header %s from reference map at offset "LONGLONG_FMT"\n",filename, offset); + return; + } + + if( lfh->zipuncmp==new_len ) { + void *old_data=zipfile_read_content(zhc->fi, offset, lfh, NULL); + if( old_data && memcmp(old_data, new_data, new_len)==0 ) { + dbg(lvl_debug,"Adjusting timestamp for tile %s\n",filename); + *date=lfh->zipdate; + *time=lfh->ziptime; + } + file_data_free(zhc->fi, (unsigned char *)old_data); + } + + file_data_free(zhc->fi, (unsigned char *)lfh); +} static int zip_write(struct zip_info *info, void *data, int len) @@ -97,6 +211,17 @@ compress2_int(Byte *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, } #endif +/** + * @brief Write a single member file to the zip file, compressing and encrypting it on the way. + * Supports zip64 in the sense of handling zip files above 4Gb, but does not allow individual files above zip32 limits. + * Does not support writing multivolume zip files. + * @param zip_info pointer to structure holding global zip file informations + * @param name name of file being written (not necessarily nul-terminated) + * @param filelen length of file name to pad to + * @param data uncopmpressed contents of file being written + * @param data_size uncopmpressed length of file being written + * @return nothing. + */ void write_zipmember(struct zip_info *zip_info, char *name, int filelen, char *data, int data_size) { @@ -121,8 +246,8 @@ write_zipmember(struct zip_info *zip_info, char *name, int filelen, char *data, 0x00, 0x0000, 0x0, - zip_info->time, - zip_info->date, + 0, + 0, 0x0, 0x0, 0x0, @@ -155,6 +280,18 @@ write_zipmember(struct zip_info *zip_info, char *name, int filelen, char *data, uLongf destlen=data_size+data_size/500+12; char *compbuffer; + filename=g_alloca(filelen+1); + g_strlcpy(filename, name, filelen+1); + len=strlen(filename); + while (len < filelen) { + filename[len++]='_'; + } + filename[filelen]='\0'; + + adjust_timestamp(filename, data, data_size, zip_info->reference_map, &lfh.zipdate, &lfh.ziptime); + cd.zipdat=lfh.zipdate; + cd.ziptim=lfh.ziptime; + compbuffer = malloc(destlen); if (!compbuffer) { fprintf(stderr, "No more memory.\n"); @@ -215,13 +352,6 @@ write_zipmember(struct zip_info *zip_info, char *name, int filelen, char *data, cd.zipcflg|=1; } #endif - filename=g_alloca(filelen+1); - strcpy(filename, name); - len=strlen(filename); - while (len < filelen) { - filename[len++]='_'; - } - filename[filelen]='\0'; zip_write(zip_info, &lfh, sizeof(lfh)); zip_write(zip_info, filename, filelen); zip_info->offset+=sizeof(lfh)+filelen; |
