Improve internal data structures.  New extra field API (read only).
Added paranoid mode to zipcmp (compare more meta data).

Keep up-to-date metadata for changed entries, avoid parallel arrays.
XXX: Modifying extra fields API functions are stubs.  Old API removed.
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 441fba3..b8ad48f 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -55,12 +55,12 @@
 SET(LIBZIP_SOURCES
   zip_add.c
   zip_add_dir.c
+  zip_add_entry.c
   zip_close.c
   zip_delete.c
   zip_dirent.c
   zip_discard.c
-  zip_entry_free.c
-  zip_entry_new.c
+  zip_entry.c
   zip_err_str.c
   zip_error.c
   zip_error_clear.c
@@ -68,6 +68,8 @@
   zip_error_get_sys_type.c
   zip_error_strerror.c
   zip_error_to_str.c
+  zip_extra_field.c
+  zip_extra_field_api.c
   zip_fclose.c
   zip_fdopen.c
   zip_file_error_clear.c
@@ -84,9 +86,7 @@
   zip_get_archive_flag.c
   zip_get_compression_implementation.c
   zip_get_encryption_implementation.c
-  zip_get_extra_field_by_id.c
   zip_get_file_comment.c
-  zip_get_file_extra.c
   zip_get_name.c
   zip_get_num_entries.c
   zip_get_num_files.c
@@ -101,7 +101,6 @@
   zip_set_default_password.c
   zip_set_file_comment.c
   zip_set_file_compression.c
-  zip_set_file_extra.c
   zip_set_name.c
   zip_source_buffer.c
   zip_source_close.c
@@ -125,6 +124,7 @@
   zip_stat_index.c
   zip_stat_init.c
   zip_strerror.c
+  zip_string.c
   zip_unchange.c
   zip_unchange_all.c
   zip_unchange_archive.c
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 00394ce..de5c718 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -12,12 +12,12 @@
 libzip_la_SOURCES=\
 	zip_add.c \
 	zip_add_dir.c \
+	zip_add_entry.c \
 	zip_close.c \
 	zip_delete.c \
 	zip_dirent.c \
 	zip_discard.c \
-	zip_entry_free.c \
-	zip_entry_new.c \
+	zip_entry.c \
 	zip_err_str.c \
 	zip_error.c \
 	zip_error_clear.c \
@@ -25,6 +25,8 @@
 	zip_error_get_sys_type.c \
 	zip_error_strerror.c \
 	zip_error_to_str.c \
+	zip_extra_field.c \
+	zip_extra_field_api.c \
 	zip_fclose.c \
 	zip_fdopen.c \
 	zip_file_error_clear.c \
@@ -41,9 +43,7 @@
 	zip_get_archive_flag.c \
 	zip_get_compression_implementation.c \
 	zip_get_encryption_implementation.c \
-	zip_get_extra_field_by_id.c \
 	zip_get_file_comment.c \
-	zip_get_file_extra.c \
 	zip_get_num_entries.c \
 	zip_get_num_files.c \
 	zip_get_name.c \
@@ -58,7 +58,7 @@
 	zip_set_default_password.c \
 	zip_set_file_comment.c \
 	zip_set_file_compression.c \
-	zip_set_file_extra.c \
+	zip_set_name.c \
 	zip_source_buffer.c \
 	zip_source_close.c \
 	zip_source_crc.c \
@@ -77,11 +77,11 @@
 	zip_source_window.c \
 	zip_source_zip.c \
 	zip_source_zip_new.c \
-	zip_set_name.c \
 	zip_stat.c \
 	zip_stat_index.c \
 	zip_stat_init.c \
 	zip_strerror.c \
+	zip_string.c \
 	zip_unchange.c \
 	zip_unchange_all.c \
 	zip_unchange_archive.c \
diff --git a/lib/zip.h b/lib/zip.h
index 10e2f36..2edec7b 100644
--- a/lib/zip.h
+++ b/lib/zip.h
@@ -71,11 +71,12 @@
 #define ZIP_FL_COMPRESSED	4 /* read compressed data */
 #define ZIP_FL_UNCHANGED	8 /* use original data, ignoring changes */
 #define ZIP_FL_RECOMPRESS      16 /* force recompression of data */
-#define ZIP_FL_ENCRYPTED       32 /* read encrypted data
-				     (implies ZIP_FL_COMPRESSED) */
+#define ZIP_FL_ENCRYPTED       32 /* read encrypted data (implies ZIP_FL_COMPRESSED) */
 #define ZIP_FL_NAME_GUESS       0 /* guess name encoding (is default) */
 #define ZIP_FL_NAME_RAW        64 /* get unmodified name */
 #define ZIP_FL_NAME_STRICT    128 /* follow specification strictly */
+#define ZIP_FL_LOCAL	      256 /* in local header */
+#define ZIP_FL_CENTRAL	      512 /* in central directory */
 
 /* archive global flags flags */
 
@@ -83,6 +84,10 @@
 #define ZIP_AFL_RDONLY		2 /* read only -- cannot be cleared */
 
 
+/* create a new extra field */
+
+#define ZIP_EXTRA_FIELD_NEW	ZIP_UINT16_MAX
+
 /* flags for compression and encryption sources */
 
 #define ZIP_CODEC_DECODE	0 /* decompress/decrypt (encode flag not set) */
@@ -218,6 +223,8 @@
 ZIP_EXTERN int zip_close(struct zip *);
 ZIP_EXTERN void zip_discard(struct zip *);
 ZIP_EXTERN int zip_delete(struct zip *, zip_uint64_t);
+ZIP_EXTERN int zip_delete_file_extra_field(struct zip *, zip_uint64_t, zip_uint32_t, zip_uint16_t);
+ZIP_EXTERN int zip_delete_file_extra_field_by_id(struct zip *, zip_uint64_t, zip_uint32_t, zip_uint16_t, zip_uint16_t);
 ZIP_EXTERN void zip_error_clear(struct zip *);
 ZIP_EXTERN void zip_error_get(struct zip *, int *, int *);
 ZIP_EXTERN int zip_error_get_sys_type(int);
@@ -239,8 +246,10 @@
 ZIP_EXTERN int zip_get_archive_flag(struct zip *, int, int);
 ZIP_EXTERN const char *zip_get_file_comment(struct zip *, zip_uint64_t,
 					    int *, int);
-ZIP_EXTERN const char *zip_get_file_extra(struct zip *, zip_uint64_t,
-					  int *, int);
+ZIP_EXTERN const zip_uint8_t *zip_get_file_extra_field(struct zip *, zip_uint64_t, zip_uint32_t, zip_uint16_t, zip_uint16_t *, zip_uint16_t *);
+ZIP_EXTERN const zip_uint8_t *zip_get_file_extra_field_by_id(struct zip *, zip_uint64_t, zip_uint32_t, zip_uint16_t, zip_uint16_t, zip_uint16_t *);
+ZIP_EXTERN zip_int16_t zip_get_file_num_extra_fields(struct zip *, zip_uint64_t, zip_uint32_t);
+ZIP_EXTERN zip_int16_t zip_get_file_num_extra_fields_by_id(struct zip *, zip_uint64_t, zip_uint32_t, zip_uint16_t);
 ZIP_EXTERN const char *zip_get_name(struct zip *, zip_uint64_t, int);
 ZIP_EXTERN zip_uint64_t zip_get_num_entries(struct zip *, int);
 ZIP_EXTERN int zip_get_num_files(struct zip *);  /* deprecated, use zip_get_num_entries instead */
@@ -255,8 +264,7 @@
 				    const char *, int);
 ZIP_EXTERN int zip_set_file_compression(struct zip *, zip_uint64_t,
 					zip_int32_t, zip_uint32_t);
-ZIP_EXTERN int zip_set_file_extra(struct zip *, zip_uint64_t,
-				  const char *, int);
+ZIP_EXTERN int zip_set_file_extra_field(struct zip *, zip_uint64_t, zip_uint32_t, zip_uint16_t, zip_uint16_t, const zip_uint8_t *, zip_uint16_t);
 ZIP_EXTERN struct zip_source *zip_source_buffer(struct zip *, const void *,
 						zip_uint64_t, int);
 ZIP_EXTERN struct zip_source *zip_source_file(struct zip *, const char *,
diff --git a/lib/zip_add.c b/lib/zip_add.c
index 6a310a8..3c22ddc 100644
--- a/lib/zip_add.c
+++ b/lib/zip_add.c
@@ -34,7 +34,6 @@
 
 
 #include "zipint.h"
-
 
 
 /*
diff --git a/lib/zip_entry_free.c b/lib/zip_add_entry.c
similarity index 69%
copy from lib/zip_entry_free.c
copy to lib/zip_add_entry.c
index f801855..f3ba576 100644
--- a/lib/zip_entry_free.c
+++ b/lib/zip_add_entry.c
@@ -1,6 +1,6 @@
 /*
-  zip_entry_free.c -- free struct zip_entry
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  zip_add_entry.c -- create and init struct zip_entry
+  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -39,16 +39,28 @@
 
 
 
-void
-_zip_entry_free(struct zip_entry *ze)
-{
-    if (ze->changes.valid & ZIP_DIRENT_FILENAME)
-	free(ze->changes.filename);
-    if (ze->changes.valid & ZIP_DIRENT_EXTRAFIELD)
-	free(ze->changes.extrafield);
-    if (ze->changes.valid & ZIP_DIRENT_COMMENT)
-	free(ze->changes.comment);
-    ze->changes.valid = 0;
+/* NOTE: Signed due to -1 on error.  See zip_add.c for more details. */
 
-    _zip_unchange_data(ze);
+zip_int64_t
+_zip_add_entry(struct zip *za)
+{
+    zip_uint64_t idx;
+
+    if (za->nentry+1 >= za->nentry_alloc) {
+	struct zip_entry *rentries;
+	zip_uint64_t nalloc = za->nentry_alloc + 16;
+	rentries = (struct zip_entry *)realloc(za->entry, sizeof(struct zip_entry) * nalloc);
+	if (!rentries) {
+	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+	    return -1;
+	}
+	za->entry = rentries;
+	za->nentry_alloc = nalloc;
+    }
+
+    idx = za->nentry++;
+
+    _zip_entry_init(za->entry+idx);
+
+    return idx;
 }
diff --git a/lib/zip_close.c b/lib/zip_close.c
index 123f919..00aa7da 100644
--- a/lib/zip_close.c
+++ b/lib/zip_close.c
@@ -49,24 +49,18 @@
 #include <fcntl.h>
 #endif
 
+
+
 static int add_data(struct zip *, struct zip_source *, struct zip_dirent *,
 		    FILE *);
 static int copy_data(FILE *, off_t, FILE *, struct zip_error *);
 static int copy_source(struct zip *, struct zip_source *, FILE *);
-static int write_cdir(struct zip *, struct zip_cdir *, FILE *);
-static int _zip_cdir_set_comment(struct zip_cdir *, struct zip *);
+static int write_cdir(struct zip *, const struct zip_filelist *, int, FILE *);
 static char *_zip_create_temp_output(struct zip *, FILE **);
 static int _zip_torrentzip_cmp(const void *, const void *);
 
 
 
-struct filelist {
-    int idx;
-    const char *name;
-};
-
-
-
 ZIP_EXTERN int
 zip_close(struct zip *za)
 {
@@ -77,15 +71,14 @@
 #ifndef _WIN32
     mode_t mask;
 #endif
-    struct zip_cdir *cd;
-    struct zip_dirent de;
-    struct filelist *filelist;
+    struct zip_filelist *filelist;
     int reopen_on_error;
     int new_torrentzip;
     enum zip_encoding_type com_enc, enc;
-    int changed;
+    int changed, is_zip64;
 
     reopen_on_error = 0;
+    is_zip64 = 0; /* for wiz */
 
     if (za == NULL)
 	return -1;
@@ -109,40 +102,21 @@
 	return 0;
     }
 
-    if ((filelist=(struct filelist *)malloc(sizeof(filelist[0])*survivors))
+    if ((filelist=(struct zip_filelist *)malloc(sizeof(filelist[0])*survivors))
 	== NULL)
 	return -1;
 
-    if ((cd=_zip_cdir_new(survivors, &za->error)) == NULL) {
-	free(filelist);
-	return -1;
-    }
-
-    for (i=0; i<survivors; i++)
-	_zip_dirent_init(&cd->entry[i]);
-
     /* archive comment is special for torrentzip */
     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) {
-	cd->comment = _zip_memdup(TORRENT_SIG "XXXXXXXX",
-				  TORRENT_SIG_LEN + TORRENT_CRC_LEN,
-				  &za->error);
-	if (cd->comment == NULL) {
-	    _zip_cdir_free(cd);
-	    free(filelist);
-	    return -1;
-	}
-	cd->comment_len = TORRENT_SIG_LEN + TORRENT_CRC_LEN;
-    }
-    else if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, ZIP_FL_UNCHANGED) == 0) {
-	if (_zip_cdir_set_comment(cd, za) == -1) {
-	    _zip_cdir_free(cd);
+	/* XXX: use internal function when zip_set_archive_comment clears TORRENT flag */
+	if (zip_set_archive_comment(za, TORRENT_SIG "XXXXXXXX", TORRENT_SIG_LEN + TORRENT_CRC_LEN) < 0) {
 	    free(filelist);
 	    return -1;
 	}
     }
+    /* XXX: if no longer torrentzip and archive comment not changed by user, delete it */
 
     if ((temp=_zip_create_temp_output(za, &out)) == NULL) {
-	_zip_cdir_free(cd);
 	free(filelist);
 	return -1;
     }
@@ -150,7 +124,7 @@
 
     /* create list of files with index into original archive  */
     for (i=j=0; i<za->nentry; i++) {
-	if (za->entry[i].state == ZIP_ST_DELETED)
+	if (za->entry[i].deleted)
 	    continue;
 
 	filelist[j].idx = i;
@@ -167,119 +141,55 @@
     error = 0;
     for (j=0; j<survivors; j++) {
 	int new_data;
+	struct zip_entry *entry;
+	struct zip_dirent *de;
 
 	i = filelist[j].idx;
+	entry = za->entry+i;
 
-	new_data = (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip || (za->entry[i].changes.valid & ZIP_DIRENT_COMP_METHOD));
+	new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || new_torrentzip || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD));
 
 	/* create new local directory entry */
-	if (new_data) {
-	    _zip_dirent_init(&de);
-
-	    if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
-		_zip_dirent_torrent_normalize(&de);
-		
-	    if (za->entry[i].changes.valid & ZIP_DIRENT_COMP_METHOD) {
-		de.settable.comp_method = za->entry[i].changes.comp_method;
-		de.settable.valid |= ZIP_DIRENT_COMP_METHOD;
-	    }
-
-	    /* use it as central directory entry */
-	    memcpy(cd->entry+j, &de, sizeof(cd->entry[j]));
-
-	    /* set/update file name */
-	    if ((za->entry[i].changes.valid & ZIP_DIRENT_FILENAME) == 0) {
-		de.settable.filename = strdup(za->cdir->entry[i].settable.filename);
-		cd->entry[j].settable.filename = za->cdir->entry[i].settable.filename;
-		de.settable.valid |= ZIP_DIRENT_FILENAME;
-		cd->entry[j].settable.valid |= ZIP_DIRENT_FILENAME;
+	if (entry->changes == NULL) {
+	    if ((entry->changes=_zip_dirent_clone(entry->orig)) == NULL) {
+		/* XXX: error */
 	    }
 	}
-	else {
-	    /* copy existing directory entries */
-	    if (fseeko(za->zp, za->cdir->entry[i].offset, SEEK_SET) != 0) {
-		_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
-		error = 1;
-		break;
-	    }
-	    if (_zip_dirent_read(&de, za->zp, NULL, NULL, 1,
-				 &za->error) != 0) {
-		error = 1;
-		break;
-	    }
-	    memcpy(cd->entry+j, za->cdir->entry+i, sizeof(cd->entry[j]));
-	    if (de.bitflags & ZIP_GPBF_DATA_DESCRIPTOR) {
-		de.crc = za->cdir->entry[i].crc;
-		de.comp_size = za->cdir->entry[i].comp_size;
-		de.uncomp_size = za->cdir->entry[i].uncomp_size;
-		de.bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR;
-		cd->entry[j].bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR;
-	    }
+	de = entry->changes;
+
+	if (_zip_read_local_ef(za, i) < 0) {
+	    error = 1;
+	    break;
 	}
 
-	if (za->entry[i].changes.valid & ZIP_DIRENT_FILENAME) {
-	    free(de.settable.filename);
-	    if ((de.settable.filename=strdup(za->entry[i].changes.filename)) == NULL) {
-		error = 1;
-		break;
-	    }
-	    cd->entry[j].settable.filename = za->entry[i].changes.filename;
-	}
-
-	if (za->entry[i].changes.valid & ZIP_DIRENT_EXTRAFIELD) {
-	    free(de.settable.extrafield);
-	    if (za->entry[i].changes.extrafield_len > 0) {
-	        if ((de.settable.extrafield=malloc(za->entry[i].changes.extrafield_len)) == NULL) {
-		    error = 1;
-		    break;
-	        }
-	        memcpy(de.settable.extrafield, za->entry[i].changes.extrafield, za->entry[i].changes.extrafield_len);
-            }
-	    else
-		de.settable.extrafield = NULL;
-	    de.settable.extrafield_len = za->entry[i].changes.extrafield_len;
-	    /* as the rest of cd entries, its malloc/free is done by za */
-	    /* TODO unsure if this should also be set in the CD --
-	     * not done for now
-	    cd->entry[j].settable.extrafield = za->entry[i].changes.extrafield;
-	    cd->entry[j].settable.extrafield_len = za->entry[i].changes.extrafield_len;
-	    */
-	}
-
-	if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0
-	    && za->entry[i].changes.valid & ZIP_DIRENT_COMMENT) {
-	    /* as the rest of cd entries, its malloc/free is done by za */
-	    cd->entry[j].settable.comment = za->entry[i].changes.comment;
-	    cd->entry[j].settable.comment_len = za->entry[i].changes.comment_len;
-	}
+	if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
+	    _zip_dirent_torrent_normalize(entry->changes);
 
 	/* set general purpose bit flag for file name/comment encoding */
-	enc = _zip_guess_encoding(de.settable.filename,
-				  strlen(de.settable.filename));
-	com_enc = _zip_guess_encoding(cd->entry[j].settable.comment,
-				      cd->entry[j].settable.comment_len);
-	if ((enc == ZIP_ENCODING_UTF8  && com_enc == ZIP_ENCODING_ASCII) ||
-	    (enc == ZIP_ENCODING_ASCII && com_enc == ZIP_ENCODING_UTF8 ) ||
-	    (enc == ZIP_ENCODING_UTF8  && com_enc == ZIP_ENCODING_UTF8 ))
-	    de.bitflags |= ZIP_GPBF_ENCODING_UTF_8;
+	enc = _zip_guess_encoding(de->filename, ZIP_ENCODING_UNKNOWN);
+	com_enc = _zip_guess_encoding(de->comment, ZIP_ENCODING_UNKNOWN);
+	if ((enc == ZIP_ENCODING_UTF8_KNOWN  && com_enc == ZIP_ENCODING_ASCII) ||
+	    (enc == ZIP_ENCODING_ASCII && com_enc == ZIP_ENCODING_UTF8_KNOWN ) ||
+	    (enc == ZIP_ENCODING_UTF8_KNOWN  && com_enc == ZIP_ENCODING_UTF8_KNOWN ))
+	    de->bitflags |= ZIP_GPBF_ENCODING_UTF_8;
 	else
-	    de.bitflags &= ~ZIP_GPBF_ENCODING_UTF_8;
-	cd->entry[j].bitflags = de.bitflags;
+	    de->bitflags &= ~ZIP_GPBF_ENCODING_UTF_8;
 
-	cd->entry[j].offset = ftello(out);
+	de->offset = ftello(out);
 
 	if (new_data) {
 	    struct zip_source *zs;
 
 	    zs = NULL;
-	    if (!ZIP_ENTRY_DATA_CHANGED(za->entry+i)) {
+	    if (!ZIP_ENTRY_DATA_CHANGED(entry)) {
 		if ((zs=_zip_source_zip_new(za, za, i, ZIP_FL_UNCHANGED, 0, -1, NULL)) == NULL) {
 		    error = 1;
 		    break;
 		}
 	    }
 
-	    if (add_data(za, zs ? zs : za->entry[i].source, &de, out) < 0) {
+	    /* add_data writes dirent */
+	    if (add_data(za, zs ? zs : entry->source, de, out) < 0) {
 		error = 1;
 		if (zs)
 		    zip_source_free(zs);
@@ -287,42 +197,28 @@
 	    }
 	    if (zs)
 		zip_source_free(zs);
-	    
-	    cd->entry[j].last_mod = de.last_mod;
-	    cd->entry[j].settable.comp_method = de.settable.comp_method;
-	    cd->entry[j].comp_size = de.comp_size;
-	    cd->entry[j].uncomp_size = de.uncomp_size;
-	    cd->entry[j].crc = de.crc;
 	}
 	else {
-	    if (_zip_dirent_write(&de, out, 1, &za->error) < 0) {
+	    if (_zip_dirent_write(de, out, 1, &za->error) < 0) {
 		error = 1;
 		break;
 	    }
 	    /* we just read the local dirent, file is at correct position */
-	    if (copy_data(za->zp, cd->entry[j].comp_size, out,
-			  &za->error) < 0) {
+	    if (copy_data(za->zp, de->comp_size, out, &za->error) < 0) {
 		error = 1;
 		break;
 	    }
 	}
+    }
 
-	_zip_dirent_finalize(&de);
+    if (!error) {
+	if (write_cdir(za, filelist, survivors, out) < 0)
+	    error = 1;
     }
 
     free(filelist);
 
-    if (!error) {
-	if (write_cdir(za, cd, out) < 0)
-	    error = 1;
-    }
-   
-    /* pointers in cd entries are owned by za */
-    cd->nentry = 0;
-    _zip_cdir_free(cd);
-
     if (error) {
-	_zip_dirent_finalize(&de);
 	fclose(out);
 	remove(temp);
 	free(temp);
@@ -366,8 +262,7 @@
 
 
 static int
-add_data(struct zip *za, struct zip_source *src, struct zip_dirent *de,
-	 FILE *ft)
+add_data(struct zip *za, struct zip_source *src, struct zip_dirent *de, FILE *ft)
 {
     off_t offstart, offdata, offend;
     struct zip_stat st;
@@ -389,12 +284,12 @@
 	st.comp_method = ZIP_CM_STORE;
     }
 
-    if ((de->settable.valid & ZIP_DIRENT_COMP_METHOD) == 0 || de->settable.comp_method == ZIP_CM_DEFAULT) {
-	de->settable.valid |= ZIP_DIRENT_COMP_METHOD;
-	de->settable.comp_method = ZIP_CM_DEFLATE;
+    if (de->comp_method == ZIP_CM_DEFAULT) {
+	/* XXX: set changed? */
+	de->comp_method = ZIP_CM_DEFLATE;
     }
 
-    if (st.comp_method != de->settable.comp_method) {
+    if (st.comp_method != de->comp_method || st.comp_method == ZIP_CM_STORE) {
 	struct zip_source *s_store, *s_crc;
 	zip_compression_implementation comp_impl;
 	
@@ -418,15 +313,15 @@
 	}
 
 	/* XXX: deflate 0-byte files for torrentzip? */
-	if (de->settable.comp_method != ZIP_CM_STORE && ((st.valid & ZIP_STAT_SIZE) == 0 || st.size != 0)) {
-	    if ((comp_impl=zip_get_compression_implementation(de->settable.comp_method)) == NULL) {
+	if (de->comp_method != ZIP_CM_STORE && ((st.valid & ZIP_STAT_SIZE) == 0 || st.size != 0)) {
+	    if ((comp_impl=zip_get_compression_implementation(de->comp_method)) == NULL) {
 		_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
 		zip_source_pop(s_crc);
 		if (s_store != src)
 		    zip_source_pop(s_store);
 		return -1;
 	    }
-	    if ((s2=comp_impl(za, s_crc, de->settable.comp_method, ZIP_CODEC_ENCODE)) == NULL) {
+	    if ((s2=comp_impl(za, s_crc, de->comp_method, ZIP_CODEC_ENCODE)) == NULL) {
 		zip_source_pop(s_crc);
 		if (s_store != src)
 		    zip_source_pop(s_store);
@@ -465,7 +360,7 @@
     }
 
     de->last_mod = st.mtime;
-    de->settable.comp_method = st.comp_method;
+    de->comp_method = st.comp_method;
     de->crc = st.crc;
     de->uncomp_size = st.size;
     de->comp_size = offend - offdata;
@@ -554,29 +449,32 @@
 
 
 static int
-write_cdir(struct zip *za, struct zip_cdir *cd, FILE *out)
+write_cdir(struct zip *za, const struct zip_filelist *filelist, int survivors, FILE *out)
 {
-    off_t offset;
+    off_t cd_start, end;
+    zip_int64_t size;
     uLong crc;
     char buf[TORRENT_CRC_LEN+1];
     
-    if (_zip_cdir_write(cd, out, &za->error) < 0)
+    cd_start = ftello(out);
+
+    if ((size=_zip_cdir_write(za, filelist, survivors, out)) < 0)
 	return -1;
     
+    end = ftello(out);
+
     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0)
 	return 0;
 
 
     /* fix up torrentzip comment */
 
-    offset = ftello(out);
-
-    if (_zip_filerange_crc(out, cd->offset, cd->size, &crc, &za->error) < 0)
+    if (_zip_filerange_crc(out, cd_start, size, &crc, &za->error) < 0)
 	return -1;
 
     snprintf(buf, sizeof(buf), "%08lX", (long)crc);
 
-    if (fseeko(out, offset-TORRENT_CRC_LEN, SEEK_SET) < 0) {
+    if (fseeko(out, end-TORRENT_CRC_LEN, SEEK_SET) < 0) {
 	_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
 	return -1;
     }
@@ -591,35 +489,6 @@
 
 
 
-static int
-_zip_cdir_set_comment(struct zip_cdir *dest, struct zip *src)
-{
-    if (src->ch_comment_len != -1) {
-	dest->comment_len = src->ch_comment_len;
-	if (src->ch_comment_len == 0)
-	    dest->comment = NULL;
-	else {
-	    dest->comment = _zip_memdup(src->ch_comment,
-				        src->ch_comment_len, &src->error);
-	    if (dest->comment == NULL)
-	        return -1;
-	}
-    }
-    else {
-	if (src->cdir && src->cdir->comment_len > 0) {
-	    dest->comment = _zip_memdup(src->cdir->comment,
-					src->cdir->comment_len, &src->error);
-	    if (dest->comment == NULL)
-		return -1;
-	    dest->comment_len = src->cdir->comment_len;
-	}
-    }
-
-    return 0;
-}
-
-
-
 int
 _zip_changed(struct zip *za, int *survivorsp)
 {
@@ -627,14 +496,13 @@
 
     changed = survivors = 0;
 
-    if (za->ch_comment_len != -1
-	|| za->ch_flags != za->flags)
+    if (za->comment_changed || za->ch_flags != za->flags)
 	changed = 1;
 
     for (i=0; i<za->nentry; i++) {
-	if (za->entry[i].state != ZIP_ST_UNCHANGED || za->entry[i].changes.valid != 0)
+	if (za->entry[i].deleted || za->entry[i].source || (za->entry[i].changes && za->entry[i].changes->changed != 0))
 	    changed = 1;
-	if (za->entry[i].state != ZIP_ST_DELETED)
+	if (!za->entry[i].deleted)
 	    survivors++;
     }
 
@@ -691,6 +559,6 @@
 static int
 _zip_torrentzip_cmp(const void *a, const void *b)
 {
-    return strcasecmp(((const struct filelist *)a)->name,
-		      ((const struct filelist *)b)->name);
+    return strcasecmp(((const struct zip_filelist *)a)->name,
+		      ((const struct zip_filelist *)b)->name);
 }
diff --git a/lib/zip_delete.c b/lib/zip_delete.c
index 8178ebb..cb76936 100644
--- a/lib/zip_delete.c
+++ b/lib/zip_delete.c
@@ -55,7 +55,7 @@
     if (_zip_unchange(za, idx, 1) != 0)
 	return -1;
 
-    za->entry[idx].state = ZIP_ST_DELETED;
+    za->entry[idx].deleted = 1;
 
     return 0;
 }
diff --git a/lib/zip_dirent.c b/lib/zip_dirent.c
index 89926f4..0e025a0 100644
--- a/lib/zip_dirent.c
+++ b/lib/zip_dirent.c
@@ -43,10 +43,7 @@
 #include "zipint.h"
 
 static time_t _zip_d2u_time(int, int);
-static char *_zip_readfpstr(FILE *, unsigned int, int, struct zip_error *);
-static char *_zip_readstr(const unsigned char **, int, int, struct zip_error *);
-static void _zip_write2(unsigned short, FILE *);
-static void _zip_write4(unsigned int, FILE *);
+static struct zip_string *_zip_read_string(const unsigned char **, FILE *, int, int, struct zip_error *);
 
 
 
@@ -59,10 +56,9 @@
 	return;
 
     for (i=0; i<cd->nentry; i++)
-	_zip_dirent_finalize(cd->entry+i);
-    free(cd->comment);
-    free(cd->comment_converted);
+	_zip_entry_finalize(cd->entry+i);
     free(cd->entry);
+    _zip_string_free(cd->comment);
     free(cd);
 }
 
@@ -71,23 +67,27 @@
 int
 _zip_cdir_grow(struct zip_cdir *cd, int nentry, struct zip_error *error)
 {
-    struct zip_dirent *entry;
+    struct zip_entry *entry;
+    int i;
 
-    if (nentry < cd->nentry) {
+    if (nentry < cd->nentry_alloc) {
 	_zip_error_set(error, ZIP_ER_INTERNAL, 0);
 	return -1;
     }
 
-    if (nentry == cd->nentry)
+    if (nentry == cd->nentry_alloc)
 	return 0;
 
-    if ((entry=((struct zip_dirent *)
+    if ((entry=((struct zip_entry *)
 		realloc(cd->entry, sizeof(*(cd->entry))*nentry))) == NULL) {
 	_zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return -1;
     }
+    
+    for (i=cd->nentry_alloc; i<nentry; i++)
+	_zip_entry_init(entry+i);
 
-    cd->nentry = nentry;
+    cd->nentry_alloc = nentry;
     cd->entry = entry;
 
     return 0;
@@ -99,6 +99,7 @@
 _zip_cdir_new(int nentry, struct zip_error *error)
 {
     struct zip_cdir *cd;
+    int i;
     
     if ((cd=(struct zip_cdir *)malloc(sizeof(*cd))) == NULL) {
 	_zip_error_set(error, ZIP_ER_MEMORY, 0);
@@ -107,57 +108,84 @@
 
     if (nentry == 0)
 	cd->entry = NULL;
-    else if ((cd->entry=(struct zip_dirent *)malloc(sizeof(*(cd->entry))*nentry))
-	== NULL) {
+    else if ((cd->entry=(struct zip_entry *)malloc(sizeof(*(cd->entry))*nentry)) == NULL) {
 	_zip_error_set(error, ZIP_ER_MEMORY, 0);
 	free(cd);
 	return NULL;
     }
 
-    /* entries must be initialized by caller */
+    for (i=0; i<nentry; i++)
+	_zip_entry_init(cd->entry+i);
 
-    cd->nentry = nentry;
+    cd->nentry = cd->nentry_alloc = nentry;
     cd->size = cd->offset = 0;
     cd->comment = NULL;
-    cd->comment_len = 0;
-    cd->comment_type = ZIP_ENCODING_UNKNOWN;
-    cd->comment_converted = NULL;
 
     return cd;
 }
 
 
 
-int
-_zip_cdir_write(struct zip_cdir *cd, FILE *fp, struct zip_error *error)
+zip_int64_t
+_zip_cdir_write(struct zip *za, const struct zip_filelist *filelist, int survivors, FILE *fp)
 {
+    zip_uint64_t offset, size;
+    struct zip_string *comment;
     int i;
 
-    cd->offset = ftello(fp);
+    offset = ftello(fp);
 
-    for (i=0; i<cd->nentry; i++) {
-	if (_zip_dirent_write(cd->entry+i, fp, 0, error) != 0)
+    for (i=0; i<survivors; i++) {
+	struct zip_entry *entry = za->entry+filelist[i].idx;
+
+	if (_zip_dirent_write(entry->changes ? entry->changes : entry->orig, fp, 0, &za->error) != 0)
 	    return -1;
     }
 
-    cd->size = ftello(fp) - cd->offset;
+    size = ftello(fp) - offset;
     
+    /* EOCD64 */
+
     /* clearerr(fp); */
     fwrite(EOCD_MAGIC, 1, 4, fp);
     _zip_write4(0, fp);
-    _zip_write2((unsigned short)cd->nentry, fp);
-    _zip_write2((unsigned short)cd->nentry, fp);
-    _zip_write4(cd->size, fp);
-    _zip_write4(cd->offset, fp);
-    _zip_write2(cd->comment_len, fp);
-    fwrite(cd->comment, 1, cd->comment_len, fp);
+    _zip_write2((unsigned short)survivors, fp);
+    _zip_write2((unsigned short)survivors, fp);
+    _zip_write4(size, fp);
+    _zip_write4(offset, fp);
+
+    comment = za->comment_changed ? za->comment_changes : za->comment_orig;
+
+    _zip_write2(comment ? comment->length : 0, fp);
+    if (comment)
+	fwrite(comment->raw, 1, comment->length, fp);
 
     if (ferror(fp)) {
-	_zip_error_set(error, ZIP_ER_WRITE, errno);
+	_zip_error_set(&za->error, ZIP_ER_WRITE, errno);
 	return -1;
     }
 
-    return 0;
+    return size;
+}
+
+
+
+struct zip_dirent *
+_zip_dirent_clone(const struct zip_dirent *sde)
+{
+    struct zip_dirent *tde;
+
+    if ((tde=malloc(sizeof(*tde))) == NULL)
+	return NULL;
+
+    if (sde)
+	memcpy(tde, sde, sizeof(*sde));
+    else
+	_zip_dirent_init(tde);
+    
+    tde->changed = 0;
+
+    return tde;
 }
 
 
@@ -165,16 +193,24 @@
 void
 _zip_dirent_finalize(struct zip_dirent *zde)
 {
-    free(zde->filename_converted);
-    zde->filename_converted = NULL;
-    free(zde->comment_converted);
-    zde->comment_converted = NULL;
-    free(zde->settable.filename);
-    zde->settable.filename = NULL;
-    free(zde->settable.extrafield);
-    zde->settable.extrafield = NULL;
-    free(zde->settable.comment);
-    zde->settable.comment = NULL;
+    if (zde->changed & ZIP_DIRENT_FILENAME)
+	_zip_string_free(zde->filename);
+    if (zde->changed & ZIP_DIRENT_EXTRA_FIELD)
+	_zip_ef_free(zde->extra_fields);
+    if (zde->changed & ZIP_DIRENT_COMMENT)
+	_zip_string_free(zde->comment);
+}
+
+
+
+void
+_zip_dirent_free(struct zip_dirent *zde)
+{
+    if (zde == NULL)
+	return;
+
+    _zip_dirent_finalize(zde);
+    free(zde);
 }
 
 
@@ -182,28 +218,38 @@
 void
 _zip_dirent_init(struct zip_dirent *de)
 {
-    de->settable.valid = 0;
-    de->settable.comp_method = 0;
-    de->settable.filename = NULL;
-    de->settable.extrafield = NULL;
-    de->settable.extrafield_len = 0;
-    de->settable.comment = NULL;
-    de->settable.comment_len = 0;
+    de->changed = 0;
+    de->local_extra_fields_read = 0;
+
     de->version_madeby = 0;
     de->version_needed = 20; /* 2.0 */
     de->bitflags = 0;
+    de->comp_method = ZIP_CM_DEFAULT;
     de->last_mod = 0;
     de->crc = 0;
     de->comp_size = 0;
     de->uncomp_size = 0;
+    de->filename = NULL;
+    de->extra_fields = NULL;
+    de->comment = NULL;
     de->disk_number = 0;
     de->int_attrib = 0;
     de->ext_attrib = 0;
     de->offset = 0;
-    de->fn_type = ZIP_ENCODING_UNKNOWN;
-    de->filename_converted = NULL;
-    de->fc_type = ZIP_ENCODING_UNKNOWN;
-    de->comment_converted = NULL;
+}
+
+
+
+struct zip_dirent *
+_zip_dirent_new(void)
+{
+    struct zip_dirent *de;
+
+    if ((de=malloc(sizeof(*de))) == NULL)
+	return NULL;
+
+    _zip_dirent_init(de);
+    return de;
 }
 
 
@@ -235,7 +281,7 @@
     const unsigned char *cur;
     unsigned short dostime, dosdate;
     zip_uint32_t size;
-    zip_uint16_t filename_len;
+    zip_uint32_t filename_len, ef_len, comment_len;
 
     if (local)
 	size = LENTRYSIZE;
@@ -276,8 +322,7 @@
 	zde->version_madeby = 0;
     zde->version_needed = _zip_read2(&cur);
     zde->bitflags = _zip_read2(&cur);
-    zde->settable.comp_method = _zip_read2(&cur);
-    zde->settable.valid |= ZIP_DIRENT_COMP_METHOD;
+    zde->comp_method = _zip_read2(&cur);
     
     /* convert to time_t */
     dostime = _zip_read2(&cur);
@@ -289,99 +334,84 @@
     zde->uncomp_size = _zip_read4(&cur);
     
     filename_len = _zip_read2(&cur);
-    zde->settable.extrafield_len = _zip_read2(&cur);
+    ef_len = _zip_read2(&cur);
     
     if (local) {
-	zde->settable.comment_len = 0;
+	comment_len = 0;
 	zde->disk_number = 0;
 	zde->int_attrib = 0;
 	zde->ext_attrib = 0;
 	zde->offset = 0;
     } else {
-	zde->settable.comment_len = _zip_read2(&cur);
+	comment_len = _zip_read2(&cur);
 	zde->disk_number = _zip_read2(&cur);
 	zde->int_attrib = _zip_read2(&cur);
 	zde->ext_attrib = _zip_read4(&cur);
 	zde->offset = _zip_read4(&cur);
     }
 
-    zde->settable.filename = NULL;
-    zde->settable.extrafield = NULL;
-    zde->settable.comment = NULL;
+    zde->filename = NULL;
+    zde->extra_fields = NULL;
+    zde->comment = NULL;
 
-    size += filename_len+zde->settable.extrafield_len+zde->settable.comment_len;
+    size += filename_len+ef_len+comment_len;
 
     if (leftp && (*leftp < size)) {
 	_zip_error_set(error, ZIP_ER_NOZIP, 0);
 	return -1;
     }
 
-    if (bufp) {
-	if (filename_len) {
-	    zde->settable.filename = _zip_readstr(&cur, filename_len, 1, error);
-	    if (!zde->settable.filename)
-		    return -1;
-	}
-
-	if (zde->settable.extrafield_len) {
-	    zde->settable.extrafield = _zip_readstr(&cur, zde->settable.extrafield_len, 0,
-					   error);
-	    if (!zde->settable.extrafield)
-		return -1;
-	}
-
-	if (zde->settable.comment_len) {
-	    zde->settable.comment = _zip_readstr(&cur, zde->settable.comment_len, 0, error);
-	    if (!zde->settable.comment)
-		return -1;
-	}
-    }
-    else {
-	if (filename_len) {
-	    zde->settable.filename = _zip_readfpstr(fp, filename_len, 1, error);
-	    if (!zde->settable.filename)
-		    return -1;
-	}
-
-	if (zde->settable.extrafield_len) {
-	    zde->settable.extrafield = _zip_readfpstr(fp, zde->settable.extrafield_len, 0,
-					     error);
-	    if (!zde->settable.extrafield)
-		return -1;
-	}
-
-	if (zde->settable.comment_len) {
-	    zde->settable.comment = _zip_readfpstr(fp, zde->settable.comment_len, 0, error);
-	    if (!zde->settable.comment)
-		return -1;
-	}
-    }
-
-    if (filename_len == 0) {
-	if ((zde->settable.filename=strdup("")) == NULL)
+    if (filename_len) {
+	zde->filename = _zip_read_string(bufp ? &cur : NULL, fp, filename_len, 1, error);
+	if (!zde->filename)
 	    return -1;
-    }
-    if (strlen(zde->settable.filename) != filename_len)
-	return -1;
-    zde->settable.valid |= ZIP_DIRENT_FILENAME;
 
-    if (zde->settable.comment_len)
-	zde->settable.valid |= ZIP_DIRENT_COMMENT;
-    if (zde->settable.extrafield_len)
-	zde->settable.valid |= ZIP_DIRENT_EXTRAFIELD;
+	if (zde->bitflags & ZIP_GPBF_ENCODING_UTF_8) {
+	    if (_zip_guess_encoding(zde->filename, ZIP_ENCODING_UTF8_KNOWN) == ZIP_ENCODING_ERROR) {
+		_zip_error_set(error, ZIP_ER_INCONS, 0);
+		return -1;
+	    }
+	}
+    }
+
+    if (ef_len) {
+	zip_uint8_t *ef = _zip_read_data(bufp ? &cur : NULL, fp, ef_len, 0, error);
+
+	if (ef == NULL)
+	    return -1;
+	if ((zde->extra_fields=_zip_ef_parse(ef, ef_len, local ? ZIP_EF_LOCAL : ZIP_EF_CENTRAL, error)) == NULL) {
+	    free(ef);
+	    return -1;
+	}
+	free(ef);
+	if (local)
+	    zde->local_extra_fields_read = 1;
+    }
+
+    if (comment_len) {
+	zde->comment = _zip_read_string(bufp ? &cur : NULL, fp, comment_len, 0, error);
+	if (!zde->comment)
+	    return -1;
+
+	if (zde->bitflags & ZIP_GPBF_ENCODING_UTF_8) {
+	    if (_zip_guess_encoding(zde->comment, ZIP_ENCODING_UTF8_KNOWN) == ZIP_ENCODING_ERROR) {
+		_zip_error_set(error, ZIP_ER_INCONS, 0);
+		return -1;
+	    }
+	}
+    }
 
 
     /* Zip64 */
 
     if (zde->uncomp_size == ZIP_UINT32_MAX || zde->comp_size == ZIP_UINT32_MAX || zde->offset == ZIP_UINT32_MAX) {
 	zip_uint16_t ef_len, needed_len;
-	const zip_uint8_t *ef = _zip_extract_extra_field_by_id(error, ZIP_EF_ZIP64, 0,
-							       (zip_uint8_t *)zde->settable.extrafield, zde->settable.extrafield_len, &ef_len);
-
+	const zip_uint8_t *ef = _zip_ef_get_by_id(zde->extra_fields, &ef_len, ZIP_EF_ZIP64, 0, local ? ZIP_EF_LOCAL : ZIP_EF_CENTRAL, error);
+	/* XXX: if ef_len == 0 && !ZIP64_EOCD: no error, 0xffffffff is valid value */
 	if (ef == NULL)
 	    return -1;
 
-	/* XXX: if ef_len == 0 && !ZIP64_EOCD: no error, 0xffffffff is valid value */
+
 	if (local)
 	    needed_len = 16;
 	else
@@ -417,6 +447,37 @@
 
 
 
+zip_int32_t
+_zip_dirent_size(FILE *f, zip_uint16_t flags, struct zip_error *error)
+{
+    zip_int32_t size;
+    int local = (flags & ZIP_EF_LOCAL);
+    int i;
+    unsigned char b[6];
+    const unsigned char *p;
+
+    size = local ? LENTRYSIZE : CDENTRYSIZE;
+
+    if (fseek(f, local ? 26 : 28, SEEK_CUR) < 0) {
+	_zip_error_set(error, ZIP_ER_SEEK, errno);
+	return -1;
+    }
+
+    if (fread(b, (local ? 4 : 6), 1, f) != 1) {
+	_zip_error_set(error, ZIP_ER_READ, errno);
+	return -1;
+    }
+
+    p = b;
+    for (i=0; i<(local ? 2 : 3); i++) {
+	size += _zip_read2(&p);
+    }
+
+    return size;
+}
+
+
+
 /* _zip_dirent_torrent_normalize(de);
    Set values suitable for torrentzip.
 */
@@ -456,22 +517,17 @@
     de->version_madeby = 0;
     de->version_needed = 20; /* 2.0 */
     de->bitflags = 2; /* maximum compression */
-    de->settable.comp_method = ZIP_CM_DEFLATE;
-    de->settable.valid |= ZIP_DIRENT_COMP_METHOD;
+    de->comp_method = ZIP_CM_DEFLATE;
     de->last_mod = last_mod;
 
     de->disk_number = 0;
     de->int_attrib = 0;
     de->ext_attrib = 0;
-    de->offset = 0;
 
-    free(de->settable.extrafield);
-    de->settable.extrafield = NULL;
-    de->settable.extrafield_len = 0;
-    free(de->settable.comment);
-    de->settable.comment = NULL;
-    de->settable.comment_len = 0;
-    de->settable.valid &= ~(ZIP_DIRENT_COMMENT|ZIP_DIRENT_EXTRAFIELD);
+    _zip_ef_free(de->extra_fields);
+    de->extra_fields = NULL;
+    _zip_string_free(de->comment);
+    de->comment = NULL;
 }
 
 
@@ -491,9 +547,6 @@
 		  struct zip_error *error)
 {
     unsigned short dostime, dosdate;
-    zip_uint16_t filename_len;
-
-    filename_len = strlen(zde->settable.filename);
 
     fwrite(localp ? LOCAL_MAGIC : CENTRAL_MAGIC, 1, 4, fp);
 
@@ -501,7 +554,7 @@
 	_zip_write2(zde->version_madeby, fp);
     _zip_write2(zde->version_needed, fp);
     _zip_write2(zde->bitflags, fp);
-    _zip_write2(zde->settable.comp_method, fp);
+    _zip_write2(zde->comp_method, fp);
 
     _zip_u2d_time(zde->last_mod, &dostime, &dosdate);
     _zip_write2(dostime, fp);
@@ -511,26 +564,26 @@
     _zip_write4(zde->comp_size, fp);
     _zip_write4(zde->uncomp_size, fp);
     
-    _zip_write2(filename_len, fp);
-    _zip_write2(zde->settable.extrafield_len, fp);
+    _zip_write2(_zip_string_length(zde->filename), fp);
+    _zip_write2(_zip_ef_size(zde->extra_fields, localp ? ZIP_EF_LOCAL : ZIP_EF_CENTRAL), fp);
     
     if (!localp) {
-	_zip_write2(zde->settable.comment_len, fp);
+	_zip_write2(_zip_string_length(zde->comment), fp);
 	_zip_write2(zde->disk_number, fp);
 	_zip_write2(zde->int_attrib, fp);
 	_zip_write4(zde->ext_attrib, fp);
 	_zip_write4(zde->offset, fp);
     }
 
-    if (filename_len > 0)
-	fwrite(zde->settable.filename, 1, filename_len, fp);
+    if (zde->filename)
+	_zip_string_write(zde->filename, fp);
 
-    if (zde->settable.extrafield_len)
-	fwrite(zde->settable.extrafield, 1, zde->settable.extrafield_len, fp);
+    if (zde->extra_fields)
+	_zip_ef_write(zde->extra_fields, localp ? ZIP_EF_LOCAL : ZIP_EF_CENTRAL, fp);
 
     if (!localp) {
-	if (zde->settable.comment_len)
-	    fwrite(zde->settable.comment, 1, zde->settable.comment_len, fp);
+	if (zde->comment)
+	    _zip_string_write(zde->comment, fp);
     }
 
     if (ferror(fp)) {
@@ -566,6 +619,34 @@
 
 
 
+struct zip_dirent *
+_zip_get_dirent(struct zip *za, zip_uint64_t idx, int flags, struct zip_error *error)
+{
+    if (error == NULL)
+	error = &za->error;
+
+    if (idx >= za->nentry) {
+	_zip_error_set(error, ZIP_ER_INVAL, 0);
+	return NULL;
+    }
+
+    if ((flags & ZIP_FL_UNCHANGED) || za->entry[idx].changes == NULL) {
+	if (za->entry[idx].orig == NULL) {
+	    _zip_error_set(error, ZIP_ER_INVAL, 0);
+	    return NULL;
+	}
+	if (za->entry[idx].deleted && (flags & ZIP_FL_UNCHANGED) == 0) {
+	    _zip_error_set(error, ZIP_ER_DELETED, 0);
+	    return NULL;
+	}
+	return za->entry[idx].orig;
+    }
+    else
+	return za->entry[idx].changes;
+}
+
+
+
 zip_uint16_t
 _zip_read2(const unsigned char **a)
 {
@@ -607,55 +688,31 @@
 
 
 
-static char *
-_zip_readfpstr(FILE *fp, unsigned int len, int nulp, struct zip_error *error)
+zip_uint8_t *
+_zip_read_data(const unsigned char **buf, FILE *fp, int len, int nulp, struct zip_error *error)
 {
-    char *r, *o;
+    zip_uint8_t *r, *o;
 
     if (len == 0 && nulp == 0)
 	return NULL;
 
-    r = (char *)malloc(nulp ? len+1 : len);
+    r = (zip_uint8_t *)malloc(nulp ? len+1 : len);
     if (!r) {
 	_zip_error_set(error, ZIP_ER_MEMORY, 0);
 	return NULL;
     }
 
-    if (fread(r, 1, len, fp)<len) {
-	free(r);
-	_zip_error_set(error, ZIP_ER_READ, errno);
-	return NULL;
+    if (buf) {
+	memcpy(r, *buf, len);
+	*buf += len;
     }
-
-    if (nulp) {
-	/* replace any in-string NUL characters with spaces */
-	r[len] = 0;
-	for (o=r; o<r+len; o++)
-	    if (*o == '\0')
-		*o = ' ';
+    else {
+	if (fread(r, 1, len, fp)<len) {
+	    free(r);
+	    _zip_error_set(error, ZIP_ER_READ, errno);
+	    return NULL;
+	}
     }
-    
-    return r;
-}
-
-
-
-static char *
-_zip_readstr(const unsigned char **buf, int len, int nulp, struct zip_error *error)
-{
-    char *r, *o;
-
-    if (len == 0 && nulp == 0)
-	return NULL;
-
-    r = (char *)malloc(nulp ? len+1 : len);
-    if (!r) {
-	_zip_error_set(error, ZIP_ER_MEMORY, 0);
-	return NULL;
-    }
-    
-    memcpy(r, *buf, len);
-    *buf += len;
 
     if (nulp) {
 	/* replace any in-string NUL characters with spaces */
@@ -670,8 +727,21 @@
 
 
 
-static void
-_zip_write2(unsigned short i, FILE *fp)
+static struct zip_string *
+_zip_read_string(const unsigned char **buf, FILE *fp, int len, int nulp, struct zip_error *error)
+{
+    zip_uint8_t *raw;
+
+    if ((raw=_zip_read_data(buf, fp, len, nulp, error)) == NULL)
+	return NULL;
+
+    return _zip_string_new(raw, len, error);
+}
+
+
+
+void
+_zip_write2(zip_uint16_t i, FILE *fp)
 {
     putc(i&0xff, fp);
     putc((i>>8)&0xff, fp);
@@ -681,8 +751,8 @@
 
 
 
-static void
-_zip_write4(unsigned int i, FILE *fp)
+void
+_zip_write4(zip_uint32_t i, FILE *fp)
 {
     putc(i&0xff, fp);
     putc((i>>8)&0xff, fp);
diff --git a/lib/zip_discard.c b/lib/zip_discard.c
index 16cc3d8..29aa25e 100644
--- a/lib/zip_discard.c
+++ b/lib/zip_discard.c
@@ -58,13 +58,12 @@
 	fclose(za->zp);
 
     free(za->default_password);
-    _zip_cdir_free(za->cdir);
-    free(za->ch_comment);
+    _zip_string_free(za->comment_orig);
+    _zip_string_free(za->comment_changes);
 
     if (za->entry) {
-	for (i=0; i<za->nentry; i++) {
-	    _zip_entry_free(za->entry+i);
-	}
+	for (i=0; i<za->nentry; i++)
+	    _zip_entry_finalize(za->entry+i);
 	free(za->entry);
     }
 
diff --git a/lib/zip_entry_free.c b/lib/zip_entry.c
similarity index 77%
rename from lib/zip_entry_free.c
rename to lib/zip_entry.c
index f801855..58663e8 100644
--- a/lib/zip_entry_free.c
+++ b/lib/zip_entry.c
@@ -1,6 +1,6 @@
 /*
-  zip_entry_free.c -- free struct zip_entry
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  zip_entry.c -- struct zip_entry helper functions
+  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -33,22 +33,23 @@
 
 
 
-#include <stdlib.h>
-
 #include "zipint.h"
 
+void
+_zip_entry_finalize(struct zip_entry *e)
+{
+    _zip_unchange_data(e);
+    _zip_dirent_free(e->orig);
+    _zip_dirent_free(e->changes);
+}
+
 
 
 void
-_zip_entry_free(struct zip_entry *ze)
+_zip_entry_init(struct zip_entry *e)
 {
-    if (ze->changes.valid & ZIP_DIRENT_FILENAME)
-	free(ze->changes.filename);
-    if (ze->changes.valid & ZIP_DIRENT_EXTRAFIELD)
-	free(ze->changes.extrafield);
-    if (ze->changes.valid & ZIP_DIRENT_COMMENT)
-	free(ze->changes.comment);
-    ze->changes.valid = 0;
-
-    _zip_unchange_data(ze);
+    e->orig = NULL;
+    e->changes = NULL;
+    e->source = NULL;
+    e->deleted = 0;
 }
diff --git a/lib/zip_entry_new.c b/lib/zip_entry_new.c
deleted file mode 100644
index b8ab272..0000000
--- a/lib/zip_entry_new.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
-  zip_entry_new.c -- create and init struct zip_entry
-  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
-
-  This file is part of libzip, a library to manipulate ZIP archives.
-  The authors can be contacted at <libzip@nih.at>
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions
-  are met:
-  1. Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-  2. Redistributions in binary form must reproduce the above copyright
-     notice, this list of conditions and the following disclaimer in
-     the documentation and/or other materials provided with the
-     distribution.
-  3. The names of the authors may not be used to endorse or promote
-     products derived from this software without specific prior
-     written permission.
- 
-  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
-  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
-  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-
-
-#include <stdlib.h>
-
-#include "zipint.h"
-
-
-
-struct zip_entry *
-_zip_entry_new(struct zip *za)
-{
-    struct zip_entry *ze;
-    if (!za) {
-	ze = (struct zip_entry *)malloc(sizeof(struct zip_entry));
-	if (!ze)
-	    return NULL;
-    }
-    else {
-	if (za->nentry+1 >= za->nentry_alloc) {
-	    struct zip_entry *rentries;
-	    za->nentry_alloc += 16;
-	    rentries = (struct zip_entry *)realloc(za->entry,
-						   sizeof(struct zip_entry)
-						   * za->nentry_alloc);
-	    if (!rentries) {
-		_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-		return NULL;
-	    }
-	    za->entry = rentries;
-	}
-	ze = za->entry+za->nentry;
-    }
-
-    ze->state = ZIP_ST_UNCHANGED;
-    ze->source = NULL;
-    ze->changes.valid = 0;
-
-    if (za)
-	za->nentry++;
-
-    return ze;
-}
diff --git a/lib/zip_extra_field.c b/lib/zip_extra_field.c
new file mode 100644
index 0000000..1c5a414
--- /dev/null
+++ b/lib/zip_extra_field.c
@@ -0,0 +1,290 @@
+/*
+  zip_extra_field.c -- manipulate extra fields
+  Copyright (C) 2012 Dieter Baron and Thomas Klausner
+
+  This file is part of libzip, a library to manipulate ZIP archives.
+  The authors can be contacted at <libzip@nih.at>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#include "zipint.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+
+void
+_zip_ef_free(struct zip_extra_field *ef)
+{
+    struct zip_extra_field *ef2;
+
+    while (ef) {
+	ef2 = ef->next;
+	free(ef->data);
+	free(ef);
+	ef = ef2;
+    }
+}
+
+
+
+const zip_uint8_t *
+_zip_ef_get_by_id(struct zip_extra_field *ef, zip_uint16_t *lenp, zip_uint16_t id, zip_uint16_t id_idx, zip_uint16_t flags, struct zip_error *error)
+{
+    static const zip_uint8_t empty[1];
+    
+    int i;
+
+    i = 0;
+    for (; ef; ef=ef->next) {
+	if (ef->id == id && (ef->flags & flags & ZIP_EF_BOTH)) {
+	    if (i < id_idx) {
+		i++;
+		continue;
+	    }
+
+	    if (lenp)
+		*lenp = ef->size;
+	    if (ef->size > 0)
+		return ef->data;
+	    else
+		return empty;
+	}
+    }
+
+    _zip_error_set(error, ZIP_ER_NOENT, 0);
+    return NULL;
+}
+
+
+
+struct zip_extra_field *
+_zip_ef_merge(struct zip_extra_field *to, struct zip_extra_field *from)
+{
+    struct zip_extra_field *ef2, *tt, *tail;
+    int duplicate;
+
+    if (to == NULL)
+	return from;
+
+    for (tail=to; tail->next; tail=tail->next)
+	;
+
+    for (; from; from=ef2) {
+	ef2 = from->next;
+
+	duplicate = 0;
+	for (tt=to; tt; tt=tt->next) {
+	    if (tt->id == from->id && tt->size == from->size && memcmp(tt->data, from->data, tt->size) == 0) {
+		tt->flags |= (from->flags & ZIP_EF_BOTH);
+		duplicate = 1;
+		break;
+	    }
+	}
+
+	from->next = NULL;
+	if (duplicate)
+	    _zip_ef_free(from);
+	else
+	    tail = tail->next = from;
+    }
+
+    return to;
+}
+
+
+
+struct zip_extra_field *
+_zip_ef_new(zip_uint16_t id, zip_uint16_t size, const zip_uint8_t *data, zip_uint16_t flags)
+{
+    struct zip_extra_field *ef;
+
+    if ((ef=malloc(sizeof(*ef))) == NULL)
+	return NULL;
+
+    ef->next = NULL;
+    ef->flags = flags;
+    ef->id = id;
+    ef->size = size;
+    if (size > 0) {
+	if ((ef->data=_zip_memdup(data, size, NULL)) == NULL) {
+	    free(ef);
+	    return NULL;
+	}
+    }
+    else
+	ef->data = NULL;
+
+    return ef;
+}
+
+
+
+struct zip_extra_field *
+_zip_ef_parse(const zip_uint8_t *data, zip_uint16_t len, zip_uint16_t flags, struct zip_error *error)
+{
+    struct zip_extra_field *ef, *ef2, *ef_head;
+    int i;
+    const zip_uint8_t *p;
+    zip_uint16_t fid, flen;
+
+    ef_head = NULL;
+    i = 0;
+    for (p=data; p<data+len; p+=flen+4) {
+	if (p+4 > data+len) {
+	    _zip_error_set(error, ZIP_ER_INCONS, 0);
+	    _zip_ef_free(ef_head);
+	    return NULL;
+	}
+
+	fid = p[0]+(p[1]<<8);
+	flen = p[2]+(p[3]<<8);
+
+	if (p+flen+4 > data+len) {
+	    _zip_error_set(error, ZIP_ER_INCONS, 0);
+	    _zip_ef_free(ef_head);
+	    return NULL;
+	}
+
+	if ((ef2=_zip_ef_new(fid, flen, p+4, flags)) == NULL) {
+	    _zip_error_set(error, ZIP_ER_MEMORY, 0);
+	    _zip_ef_free(ef_head);
+	    return NULL;
+	}
+
+	if (ef_head) {
+	    ef->next = ef2;
+	    ef = ef2;
+	}
+	else
+	    ef_head = ef = ef2;
+    }
+
+    return ef_head;
+}
+
+
+
+zip_uint16_t
+_zip_ef_size(struct zip_extra_field *ef, zip_uint16_t flags)
+{
+    zip_uint16_t size;
+
+    size = 0;
+    for (; ef; ef=ef->next) {
+	if (ef->flags & flags & ZIP_EF_BOTH)
+	    size += 4+ef->size;
+    }
+
+    return size;
+}
+
+
+
+void
+_zip_ef_write(struct zip_extra_field *ef, zip_uint16_t flags, FILE *f)
+{
+    for (; ef; ef=ef->next) {
+	if (ef->flags & flags & ZIP_EF_BOTH) {
+	    _zip_write2(ef->id, f);
+	    _zip_write2(ef->size, f);
+	    if (ef->size > 0)
+		fwrite(ef->data, ef->size, 1, f);
+	}
+    }
+}
+
+
+
+int
+_zip_read_local_ef(struct zip *za, int idx)
+{
+    struct zip_entry *e;
+    unsigned char b[4];
+    const unsigned char *p;
+    zip_uint16_t fname_len, ef_len;
+    zip_uint8_t *ef_raw;
+    struct zip_extra_field *ef;
+
+    if (idx >= za->nentry) {
+	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	return -1;
+    }
+
+    e = za->entry+idx;
+
+    if (e->orig == NULL || e->orig->local_extra_fields_read)
+	return 0;
+
+    
+    if (fseek(za->zp, e->orig->offset + 26, SEEK_SET) < 0) {
+	_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
+	return -1;
+    }
+
+    if (fread(b, sizeof(b), 1, za->zp) != 1) {
+	_zip_error_set(&za->error, ZIP_ER_READ, errno);
+	return -1;
+    }
+
+    p = b;
+    fname_len = _zip_read2(&p);
+    ef_len = _zip_read2(&p);
+
+    if (ef_len > 0) {
+	if (fseek(za->zp, fname_len, SEEK_CUR) < 0) {
+	    _zip_error_set(&za->error, ZIP_ER_SEEK, errno);
+	    return -1;
+	}
+
+	ef_raw = _zip_read_data(NULL, za->zp, ef_len, 0, &za->error);
+
+	if (ef_raw == NULL)
+	    return -1;
+
+	if ((ef=_zip_ef_parse(ef_raw, ef_len, ZIP_EF_LOCAL, &za->error)) == NULL) {
+	    free(ef_raw);
+	    return -1;
+	}
+	free(ef_raw);
+	
+	e->orig->extra_fields = _zip_ef_merge(e->orig->extra_fields, ef);
+    }
+
+    e->orig->local_extra_fields_read = 1;
+    
+    if (e->changes && e->changes->local_extra_fields_read == 0) {
+	e->changes->extra_fields = e->orig->extra_fields;
+	e->changes->local_extra_fields_read = 1;
+    }
+
+    return 0;
+}
diff --git a/lib/zip_extra_field_api.c b/lib/zip_extra_field_api.c
new file mode 100644
index 0000000..4aafa2e
--- /dev/null
+++ b/lib/zip_extra_field_api.c
@@ -0,0 +1,182 @@
+/*
+  zip_extra_field_api.c -- public extra fields API functions
+  Copyright (C) 2012 Dieter Baron and Thomas Klausner
+
+  This file is part of libzip, a library to manipulate ZIP archives.
+  The authors can be contacted at <libzip@nih.at>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#include "zipint.h"
+
+
+
+ZIP_EXTERN int
+zip_delete_file_extra_field(struct zip *za, zip_uint64_t idx, zip_uint32_t flags, zip_uint16_t ef_idx)
+{
+    /* XXX */
+    return -1;
+}
+
+
+
+ZIP_EXTERN int
+zip_delete_file_extra_field_by_id(struct zip *za, zip_uint64_t idx, zip_uint32_t flags, zip_uint16_t ef_id, zip_uint16_t ef_idx)
+{
+    /* XXX */
+    return -1;
+}
+
+
+
+ZIP_EXTERN const zip_uint8_t *
+zip_get_file_extra_field(struct zip *za, zip_uint64_t idx, zip_uint32_t flags, zip_uint16_t ef_idx, zip_uint16_t *idp, zip_uint16_t *lenp)
+{
+    static const zip_uint8_t empty[1];
+
+    struct zip_dirent *de;
+    struct zip_extra_field *ef;
+    int i;
+
+    if ((de=_zip_get_dirent(za, idx, flags, &za->error)) == NULL)
+	return NULL;
+
+    if ((flags & ZIP_EF_BOTH) == 0)
+	flags = ZIP_EF_BOTH;
+
+    if (flags & ZIP_FL_LOCAL)
+	if (_zip_read_local_ef(za, idx) < 0)
+	    return NULL;
+
+    i = 0;
+    for (ef=de->extra_fields; ef; ef=ef->next) {
+	if (ef->flags & flags & ZIP_EF_BOTH) {
+	    if (i < ef_idx) {
+		i++;
+		continue;
+	    }
+
+	    if (idp)
+		*idp = ef->id;
+	    if (lenp)
+		*lenp = ef->size;
+	    if (ef->size > 0)
+		return ef->data;
+	    else
+		return empty;
+	}
+    }
+
+    _zip_error_set(&za->error, ZIP_ER_NOENT, 0);
+    return NULL;
+
+}
+
+
+
+ZIP_EXTERN const zip_uint8_t *
+zip_get_file_extra_field_by_id(struct zip *za, zip_uint64_t idx, zip_uint32_t flags, zip_uint16_t ef_id, zip_uint16_t ef_idx, zip_uint16_t *lenp)
+{
+    struct zip_dirent *de;
+
+    if ((de=_zip_get_dirent(za, idx, flags, &za->error)) == NULL)
+	return NULL;
+
+    if ((flags & ZIP_EF_BOTH) == 0)
+	flags = ZIP_EF_BOTH;
+
+    if (flags & ZIP_FL_LOCAL)
+	if (_zip_read_local_ef(za, idx) < 0)
+	    return NULL;
+
+    return _zip_ef_get_by_id(de->extra_fields, lenp, ef_id, ef_idx, flags, &za->error);
+}
+
+
+
+ZIP_EXTERN zip_int16_t
+zip_get_file_num_extra_fields(struct zip *za, zip_uint64_t idx, zip_uint32_t flags)
+{
+    struct zip_dirent *de;
+    struct zip_extra_field *ef;
+    zip_uint16_t n;
+
+    if ((de=_zip_get_dirent(za, idx, flags, &za->error)) == NULL)
+	return -1;
+
+    if ((flags & ZIP_EF_BOTH) == 0)
+	flags = ZIP_EF_BOTH;
+
+    if (flags & ZIP_FL_LOCAL)
+	if (_zip_read_local_ef(za, idx) < 0)
+	    return -1;
+
+    n = 0;
+    for (ef=de->extra_fields; ef; ef=ef->next)
+	if (ef->flags & flags & ZIP_EF_BOTH)
+	    n++;
+
+    return n;
+}
+
+
+
+ZIP_EXTERN zip_int16_t
+zip_get_file_num_extra_fields_by_id(struct zip *za, zip_uint64_t idx, zip_uint32_t flags, zip_uint16_t ef_id)
+{
+    struct zip_dirent *de;
+    struct zip_extra_field *ef;
+    zip_uint16_t n;
+
+    if ((de=_zip_get_dirent(za, idx, flags, &za->error)) == NULL)
+	return -1;
+
+    if ((flags & ZIP_EF_BOTH) == 0)
+	flags = ZIP_EF_BOTH;
+
+    if (flags & ZIP_FL_LOCAL)
+	if (_zip_read_local_ef(za, idx) < 0)
+	    return -1;
+
+    n = 0;
+    for (ef=de->extra_fields; ef; ef=ef->next)
+	if (ef->id == ef_id && (ef->flags & flags & ZIP_EF_BOTH))
+	    n++;
+
+    return n;
+}
+
+
+
+ZIP_EXTERN int
+zip_set_file_extra_field(struct zip *za, zip_uint64_t idx, zip_uint32_t flags, zip_uint16_t ef_id, zip_uint16_t ef_idx, const zip_uint8_t *data, zip_uint16_t len)
+{
+    /* XXX */
+    return -1;
+}
diff --git a/lib/zip_file_get_offset.c b/lib/zip_file_get_offset.c
index 9f161a4..644613f 100644
--- a/lib/zip_file_get_offset.c
+++ b/lib/zip_file_get_offset.c
@@ -51,24 +51,21 @@
 */
 
 zip_uint64_t
-_zip_file_get_offset(struct zip *za, int idx, struct zip_error *ze)
+_zip_file_get_offset(struct zip *za, int idx, struct zip_error *error)
 {
-    struct zip_dirent de;
-    unsigned int offset;
+    zip_uint64_t offset;
+    zip_int32_t size;
 
-    offset = za->cdir->entry[idx].offset;
+    offset = za->entry[idx].orig->offset;
 
     if (fseeko(za->zp, offset, SEEK_SET) != 0) {
-	_zip_error_set(ze, ZIP_ER_SEEK, errno);
+	_zip_error_set(error, ZIP_ER_SEEK, errno);
 	return 0;
     }
 
-    if (_zip_dirent_read(&de, za->zp, NULL, NULL, 1, ze) != 0)
+    /* XXX: cache? */
+    if ((size=_zip_dirent_size(za->zp, ZIP_EF_LOCAL, error)) < 0)
 	return 0;
 
-    offset += LENTRYSIZE + strlen(de.settable.filename) + de.settable.extrafield_len;
-
-    _zip_dirent_finalize(&de);
-
-    return offset;
+    return offset + size;
 }
diff --git a/lib/zip_get_archive_comment.c b/lib/zip_get_archive_comment.c
index 730062d..ccada19 100644
--- a/lib/zip_get_archive_comment.c
+++ b/lib/zip_get_archive_comment.c
@@ -42,42 +42,20 @@
 ZIP_EXTERN const char *
 zip_get_archive_comment(struct zip *za, int *lenp, int flags)
 {
-    const char *ret;
-    if ((flags & ZIP_FL_UNCHANGED)
-	|| (za->ch_comment_len == -1)) {
-	if (za->cdir) {
-	    if (lenp != NULL)
-		*lenp = za->cdir->comment_len;
-	    ret = za->cdir->comment;
-	    
-	    if (flags & ZIP_FL_NAME_RAW)
-		return ret;
+    struct zip_string *comment;
+    zip_uint32_t len;
+    const zip_uint8_t *str;
 
-	    /* start guessing */
-	    if (za->cdir->comment_type == ZIP_ENCODING_UNKNOWN)
-		za->cdir->comment_type = _zip_guess_encoding(ret, za->cdir->comment_len);
+    if ((flags & ZIP_FL_UNCHANGED) || (za->comment_changes == NULL))
+	comment = za->comment_orig;
+    else
+	comment = za->comment_changes;
 
-	    if (((flags & ZIP_FL_NAME_STRICT) && (za->cdir->comment_type != ZIP_ENCODING_ASCII))
-		|| (za->cdir->comment_type == ZIP_ENCODING_CP437)) {
-		if (za->cdir->comment_converted == NULL)
-		    za->cdir->comment_converted = _zip_cp437_to_utf8(ret, za->cdir->comment_len,
-									     &za->cdir->comment_converted_len, &za->error);
-		ret = za->cdir->comment_converted;
-		if (lenp != NULL)
-		    *lenp = za->cdir->comment_converted_len;
-	    }
+    if ((str=_zip_string_get(comment, &len, flags, &za->error)) == NULL)
+	return NULL;
 
-	    return ret;
-	}
-	else {
-	    if (lenp != NULL)
-		*lenp = -1;
-	    return NULL;
-	}
-    }
+    if (lenp)
+	*lenp = len;
 
-    /* already UTF-8, no conversion needed */
-    if (lenp != NULL)
-	*lenp = za->ch_comment_len;
-    return za->ch_comment;
+    return (const char *)str;
 }
diff --git a/lib/zip_get_extra_field_by_id.c b/lib/zip_get_extra_field_by_id.c
deleted file mode 100644
index 588a337..0000000
--- a/lib/zip_get_extra_field_by_id.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
-  zip_get_extra_field_by_id.c -- get extrafield by ID
-  Copyright (C) 2012 Dieter Baron and Thomas Klausner
-
-  This file is part of libzip, a library to manipulate ZIP archives.
-  The authors can be contacted at <libzip@nih.at>
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions
-  are met:
-  1. Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-  2. Redistributions in binary form must reproduce the above copyright
-     notice, this list of conditions and the following disclaimer in
-     the documentation and/or other materials provided with the
-     distribution.
-  3. The names of the authors may not be used to endorse or promote
-     products derived from this software without specific prior
-     written permission.
- 
-  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
-  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
-  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-
-
-#include "zipint.h"
-
-
-
-const zip_uint8_t *
-zip_get_extra_field_by_id(struct zip *za, int idx, int flags,
-			  zip_uint16_t id, int id_index, zip_uint16_t *lenp)
-{
-    static zip_uint8_t empty[1];
-    
-    const zip_uint8_t *ef;
-    int len;
-
-    len = 17;
-    ef = (const zip_uint8_t *)zip_get_file_extra(za, idx, &len, flags);
-    
-    if (ef == NULL) {
-	if (len == 17)
-	    return NULL;
-	if (lenp)
-	    *lenp = 0;
-	return empty;
-    }
-
-    return _zip_extract_extra_field_by_id(&za->error, id, id_index, ef, len, lenp);
-}
-    
-
-const zip_uint8_t *
-_zip_extract_extra_field_by_id(struct zip_error *errorp, zip_uint16_t id, int id_index,
-			       const zip_uint8_t *ef, zip_uint16_t ef_len, zip_uint16_t *lenp)
-{
-    int i;
-    const zip_uint8_t *p;
-    zip_uint16_t fid, flen;
-
-    i = 0;
-    for (p=ef; p<ef+ef_len; p+=flen+4) {
-	if (p+4 > ef+ef_len) {
-	    _zip_error_set(errorp, ZIP_ER_INCONS, 0);
-	    return NULL;
-	}
-
-	fid = p[0]+(p[1]<<8);
-	flen = p[2]+(p[3]<<8);
-
-	if (p+flen+4 > ef+ef_len) {
-	    _zip_error_set(errorp, ZIP_ER_INCONS, 0);
-	    return NULL;
-	}
-
-	if (fid == id) {
-	    if (i < id_index) {
-		i++;
-		continue;
-	    }
-
-	    if (lenp)
-		*lenp = flen;
-	    return p+4;
-	}
-    }
-
-    _zip_error_set(errorp, ZIP_ER_NOENT, 0);
-    return NULL;
-}
-
diff --git a/lib/zip_get_file_comment.c b/lib/zip_get_file_comment.c
index ee71609..a7a586a 100644
--- a/lib/zip_get_file_comment.c
+++ b/lib/zip_get_file_comment.c
@@ -33,8 +33,6 @@
 
 
 
-#include <string.h>
-
 #include "zipint.h"
 
 
@@ -42,53 +40,18 @@
 ZIP_EXTERN const char *
 zip_get_file_comment(struct zip *za, zip_uint64_t idx, int *lenp, int flags)
 {
-    const char *ret;
+    struct zip_dirent *de;
+    zip_uint32_t len;
+    const zip_uint8_t *str;
 
-    if (idx >= za->nentry) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+    if ((de=_zip_get_dirent(za, idx, flags, NULL)) == NULL)
 	return NULL;
-    }
 
-    if ((flags & ZIP_FL_UNCHANGED) || (za->entry[idx].changes.valid & ZIP_DIRENT_COMMENT) == 0) {
-	if (za->cdir == NULL) {
-	    _zip_error_set(&za->error, ZIP_ER_NOENT, 0);
-	    return NULL;
-	}
-	if (idx >= za->cdir->nentry) {
-	    _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
-	    return NULL;
-	}
-	    
-	if (lenp != NULL)
-	    *lenp = za->cdir->entry[idx].settable.comment_len;
-	ret = za->cdir->entry[idx].settable.comment;
+    if ((str=_zip_string_get(de->comment, &len, flags, &za->error)) == NULL)
+	return NULL;
 
-	if (flags & ZIP_FL_NAME_RAW)
-	    return ret;
+    if (lenp)
+	*lenp = len;
 
-	/* file comment already is UTF-8? */
-	if (za->cdir->entry[idx].bitflags & ZIP_GPBF_ENCODING_UTF_8)
-	    return ret;
-	
-	/* undeclared, start guessing */
-	if (za->cdir->entry[idx].fc_type == ZIP_ENCODING_UNKNOWN)
-	    za->cdir->entry[idx].fc_type = _zip_guess_encoding(ret, za->cdir->entry[idx].settable.comment_len);
-
-	if (((flags & ZIP_FL_NAME_STRICT) && (za->cdir->entry[idx].fc_type != ZIP_ENCODING_ASCII))
-	    || (za->cdir->entry[idx].fc_type == ZIP_ENCODING_CP437)) {
-	    if (za->cdir->entry[idx].comment_converted == NULL)
-		za->cdir->entry[idx].comment_converted = _zip_cp437_to_utf8(ret, za->cdir->entry[idx].settable.comment_len,
-									    &za->cdir->entry[idx].comment_converted_len, &za->error);
-	    ret = za->cdir->entry[idx].comment_converted;
-	    if (lenp != NULL)
-		*lenp = za->cdir->entry[idx].comment_converted_len;
-	}
-
-	return ret;
-    }
-
-    /* already UTF-8, no conversion necessary */
-    if (lenp != NULL)
-	*lenp = za->entry[idx].changes.comment_len;
-    return za->entry[idx].changes.comment;
+    return (const char *)str;
 }
diff --git a/lib/zip_get_file_extra.c b/lib/zip_get_file_extra.c
deleted file mode 100644
index cb3d2b5..0000000
--- a/lib/zip_get_file_extra.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-  zip_get_file_extra.c -- get file extra field
-  Copyright (C) 2006-2010 Dieter Baron and Thomas Klausner
-
-  This file is part of libzip, a library to manipulate ZIP archives.
-  The authors can be contacted at <libzip@nih.at>
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions
-  are met:
-  1. Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-  2. Redistributions in binary form must reproduce the above copyright
-     notice, this list of conditions and the following disclaimer in
-     the documentation and/or other materials provided with the
-     distribution.
-  3. The names of the authors may not be used to endorse or promote
-     products derived from this software without specific prior
-     written permission.
-
-  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
-  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
-  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-
-
-#include "zipint.h"
-
-
-
-ZIP_EXTERN const char *
-zip_get_file_extra(struct zip *za, zip_uint64_t idx, int *lenp, int flags)
-{
-    if (idx >= za->nentry) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
-	return NULL;
-    }
-
-    if ((flags & ZIP_FL_UNCHANGED) || (za->entry[idx].changes.valid & ZIP_DIRENT_EXTRAFIELD) == 0) {
-	if (idx >= za->cdir->nentry) {
-	    _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
-	    return NULL;
-	}
-
-	if (lenp != NULL)
-	    *lenp = za->cdir->entry[idx].settable.extrafield_len;
-	return za->cdir->entry[idx].settable.extrafield;
-    }
-
-    if (lenp != NULL)
-	*lenp = za->entry[idx].changes.extrafield_len;
-    return za->entry[idx].changes.extrafield;
-}
diff --git a/lib/zip_get_name.c b/lib/zip_get_name.c
index 7c3f7b9..baa383d 100644
--- a/lib/zip_get_name.c
+++ b/lib/zip_get_name.c
@@ -51,48 +51,14 @@
 _zip_get_name(struct zip *za, zip_uint64_t idx, int flags,
 	      struct zip_error *error)
 {
-    enum zip_encoding_type enc;
-    const char *ret;
+    struct zip_dirent *de;
+    const zip_uint8_t *str;
 
-    ret = NULL;
-    if (idx >= za->nentry) {
-	_zip_error_set(error, ZIP_ER_INVAL, 0);
+    if ((de=_zip_get_dirent(za, idx, flags, NULL)) == NULL)
 	return NULL;
-    }
 
-    if ((flags & ZIP_FL_UNCHANGED) == 0) {
-	if (za->entry[idx].state == ZIP_ST_DELETED) {
-	    _zip_error_set(error, ZIP_ER_DELETED, 0);
-	    return NULL;
-	}
-	if (za->entry[idx].changes.valid & ZIP_DIRENT_FILENAME)
-	    return za->entry[idx].changes.filename; /* must be UTF-8 */
-    }
-
-    /* XXX: read Infozip UTF-8 Extension 0x7075 file name? */
-    if (za->cdir == NULL || idx >= za->cdir->nentry) {
-	_zip_error_set(error, ZIP_ER_INVAL, 0);
+    if ((str=_zip_string_get(de->filename, NULL, flags, &za->error)) == NULL)
 	return NULL;
-    }
-    ret = za->cdir->entry[idx].settable.filename;
 
-    if (flags & ZIP_FL_NAME_RAW)
-	return ret;
-
-    /* file name already is UTF-8? */
-    if (za->cdir->entry[idx].bitflags & ZIP_GPBF_ENCODING_UTF_8)
-	return ret;
-
-    /* undeclared, start guessing */
-    if (za->cdir->entry[idx].fn_type == ZIP_ENCODING_UNKNOWN)
-	za->cdir->entry[idx].fn_type = _zip_guess_encoding(ret, strlen(ret));
-
-    if (((flags & ZIP_FL_NAME_STRICT) && (za->cdir->entry[idx].fn_type != ZIP_ENCODING_ASCII))
-	|| (za->cdir->entry[idx].fn_type == ZIP_ENCODING_CP437)) {
-	if (za->cdir->entry[idx].filename_converted == NULL)
-	    za->cdir->entry[idx].filename_converted = _zip_cp437_to_utf8(ret, strlen(ret), NULL, error);
-	ret = za->cdir->entry[idx].filename_converted;
-    }
-
-    return ret;
+    return (const char *)str;
 }
diff --git a/lib/zip_get_num_entries.c b/lib/zip_get_num_entries.c
index 83293e9..3fbafd2 100644
--- a/lib/zip_get_num_entries.c
+++ b/lib/zip_get_num_entries.c
@@ -40,13 +40,16 @@
 ZIP_EXTERN zip_uint64_t
 zip_get_num_entries(struct zip *za, int flags)
 {
+    zip_uint64_t n;
+
     if (za == NULL)
 	return -1;
 
     if (flags & ZIP_FL_UNCHANGED) {
-      if (za->cdir == NULL)
-	return 0;
-      return za->cdir->nentry;
+	n = za->nentry;
+	while (n>0 && za->entry[n-1].orig == NULL)
+	    --n;
+	return n;
     }
     return za->nentry;
 }
diff --git a/lib/zip_name_locate.c b/lib/zip_name_locate.c
index 44e3c85..a5b1db2 100644
--- a/lib/zip_name_locate.c
+++ b/lib/zip_name_locate.c
@@ -53,7 +53,7 @@
 {
     int (*cmp)(const char *, const char *);
     const char *fn, *p;
-    int i, n;
+    zip_uint64_t i;
 
     if (za == NULL)
 	return -1;
@@ -63,15 +63,9 @@
 	return -1;
     }
 
-    if ((flags & ZIP_FL_UNCHANGED)  && za->cdir == NULL) {
-        _zip_error_set(error, ZIP_ER_NOENT, 0);
-        return -1;
-    }
-
     cmp = (flags & ZIP_FL_NOCASE) ? strcasecmp : strcmp;
 
-    n = (flags & ZIP_FL_UNCHANGED) ? za->cdir->nentry : za->nentry;
-    for (i=0; i<n; i++) {
+    for (i=0; i<za->nentry; i++) {
 	fn = _zip_get_name(za, i, flags, error);
 
 	/* newly added (partially filled) entry or error */
diff --git a/lib/zip_new.c b/lib/zip_new.c
index 7ce1237..62e1420 100644
--- a/lib/zip_new.c
+++ b/lib/zip_new.c
@@ -1,6 +1,6 @@
 /*
   zip_new.c -- create and init struct zip
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -56,16 +56,16 @@
 
     za->zn = NULL;
     za->zp = NULL;
+    za->open_flags = 0;
     _zip_error_init(&za->error);
-    za->cdir = NULL;
-    za->ch_comment = NULL;
-    za->ch_comment_len = -1;
+    za->flags = za->ch_flags = 0;
+    za->default_password = NULL;
+    za->comment_orig = za->comment_changes = NULL;
+    za->comment_changed = 0;
     za->nentry = za->nentry_alloc = 0;
     za->entry = NULL;
     za->nfile = za->nfile_alloc = 0;
     za->file = NULL;
-    za->flags = za->ch_flags = 0;
-    za->default_password = NULL;
     
     return za;
 }
diff --git a/lib/zip_open.c b/lib/zip_open.c
index 3901ea5..dac8677 100644
--- a/lib/zip_open.c
+++ b/lib/zip_open.c
@@ -45,11 +45,10 @@
 static void set_error(int *, struct zip_error *, int);
 static struct zip *_zip_allocate_new(const char *, int, int *);
 static int _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *);
-static void _zip_check_torrentzip(struct zip *);
+static void _zip_check_torrentzip(struct zip *, const struct zip_cdir *);
 static struct zip_cdir *_zip_find_central_dir(FILE *, int, int *, off_t);
 static int _zip_file_exists(const char *, int, int *);
-static int _zip_headercomp(struct zip_dirent *, int,
-			   struct zip_dirent *, int);
+static int _zip_headercomp(struct zip_dirent *, struct zip_dirent *);
 static unsigned char *_zip_memmem(const unsigned char *, int,
 				  const unsigned char *, int);
 static struct zip_cdir *_zip_readcdir(FILE *, off_t, unsigned char *, unsigned char *,
@@ -100,7 +99,6 @@
 {
     struct zip *za;
     struct zip_cdir *cdir;
-    int i;
     off_t len;
 
     if (fseeko(fp, 0, SEEK_END) < 0) {
@@ -130,23 +128,19 @@
 	return NULL;
     }
 
-    za->cdir = cdir;
+    za->entry = cdir->entry;
+    za->nentry = cdir->nentry;
+    za->nentry_alloc = cdir->nentry_alloc;
+    za->comment_orig = cdir->comment;
+    
     za->zp = fp;
 
-    if (cdir->nentry == 0)
-	za->entry = NULL;
-    else if ((za->entry=(struct zip_entry *)malloc(sizeof(*(za->entry))
-						   * cdir->nentry)) == NULL) {
-	set_error(zep, NULL, ZIP_ER_MEMORY);
-	zip_discard(za);
-	return NULL;
-    }
-    for (i=0; i<cdir->nentry; i++)
-	_zip_entry_new(za);
+    _zip_check_torrentzip(za, cdir);
 
-    _zip_check_torrentzip(za);
     za->ch_flags = za->flags;
 
+    free(cdir);
+
     return za;
 }
 
@@ -182,12 +176,12 @@
     struct zip_cdir *cd;
     const unsigned char *cdp;
     const unsigned char **bufp;
-    zip_int32_t comlen;
+    zip_int32_t tail_len, comment_len;
     int i;
     zip_uint32_t left;
 
-    comlen = buf + buflen - eocd - EOCDLEN;
-    if (comlen < 0) {
+    tail_len = buf + buflen - eocd - EOCDLEN;
+    if (tail_len < 0) {
 	/* not enough bytes left for comment */
 	_zip_error_set(error, ZIP_ER_NOZIP, 0);
 	return NULL;
@@ -204,7 +198,7 @@
 	return NULL;
     }
 
-    if (eocd-buf < EOCD64LOCLEN && memcmp(eocd-EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0)
+    if (eocd-EOCD64LOCLEN >= buf && memcmp(eocd-EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0)
 	cd = _zip_read_eocd64(fp, eocd-EOCD64LOCLEN, buf, buf_offset, buflen, flags, error);
     else
 	cd = _zip_read_eocd(eocd, buf, buf_offset, buflen, flags, error);
@@ -213,35 +207,28 @@
 	return NULL;
 
     cdp = eocd + 20;
-    cd->comment = NULL;
-    cd->comment_len = _zip_read2(&cdp);
+    comment_len = _zip_read2(&cdp);
 
     if (((zip_uint64_t)cd->offset)+cd->size > buf_offset + (eocd-buf)) {
 	/* cdir spans past EOCD record */
 	_zip_error_set(error, ZIP_ER_INCONS, 0);
-	cd->nentry = 0;
 	_zip_cdir_free(cd);
 	return NULL;
     }
 
-    if (comlen < cd->comment_len) {
+    if (tail_len < comment_len) {
 	_zip_error_set(error, ZIP_ER_NOZIP, 0);
-	cd->nentry = 0;
 	_zip_cdir_free(cd);
 	return NULL;
     }
-    if ((flags & ZIP_CHECKCONS) && comlen != cd->comment_len) {
+    if ((flags & ZIP_CHECKCONS) && tail_len != comment_len) {
 	_zip_error_set(error, ZIP_ER_INCONS, 0);
-	cd->nentry = 0;
 	_zip_cdir_free(cd);
 	return NULL;
     }
 
-    if (cd->comment_len) {
-	if ((cd->comment=(char *)_zip_memdup(eocd+EOCDLEN,
-					     cd->comment_len, error))
-	    == NULL) {
-	    cd->nentry = 0;
+    if (comment_len) {
+	if ((cd->comment=_zip_string_new(eocd+EOCDLEN, comment_len, error)) == NULL) {
 	    _zip_cdir_free(cd);
 	    return NULL;
 	}
@@ -265,7 +252,6 @@
 		_zip_error_set(error, ZIP_ER_SEEK, errno);
 	    else
 		_zip_error_set(error, ZIP_ER_NOZIP, 0);
-	    cd->nentry = 0;
 	    _zip_cdir_free(cd);
 	    return NULL;
 	}
@@ -274,8 +260,8 @@
     left = cd->size;
     i=0;
     while (i<cd->nentry && left > 0) {
-	if ((_zip_dirent_read(cd->entry+i, fp, bufp, &left, 0, error)) < 0) {
-	    cd->nentry = i;
+	if ((cd->entry[i].orig=_zip_dirent_new()) == NULL
+	    || (_zip_dirent_read(cd->entry[i].orig, fp, bufp, &left, 0, error)) < 0) {
 	    _zip_cdir_free(cd);
 	    return NULL;
 	}
@@ -285,7 +271,6 @@
 	    /* Infozip extension for more than 64k entries:
 	       nentries wraps around, size indicates correct EOCD */
 	    if (_zip_cdir_grow(cd, cd->nentry+ZIP_UINT16_MAX, error) < 0) {
-		cd->nentry = i;
 		_zip_cdir_free(cd);
 		return NULL;
 	    }
@@ -312,22 +297,22 @@
     struct zip_dirent temp;
 
     if (cd->nentry) {
-	max = cd->entry[0].offset;
-	min = cd->entry[0].offset;
+	max = cd->entry[0].orig->offset;
+	min = cd->entry[0].orig->offset;
     }
     else
 	min = max = 0;
 
     for (i=0; i<cd->nentry; i++) {
-	if (cd->entry[i].offset < min)
-	    min = cd->entry[i].offset;
+	if (cd->entry[i].orig->offset < min)
+	    min = cd->entry[i].orig->offset;
 	if (min > cd->offset) {
 	    _zip_error_set(error, ZIP_ER_NOZIP, 0);
 	    return -1;
 	}
 	
-	j = cd->entry[i].offset + cd->entry[i].comp_size
-	    + strlen(cd->entry[i].settable.filename) + LENTRYSIZE;
+	j = cd->entry[i].orig->offset + cd->entry[i].orig->comp_size
+	    + _zip_string_length(cd->entry[i].orig->filename) + LENTRYSIZE;
 	if (j > max)
 	    max = j;
 	if (max > cd->offset) {
@@ -335,7 +320,7 @@
 	    return -1;
 	}
 	
-	if (fseeko(fp, cd->entry[i].offset, SEEK_SET) != 0) {
+	if (fseeko(fp, cd->entry[i].orig->offset, SEEK_SET) != 0) {
 	    _zip_error_set(error, ZIP_ER_SEEK, errno);
 	    return -1;
 	}
@@ -343,11 +328,16 @@
 	if (_zip_dirent_read(&temp, fp, NULL, NULL, 1, error) == -1)
 	    return -1;
 	
-	if (_zip_headercomp(cd->entry+i, 0, &temp, 1) != 0) {
+	if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) {
 	    _zip_error_set(error, ZIP_ER_INCONS, 0);
 	    _zip_dirent_finalize(&temp);
 	    return -1;
 	}
+	
+	cd->entry[i].orig->extra_fields = _zip_ef_merge(cd->entry[i].orig->extra_fields, temp.extra_fields);
+	cd->entry[i].orig->local_extra_fields_read = 1;
+	temp.extra_fields = NULL;
+	
 	_zip_dirent_finalize(&temp);
     }
 
@@ -360,28 +350,27 @@
    check wether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
 
 static void
-_zip_check_torrentzip(struct zip *za)
+_zip_check_torrentzip(struct zip *za, const struct zip_cdir *cdir)
 {
     uLong crc_got, crc_should;
     char buf[8+1];
     char *end;
 
-    if (za->zp == NULL || za->cdir == NULL)
+    if (za->zp == NULL || cdir == NULL)
 	return;
 
-    if (za->cdir->comment_len != TORRENT_SIG_LEN+8
-	|| strncmp(za->cdir->comment, TORRENT_SIG, TORRENT_SIG_LEN) != 0)
+    if (_zip_string_length(cdir->comment) != TORRENT_SIG_LEN+8
+	|| strncmp((const char *)cdir->comment->raw, TORRENT_SIG, TORRENT_SIG_LEN) != 0)
 	return;
 
-    memcpy(buf, za->cdir->comment+TORRENT_SIG_LEN, 8);
+    memcpy(buf, cdir->comment->raw+TORRENT_SIG_LEN, 8);
     buf[8] = '\0';
     errno = 0;
     crc_should = strtoul(buf, &end, 16);
     if ((crc_should == UINT_MAX && errno != 0) || (end && *end))
 	return;
 
-    if (_zip_filerange_crc(za->zp, za->cdir->offset, za->cdir->size,
-			   &crc_got, NULL) < 0)
+    if (_zip_filerange_crc(za->zp, cdir->offset, cdir->size, &crc_got, NULL) < 0)
 	return;
 
     if (crc_got == crc_should)
@@ -392,66 +381,32 @@
 
 
 /* _zip_headercomp:
-   compares two headers h1 and h2; if they are local headers, set
-   local1p or local2p respectively to 1, else 0. Return 0 if they
-   are identical, -1 if not. */
+   compares a central directory entry and a local file header
+   Return 0 if they are consistent, -1 if not. */
 
 static int
-_zip_headercomp(struct zip_dirent *h1, int local1p, struct zip_dirent *h2,
-	   int local2p)
+_zip_headercomp(struct zip_dirent *central, struct zip_dirent *local)
 {
-    if ((h1->version_needed != h2->version_needed)
+    if ((central->version_needed != local->version_needed)
 #if 0
 	/* some zip-files have different values in local
 	   and global headers for the bitflags */
-	|| (h1->bitflags != h2->bitflags)
+	|| (central->bitflags != local->bitflags)
 #endif
-	|| (h1->settable.comp_method != h2->settable.comp_method)
-	|| (h1->last_mod != h2->last_mod)
-	|| strcmp(h1->settable.filename, h2->settable.filename))
+	|| (central->comp_method != local->comp_method)
+	|| (central->last_mod != local->last_mod)
+	|| !_zip_string_equal(central->filename, local->filename))
 	return -1;
 
-    /* check that CRC and sizes are zero if data descriptor is used */
-    if ((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local1p
-	&& (h1->crc != 0
-	    || h1->comp_size != 0
-	    || h1->uncomp_size != 0))
-	return -1;
-    if ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local2p
-	&& (h2->crc != 0
-	    || h2->comp_size != 0
-	    || h2->uncomp_size != 0))
-	return -1;
-    
-    /* check that CRC and sizes are equal if no data descriptor is used */
-    if (((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local1p == 0)
-	&& ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local2p == 0)) {
-	if ((h1->crc != h2->crc)
-	    || (h1->comp_size != h2->comp_size)
-	    || (h1->uncomp_size != h2->uncomp_size))
+
+    if ((central->crc != local->crc) || (central->comp_size != local->comp_size)
+	|| (central->uncomp_size != local->uncomp_size)) {
+	/* InfoZip stores valid values in local header even when data descriptor is used.
+	   This is in violation of the appnote. */
+	if (((local->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0
+	     || local->crc != 0 || local->comp_size != 0 || local->uncomp_size != 0))
 	    return -1;
     }
-    
-    if ((local1p == local2p)
-	&& ((h1->settable.extrafield_len != h2->settable.extrafield_len)
-	    || (h1->settable.extrafield_len && h2->settable.extrafield
-		&& memcmp(h1->settable.extrafield, h2->settable.extrafield,
-			  h1->settable.extrafield_len))))
-	return -1;
-
-    /* if either is local, nothing more to check */
-    if (local1p || local2p)
-	return 0;
-
-    if ((h1->version_madeby != h2->version_madeby)
-	|| (h1->disk_number != h2->disk_number)
-	|| (h1->int_attrib != h2->int_attrib)
-	|| (h1->ext_attrib != h2->ext_attrib)
-	|| (h1->offset != h2->offset)
-	|| (h1->settable.comment_len != h2->settable.comment_len)
-	|| (h1->settable.comment_len && h2->settable.comment
-	    && memcmp(h1->settable.comment, h2->settable.comment, h1->settable.comment_len)))
-	return -1;
 
     return 0;
 }
@@ -672,6 +627,11 @@
     cdp = eocd64loc+8;
     eocd_offset = _zip_read8(&cdp);
 
+    if (eocd_offset+EOCD64LEN > buf_offset+(eocd64loc-buf)) {
+	_zip_error_set(error, ZIP_ER_INCONS, 0);
+	return NULL;
+    }
+
     if (eocd_offset >= buf_offset)
 	cdp = buf+(eocd_offset-buf_offset);
     else {
@@ -692,14 +652,14 @@
     }
 
     if (memcmp(cdp, EOCD64_MAGIC, 4) != 0) {
-	_zip_error_set(error, ZIP_ER_NOZIP, 0);
+	_zip_error_set(error, ZIP_ER_INCONS, 0);
 	return NULL;
     }
     cdp += 4;
     
     size = _zip_read8(&cdp);
 
-    if ((flags & ZIP_CHECKCONS) && size+eocd_offset != buf_offset+(eocd64loc-buf)) {
+    if ((flags & ZIP_CHECKCONS) && size+eocd_offset+12 != buf_offset+(eocd64loc-buf)) {
 	_zip_error_set(error, ZIP_ER_INCONS, 0);
 	return NULL;
     }
diff --git a/lib/zip_replace.c b/lib/zip_replace.c
index 53524e6..0d39306 100644
--- a/lib/zip_replace.c
+++ b/lib/zip_replace.c
@@ -68,17 +68,19 @@
 
     za_nentry_prev = za->nentry;
     if (idx == ZIP_UINT64_MAX) {
-	/* create and use new entry, used by zip_add */
-	if (_zip_entry_new(za) == NULL)
-	    return -1;
+	zip_int64_t i;
 	
-	idx = za->nentry - 1;
-	za->entry[idx].changes.valid |= ZIP_DIRENT_COMP_METHOD;
-	za->entry[idx].changes.comp_method = ZIP_CM_DEFLATE; /* XXX: default */
+	/* create and use new entry, used by zip_add */
+	if ((i=_zip_add_entry(za)) < 0)
+	    return -1;
+	idx = i;
     }
     
     if (name && _zip_set_name(za, idx, name) != 0) {
-	za->nentry = za_nentry_prev;
+	if (za->nentry != za_nentry_prev) {
+	    _zip_entry_finalize(za->entry+idx);
+	    za->nentry = za_nentry_prev;
+	}
 	return -1;
     }
 
@@ -86,8 +88,6 @@
      * needed for a double add of the same file name */
     _zip_unchange_data(za->entry+idx);
 
-    za->entry[idx].state = ((za->cdir == NULL || idx >= za->cdir->nentry)
-			    ? ZIP_ST_ADDED : ZIP_ST_REPLACED);
     za->entry[idx].source = source;
 
     return idx;
diff --git a/lib/zip_set_archive_comment.c b/lib/zip_set_archive_comment.c
index bbb9a73..f71bea7 100644
--- a/lib/zip_set_archive_comment.c
+++ b/lib/zip_set_archive_comment.c
@@ -42,7 +42,12 @@
 ZIP_EXTERN int
 zip_set_archive_comment(struct zip *za, const char *comment, int len)
 {
-    char *tmpcom;
+    struct zip_string *cstr;
+
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	return -1;
+    }
 
     if (len < 0 || len > MAXCOMLEN
 	|| (len > 0 && comment == NULL)) {
@@ -50,26 +55,31 @@
 	return -1;
     }
 
-    if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
-	return -1;
-    }
-
-    if (_zip_guess_encoding(comment, len) == ZIP_ENCODING_CP437) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
-	return -1;
-    }
-
     if (len > 0) {
-	if ((tmpcom=(char *)_zip_memdup(comment, len, &za->error)) == NULL)
+	if ((cstr=_zip_string_new((const zip_uint8_t *)comment, len, &za->error)) == NULL)
 	    return -1;
+
+	if (_zip_guess_encoding(cstr, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_CP437) {
+	    _zip_string_free(cstr);
+	    _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	    return -1;
+	}
     }
     else
-	tmpcom = NULL;
+	cstr = NULL;
 
-    free(za->ch_comment);
-    za->ch_comment = tmpcom;
-    za->ch_comment_len = len;
+    _zip_string_free(za->comment_changes);
+    za->comment_changes = NULL;
+
+    if (((za->comment_orig && _zip_string_equal(za->comment_orig, cstr))
+	 || (za->comment_orig == NULL && cstr == NULL))) {
+	_zip_string_free(cstr);
+	za->comment_changed = 0;
+    }
+    else {
+	za->comment_changes = cstr;
+	za->comment_changed = 1;
+    }
     
     return 0;
 }
diff --git a/lib/zip_set_file_comment.c b/lib/zip_set_file_comment.c
index bccad10..0621ad2 100644
--- a/lib/zip_set_file_comment.c
+++ b/lib/zip_set_file_comment.c
@@ -44,48 +44,74 @@
 zip_set_file_comment(struct zip *za, zip_uint64_t idx,
 		     const char *comment, int len)
 {
-    char *tmpcom;
-    const char *name;
-    enum zip_encoding_type com_enc, enc;
+    struct zip_entry *e;
+    struct zip_string *cstr;
+    struct zip_dirent *de;
+    enum zip_encoding_type com_enc, name_enc;
+    int changed;
 
-    if (idx >= za->nentry
-	|| len < 0 || len > MAXCOMLEN
-	|| (len > 0 && comment == NULL)) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+    if ((de=_zip_get_dirent(za, idx, 0, NULL)) == NULL)
 	return -1;
-    }
 
     if (ZIP_IS_RDONLY(za)) {
 	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
-    if ((com_enc=_zip_guess_encoding(comment, len)) == ZIP_ENCODING_CP437) {
+    if (len < 0 || len > MAXCOMLEN
+	|| (len > 0 && comment == NULL)) {
 	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
-    if ((name=zip_get_name(za, idx, ZIP_FL_NAME_RAW)) == NULL)
-	return -1;
-    enc = _zip_guess_encoding(name, strlen(name));
+    if (len > 0) {
+	if ((cstr=_zip_string_new((const zip_uint8_t *)comment, len, &za->error)) == NULL)
+	    return -1;
 
-    if (enc == ZIP_ENCODING_CP437 && com_enc == ZIP_ENCODING_UTF8) {
+	if ((com_enc=_zip_guess_encoding(cstr, ZIP_ENCODING_UTF8_KNOWN)) == ZIP_ENCODING_ERROR) {
+	    _zip_string_free(cstr);
+	    _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	    return -1;
+	}
+    }
+    else {
+	cstr = NULL;
+	com_enc = ZIP_ENCODING_ASCII;
+    }
+
+    name_enc = _zip_guess_encoding(de->filename, ZIP_ENCODING_UNKNOWN);
+
+    if (name_enc == ZIP_ENCODING_CP437 && com_enc == ZIP_ENCODING_UTF8_KNOWN) {
 	_zip_error_set(&za->error, ZIP_ER_ENCMISMATCH, 0);
 	return -1;
     }
 
-    if (len > 0) {
-	if ((tmpcom=(char *)_zip_memdup(comment, len, &za->error)) == NULL)
-	    return -1;
-    }
-    else
-	tmpcom = NULL;
+    e = za->entry+idx;
 
-    if (za->entry[idx].changes.valid & ZIP_DIRENT_COMMENT)
-	free(za->entry[idx].changes.comment);
-    za->entry[idx].changes.comment = tmpcom;
-    za->entry[idx].changes.comment_len = len;
-    za->entry[idx].changes.valid |= ZIP_DIRENT_COMMENT;
-    
+    if (e->changes) {
+	_zip_string_free(e->changes->comment);
+	e->changes->comment = NULL;
+	e->changes->changed &= ~ZIP_DIRENT_COMMENT;
+    }
+
+    if (e->orig && e->orig->comment)
+	changed = !_zip_string_equal(e->orig->comment, cstr);
+    else
+	changed = (cstr != NULL);
+	
+    if (changed) {
+	if (e->changes == NULL)
+	    e->changes = _zip_dirent_clone(e->orig);
+	e->changes->comment = cstr;
+	e->changes->changed |= ZIP_DIRENT_COMMENT;
+    }
+    else {
+	_zip_string_free(cstr);
+	if (e->changes && e->changes->changed == 0) {
+	    _zip_dirent_free(e->changes);
+	    e->changes = NULL;
+	}
+    }
+
     return 0;
 }
diff --git a/lib/zip_set_file_compression.c b/lib/zip_set_file_compression.c
index 25cb5a0..51a74a2 100644
--- a/lib/zip_set_file_compression.c
+++ b/lib/zip_set_file_compression.c
@@ -41,32 +41,38 @@
 zip_set_file_compression(struct zip *za, zip_uint64_t idx,
 			 zip_int32_t method, zip_uint32_t flags)
 {
+    struct zip_entry *e;
+
     if (idx >= za->nentry) {
 	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
-    if (method != ZIP_CM_DEFAULT && method != ZIP_CM_STORE && method != ZIP_CM_DEFLATE) {
-	_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
-	return -1;
-    }
-
     if (ZIP_IS_RDONLY(za)) {
 	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
-    /* XXX: needs support for better of deflate/store
-     *      update man page when fixing this */
-    if (method == ZIP_CM_DEFAULT)
-	method = ZIP_CM_DEFLATE;
+    if (method != ZIP_CM_DEFAULT && method != ZIP_CM_STORE && method != ZIP_CM_DEFLATE) {
+	_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
+	return -1;
+    }
 
-    if (za->cdir != NULL && idx < za->cdir->nentry &&
-	method == za->cdir->entry[idx].settable.comp_method)
-	za->entry[idx].changes.valid &= ~ZIP_DIRENT_COMP_METHOD;
-    else {
-	za->entry[idx].changes.valid |= ZIP_DIRENT_COMP_METHOD;
-	za->entry[idx].changes.comp_method = method;
+    e = za->entry+idx;
+    
+    if (method != (e->orig ? e->orig->comp_method : ZIP_CM_DEFAULT)) {
+	if (e->changes == NULL)
+	    e->changes = _zip_dirent_clone(e->orig);
+
+	e->changes->comp_method = method;
+	e->changes->changed |= ZIP_DIRENT_COMP_METHOD;
+    }
+    else if (e->changes) {
+	e->changes->changed &= ~ZIP_DIRENT_COMP_METHOD;
+	if (e->changes->changed == 0) {
+	    _zip_dirent_free(e->changes);
+	    e->changes = NULL;
+	}
     }
     
     return 0;
diff --git a/lib/zip_set_file_extra.c b/lib/zip_set_file_extra.c
deleted file mode 100644
index 1a85bff..0000000
--- a/lib/zip_set_file_extra.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-  zip_set_file_extra.c -- set extra field for file in archive
-  Copyright (C) 2006-2010 Dieter Baron and Thomas Klausner
-
-  This file is part of libzip, a library to manipulate ZIP archives.
-  The authors can be contacted at <libzip@nih.at>
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions
-  are met:
-  1. Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-  2. Redistributions in binary form must reproduce the above copyright
-     notice, this list of conditions and the following disclaimer in
-     the documentation and/or other materials provided with the
-     distribution.
-  3. The names of the authors may not be used to endorse or promote
-     products derived from this software without specific prior
-     written permission.
-
-  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
-  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
-  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-
-
-#include <stdlib.h>
-
-#include "zipint.h"
-
-
-
-ZIP_EXTERN int
-zip_set_file_extra(struct zip *za, zip_uint64_t idx,
-		   const char *extra, int len)
-{
-    char *tmpext;
-
-    if (idx >= za->nentry
-	|| len < 0 || len > MAXEXTLEN
-	|| (len > 0 && extra == NULL)) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
-	return -1;
-    }
-
-    if (ZIP_IS_RDONLY(za)) {
-	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
-	return -1;
-    }
-
-    if (len > 0) {
-	if ((tmpext=(char *)_zip_memdup(extra, len, &za->error)) == NULL)
-	    return -1;
-    }
-    else
-	tmpext = NULL;
-
-    if (za->entry[idx].changes.valid & ZIP_DIRENT_EXTRAFIELD)
-	free(za->entry[idx].changes.extrafield);
-    za->entry[idx].changes.extrafield = tmpext;
-    za->entry[idx].changes.extrafield_len = len;
-    za->entry[idx].changes.valid |= ZIP_DIRENT_EXTRAFIELD;
-
-    return 0;
-}
diff --git a/lib/zip_set_name.c b/lib/zip_set_name.c
index 7b43131..70a01d9 100644
--- a/lib/zip_set_name.c
+++ b/lib/zip_set_name.c
@@ -43,53 +43,90 @@
 int
 _zip_set_name(struct zip *za, zip_uint64_t idx, const char *name)
 {
-    char *s;
-    const char *com;
-    int comlen;
-    zip_int64_t i;
-    enum zip_encoding_type enc, com_enc;
-    
-    if (idx >= za->nentry || name == NULL) {
+    struct zip_entry *e;
+    struct zip_string *str;
+    struct zip_dirent *de;
+    enum zip_encoding_type com_enc, name_enc;
+    int changed;
+    int i;
+
+    if (idx >= za->nentry) {
 	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
     }
 
-    if ((enc=_zip_guess_encoding(name, strlen(name))) == ZIP_ENCODING_CP437) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
 	return -1;
     }
 
+    if (name && strlen(name) > 0) {
+	if ((str=_zip_string_new((const zip_uint8_t *)name, strlen(name), &za->error)) == NULL)
+	    return -1;
+
+	if ((name_enc=_zip_guess_encoding(str, ZIP_ENCODING_UTF8_KNOWN)) == ZIP_ENCODING_ERROR) {
+	    _zip_string_free(str);
+	    _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+	    return -1;
+	}
+    }
+    else {
+	str = NULL;
+	name_enc = ZIP_ENCODING_ASCII;
+    }
+
     if ((i=_zip_name_locate(za, name, 0, NULL)) != -1 && i != idx) {
+	_zip_string_free(str);
 	_zip_error_set(&za->error, ZIP_ER_EXISTS, 0);
 	return -1;
     }
 
     /* no effective name change */
-    if (i == idx)
+    if (i == idx) {
+	_zip_string_free(str);
 	return 0;
+    }
 
-    com = zip_get_file_comment(za, idx, &comlen, ZIP_FL_NAME_RAW);
-    if (com == NULL)
-	com_enc = ZIP_ENCODING_ASCII;
+    de = _zip_get_dirent(za, idx, 0, NULL);
+
+    if (de)
+	com_enc = _zip_guess_encoding(de->comment, ZIP_ENCODING_UNKNOWN);
     else
-	com_enc = _zip_guess_encoding(com, comlen);
-    if (com_enc == ZIP_ENCODING_CP437 && enc == ZIP_ENCODING_UTF8) {
+	com_enc = ZIP_ENCODING_ASCII;
+
+    if (com_enc == ZIP_ENCODING_CP437 && name_enc == ZIP_ENCODING_UTF8_KNOWN) {
+	_zip_string_free(str);
 	_zip_error_set(&za->error, ZIP_ER_ENCMISMATCH, 0);
 	return -1;
     }
 
-    if ((s=strdup(name)) == NULL) {
-	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
-	return -1;
-    }
-    
-    if (za->entry[idx].state == ZIP_ST_UNCHANGED) 
-	za->entry[idx].state = ZIP_ST_RENAMED;
 
-    if (za->entry[idx].changes.valid & ZIP_DIRENT_FILENAME)
-	free(za->entry[idx].changes.filename);
-    za->entry[idx].changes.filename = s;
-    za->entry[idx].changes.valid |= ZIP_DIRENT_FILENAME;
+    e = za->entry+idx;
+
+    if (e->changes) {
+	_zip_string_free(e->changes->filename);
+	e->changes->filename = NULL;
+	e->changes->changed &= ~ZIP_DIRENT_FILENAME;
+    }
+
+    if (e->orig && e->orig->filename)
+	changed = !_zip_string_equal(e->orig->filename, str);
+    else
+	changed = (str != NULL);
+	
+    if (changed) {
+	if (e->changes == NULL)
+	    e->changes = _zip_dirent_clone(e->orig);
+	e->changes->filename = str;
+	e->changes->changed |= ZIP_DIRENT_FILENAME;
+    }
+    else {
+	_zip_string_free(str);
+	if (e->changes && e->changes->changed == 0) {
+	    _zip_dirent_free(e->changes);
+	    e->changes = NULL;
+	}
+    }
 
     return 0;
 }
diff --git a/lib/zip_source_zip_new.c b/lib/zip_source_zip_new.c
index b5364a1..d3aa339 100644
--- a/lib/zip_source_zip_new.c
+++ b/lib/zip_source_zip_new.c
@@ -58,13 +58,13 @@
     }
 
     if ((flags & ZIP_FL_UNCHANGED) == 0
-	&& (ZIP_ENTRY_DATA_CHANGED(srcza->entry+srcidx) || srcza->entry[srcidx].state == ZIP_ST_DELETED)) {
+	&& (ZIP_ENTRY_DATA_CHANGED(srcza->entry+srcidx) || srcza->entry[srcidx].deleted)) {
 	_zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
 	return NULL;
     }
 
-    if (srcza->cdir == NULL || srcidx >= srcza->cdir->nentry) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+    if (zip_stat_index(srcza, srcidx, flags|ZIP_FL_UNCHANGED, &st) < 0) {
+	_zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
 	return NULL;
     }
 
@@ -76,11 +76,6 @@
 	return NULL;
     }
 
-    if (zip_stat_index(srcza, srcidx, flags, &st) < 0) {
-	_zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
-	return NULL;
-    }
-
     if (len == -1)
 	len = 0;
 
@@ -96,8 +91,7 @@
 	    _zip_error_set(&za->error, ZIP_ER_NOPASSWD, 0);
 	    return NULL;
 	}
-	if ((enc_impl=zip_get_encryption_implementation(
-		 st.encryption_method)) == NULL) {
+	if ((enc_impl=zip_get_encryption_implementation(st.encryption_method)) == NULL) {
 	    _zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
 	    return NULL;
 	}
@@ -106,8 +100,7 @@
     comp_impl = NULL;
     if ((flags & ZIP_FL_COMPRESSED) == 0) {
 	if (st.comp_method != ZIP_CM_STORE) {
-	    if ((comp_impl=zip_get_compression_implementation(
-		     st.comp_method)) == NULL) {
+	    if ((comp_impl=zip_get_compression_implementation(st.comp_method)) == NULL) {
 		_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
 		return NULL;
 	    }
@@ -123,9 +116,15 @@
     }
     else {
 	if (start+len > 0 && enc_impl == NULL && comp_impl == NULL) {
-	    if ((src=_zip_source_file_or_p(za, NULL, srcza->zp, offset+start,
-					   len ? len : st.size-start,
-					   0, &st)) == NULL)
+	    struct zip_stat st2;
+
+	    st2.size = len ? len : st.size-start;
+	    st2.comp_size = st2.size;
+	    st2.comp_method = ZIP_CM_STORE;
+	    st2.mtime = st.mtime;
+	    st2.valid = ZIP_STAT_SIZE|ZIP_STAT_COMP_SIZE|ZIP_STAT_COMP_METHOD|ZIP_STAT_MTIME;
+
+	    if ((src=_zip_source_file_or_p(za, NULL, srcza->zp, offset+start, st2.size, 0, &st2)) == NULL)
 		return NULL;
 	}
 	else {
@@ -151,7 +150,7 @@
 	    src = s2;
 	}
 	if (((flags & ZIP_FL_COMPRESSED) == 0 || st.comp_method == ZIP_CM_STORE)
-	    && (len == 0 || start+len == st.comp_size)) {
+	    && (len == 0 || len == st.comp_size)) {
 	    /* when reading the whole file, check for crc errors */
 	    if ((s2=zip_source_crc(za, src, 1)) == NULL) {
 		zip_source_free(src);
diff --git a/lib/zip_stat_index.c b/lib/zip_stat_index.c
index 771c686..4427958 100644
--- a/lib/zip_stat_index.c
+++ b/lib/zip_stat_index.c
@@ -42,11 +42,10 @@
 	       struct zip_stat *st)
 {
     const char *name;
-    
-    if (index >= za->nentry) {
-	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+    struct zip_dirent *de;
+
+    if ((de=_zip_get_dirent(za, index, flags, NULL)) == NULL)
 	return -1;
-    }
 
     if ((name=zip_get_name(za, index, flags)) == NULL)
 	return -1;
@@ -60,23 +59,15 @@
 	}
     }
     else {
-	if (za->cdir == NULL || index >= za->cdir->nentry) {
-	    _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
-	    return -1;
-	}
-
 	zip_stat_init(st);
 
-	st->crc = za->cdir->entry[index].crc;
-	st->size = za->cdir->entry[index].uncomp_size;
-	st->mtime = za->cdir->entry[index].last_mod;
-	st->comp_size = za->cdir->entry[index].comp_size;
-	if ((flags & ZIP_FL_UNCHANGED) == 0 && (za->entry[index].changes.valid & ZIP_DIRENT_COMP_METHOD))
-	    st->comp_method = za->entry[index].changes.comp_method;
-	else
-	    st->comp_method = za->cdir->entry[index].settable.comp_method;
-	if (za->cdir->entry[index].bitflags & ZIP_GPBF_ENCRYPTED) {
-	    if (za->cdir->entry[index].bitflags & ZIP_GPBF_STRONG_ENCRYPTION) {
+	st->crc = de->crc;
+	st->size = de->uncomp_size;
+	st->mtime = de->last_mod;
+	st->comp_size = de->comp_size;
+	st->comp_method = de->comp_method;
+	if (de->bitflags & ZIP_GPBF_ENCRYPTED) {
+	    if (de->bitflags & ZIP_GPBF_STRONG_ENCRYPTION) {
 		/* XXX */
 		st->encryption_method = ZIP_EM_UNKNOWN;
 	    }
diff --git a/lib/zip_string.c b/lib/zip_string.c
new file mode 100644
index 0000000..b837ea8
--- /dev/null
+++ b/lib/zip_string.c
@@ -0,0 +1,157 @@
+/*
+  zip_string.c -- string handling (with encoding)
+  Copyright (C) 2012 Dieter Baron and Thomas Klausner
+
+  This file is part of libzip, a library to manipulate ZIP archives.
+  The authors can be contacted at <libzip@nih.at>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "zipint.h"
+
+
+
+int
+_zip_string_equal(const struct zip_string *a, const struct zip_string *b)
+{
+    if (a == NULL || b == NULL)
+	return a == b;
+
+    if (a->length != b->length)
+	return 0;
+
+    /* XXX: encoding */
+
+    return (memcmp(a->raw, b->raw, a->length) == 0);
+}
+
+
+
+void
+_zip_string_free(struct zip_string *s)
+{
+    if (s == NULL)
+	return;
+
+    free(s->raw);
+    free(s->converted);
+    free(s);
+}
+
+
+
+const zip_uint8_t *
+_zip_string_get(struct zip_string *string, zip_uint32_t *lenp, int flags, struct zip_error *error)
+{
+    static const zip_uint8_t empty[1] = "";
+
+    if (string == NULL) {
+	if (lenp)
+	    *lenp = 0;
+	return empty;
+    }
+
+    if ((flags & ZIP_FL_NAME_RAW) == 0) {
+	/* start guessing */
+	if (string->encoding == ZIP_ENCODING_UNKNOWN)
+	    _zip_guess_encoding(string, ZIP_ENCODING_UNKNOWN);
+
+	if (((flags & ZIP_FL_NAME_STRICT)
+	     && string->encoding != ZIP_ENCODING_ASCII && string->encoding != ZIP_ENCODING_UTF8_KNOWN)
+	    || (string->encoding == ZIP_ENCODING_CP437)) {
+	    if (string->converted == NULL) {
+		if ((string->converted=_zip_cp437_to_utf8(string->raw, string->length,
+							  &string->converted_length, error)) == NULL)
+		    return NULL;
+	    }
+	    if (lenp)
+		*lenp = string->converted_length;
+	    return string->converted;
+	}
+    }
+    
+    if (lenp)
+	*lenp = string->length;
+    return string->raw;
+}
+
+
+
+zip_uint16_t
+_zip_string_length(const struct zip_string *s)
+{
+    if (s == NULL)
+	return 0;
+
+    return s->length;
+}
+
+
+
+struct zip_string *
+_zip_string_new(const zip_uint8_t *raw, zip_uint16_t length, struct zip_error *error)
+{
+    struct zip_string *s;
+    
+    if (length == 0)
+	return NULL;
+
+    if ((s=malloc(sizeof(*s))) == NULL) {
+	_zip_error_set(error, ZIP_ER_MEMORY, 0);
+	return NULL;
+    }
+
+    if ((s->raw=malloc(length+1)) == NULL) {
+	free(s);
+	return NULL;
+    }
+
+    memcpy(s->raw, raw, length);
+    s->raw[length] = '\0';
+    s->length = length;
+    s->encoding = ZIP_ENCODING_UNKNOWN;
+    s->converted = NULL;
+    s->converted_length = 0;
+
+    return s;
+}
+
+
+
+void
+_zip_string_write(const struct zip_string *s, FILE *f)
+{
+    if (s == NULL)
+	return;
+    
+    fwrite(s->raw, s->length, 1, f);
+}
diff --git a/lib/zip_unchange.c b/lib/zip_unchange.c
index 61adf50..7026063 100644
--- a/lib/zip_unchange.c
+++ b/lib/zip_unchange.c
@@ -57,25 +57,16 @@
 	return -1;
     }
 
-    if (za->entry[idx].changes.valid & ZIP_DIRENT_FILENAME) {
-	if (!allow_duplicates) {
-	    i = _zip_name_locate(za,
-			 _zip_get_name(za, idx, ZIP_FL_UNCHANGED, NULL),
-				 0, NULL);
-	    if (i != -1 && i != idx) {
-		_zip_error_set(&za->error, ZIP_ER_EXISTS, 0);
-		return -1;
-	    }
+    if (!allow_duplicates && za->entry[idx].changes && (za->entry[idx].changes->changed & ZIP_DIRENT_FILENAME)) {
+	i = _zip_name_locate(za, _zip_get_name(za, idx, ZIP_FL_UNCHANGED, NULL), 0, NULL);
+	if (i != -1 && i != idx) {
+	    _zip_error_set(&za->error, ZIP_ER_EXISTS, 0);
+	    return -1;
 	}
-
-	free(za->entry[idx].changes.filename);
     }
 
-    if (za->entry[idx].changes.valid & ZIP_DIRENT_EXTRAFIELD)
-	free(za->entry[idx].changes.extrafield);
-    if (za->entry[idx].changes.valid & ZIP_DIRENT_COMMENT)
-	free(za->entry[idx].changes.comment);
-    za->entry[idx].changes.valid = 0;
+    _zip_dirent_free(za->entry[idx].changes);
+    za->entry[idx].changes = NULL;
 
     _zip_unchange_data(za->entry+idx);
 
diff --git a/lib/zip_unchange_archive.c b/lib/zip_unchange_archive.c
index 8f9c024..e24ee21 100644
--- a/lib/zip_unchange_archive.c
+++ b/lib/zip_unchange_archive.c
@@ -42,10 +42,12 @@
 ZIP_EXTERN int
 zip_unchange_archive(struct zip *za)
 {
-    free(za->ch_comment);
-    za->ch_comment = NULL;
-    za->ch_comment_len = -1;
-
+    if (za->comment_changed) {
+	_zip_string_free(za->comment_changes);
+	za->comment_changes = NULL;
+	za->comment_changed = 0;
+    }
+    
     za->ch_flags = za->flags;
 
     return 0;
diff --git a/lib/zip_unchange_data.c b/lib/zip_unchange_data.c
index fab51de..105ea3a 100644
--- a/lib/zip_unchange_data.c
+++ b/lib/zip_unchange_data.c
@@ -1,6 +1,6 @@
 /*
   zip_unchange_data.c -- undo helper function
-  Copyright (C) 1999, 2004 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -33,8 +33,6 @@
 
 
 
-#include <stdlib.h>
-
 #include "zipint.h"
 
 void
@@ -44,7 +42,7 @@
 	zip_source_free(ze->source);
 	ze->source = NULL;
     }
-    
-    ze->state = (ze->changes.valid & ZIP_DIRENT_FILENAME) ? ZIP_ST_RENAMED : ZIP_ST_UNCHANGED;
+
+    ze->deleted = 0;
 }
 
diff --git a/lib/zip_utf-8.c b/lib/zip_utf-8.c
index d184b17..4b8e4b1 100644
--- a/lib/zip_utf-8.c
+++ b/lib/zip_utf-8.c
@@ -118,39 +118,65 @@
 
 
 enum zip_encoding_type
-_zip_guess_encoding(const char * const _name, zip_uint32_t len)
+_zip_guess_encoding(struct zip_string *str, enum zip_encoding_type expected_encoding)
 {
-    zip_uint8_t *name = (zip_uint8_t *)_name;
+    enum zip_encoding_type enc;
+    const zip_uint8_t *name;
     zip_uint32_t i;
-    int ret;
     int j, ulen;
 
-    ret = ZIP_ENCODING_ASCII;
-    for (i=0; i<len; i++) {
-	if ((name[i] > 31 && name[i] < 128) || name[i] == '\r' || name[i] == '\n' || name[i] == '\t')
-	    continue;
+    if (str == NULL)
+	return ZIP_ENCODING_ASCII;
 
-	ret = ZIP_ENCODING_UTF8;
-	if ((name[i] & UTF_8_LEN_2_MASK) == UTF_8_LEN_2_MATCH)
-	    ulen = 1;
-	else if ((name[i] & UTF_8_LEN_3_MASK) == UTF_8_LEN_3_MATCH)
-	    ulen = 2;
-	else if ((name[i] & UTF_8_LEN_4_MASK) == UTF_8_LEN_4_MATCH)
-	    ulen = 3;
-	else
-	    return ZIP_ENCODING_CP437;
+    name = str->raw;
 
-	if (i + ulen >= len)
-	    return ZIP_ENCODING_CP437;
+    if (str->encoding != ZIP_ENCODING_UNKNOWN)
+	enc = str->encoding;
+    else {
+	enc = ZIP_ENCODING_ASCII;
+	for (i=0; i<str->length; i++) {
+	    if ((name[i] > 31 && name[i] < 128) || name[i] == '\r' || name[i] == '\n' || name[i] == '\t')
+		continue;
 
-	for (j=1; j<=ulen; j++) {
-	    if ((name[i+j] & UTF_8_CONTINUE_MASK) != UTF_8_CONTINUE_MATCH)
-		return ZIP_ENCODING_CP437;
+	    enc = ZIP_ENCODING_UTF8_GUESSED;
+	    if ((name[i] & UTF_8_LEN_2_MASK) == UTF_8_LEN_2_MATCH)
+		ulen = 1;
+	    else if ((name[i] & UTF_8_LEN_3_MASK) == UTF_8_LEN_3_MATCH)
+		ulen = 2;
+	    else if ((name[i] & UTF_8_LEN_4_MASK) == UTF_8_LEN_4_MATCH)
+		ulen = 3;
+	    else {
+		enc = ZIP_ENCODING_CP437;
+		break;
+	    }
+
+	    if (i + ulen >= str->length) {
+		enc = ZIP_ENCODING_CP437;
+		break;
+	    }
+
+	    for (j=1; j<=ulen; j++) {
+		if ((name[i+j] & UTF_8_CONTINUE_MASK) != UTF_8_CONTINUE_MATCH) {
+		    enc = ZIP_ENCODING_CP437;
+		    goto done;
+		}
+	    }
+	    i += ulen;
 	}
-	i += ulen;
     }
 
-    return ret;
+done:
+    str->encoding = enc;
+
+    if (expected_encoding != ZIP_ENCODING_UNKNOWN) {
+	if (expected_encoding == ZIP_ENCODING_UTF8_KNOWN && enc == ZIP_ENCODING_UTF8_GUESSED)
+	    str->encoding = enc = ZIP_ENCODING_UTF8_KNOWN;
+
+	if (expected_encoding != enc && enc != ZIP_ENCODING_ASCII)
+	    return ZIP_ENCODING_ERROR;
+    }
+    
+    return enc;
 }
 
 
@@ -196,8 +222,8 @@
 
 
 
-char *
-_zip_cp437_to_utf8(const char * const _cp437buf, zip_uint32_t len,
+zip_uint8_t *
+_zip_cp437_to_utf8(const zip_uint8_t * const _cp437buf, zip_uint32_t len,
 		   zip_uint32_t *utf8_lenp, struct zip_error *error)
 {
     zip_uint8_t *cp437buf = (zip_uint8_t *)_cp437buf;
@@ -227,5 +253,5 @@
     utf8buf[buflen-1] = 0;
     if (utf8_lenp)
 	*utf8_lenp = buflen-1;
-    return (char *)utf8buf;
+    return utf8buf;
 }
diff --git a/lib/zipint.h b/lib/zipint.h
index c2cdf3b..a03fe7c 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -180,11 +180,6 @@
 
 
 
-/* state of change of a file in zip archive */
-
-enum zip_state { ZIP_ST_UNCHANGED, ZIP_ST_DELETED, ZIP_ST_REPLACED,
-		 ZIP_ST_ADDED, ZIP_ST_RENAMED };
-
 /* error source for layered sources */
 
 enum zip_les { ZIP_LES_NONE, ZIP_LES_UPPER, ZIP_LES_LOWER, ZIP_LES_INVAL };
@@ -196,12 +191,21 @@
 #define ZIP_GPBF_STRONG_ENCRYPTION	0x0040  /* uses strong encryption */
 #define ZIP_GPBF_ENCODING_UTF_8		0x0800  /* file name encoding is UTF-8 */
 
+
+/* extra fields */
+#define ZIP_EF_LOCAL		ZIP_FL_LOCAL			/* include in local header */
+#define ZIP_EF_CENTRAL		ZIP_FL_CENTRAL			/* include in central directory */
+#define ZIP_EF_BOTH		(ZIP_EF_LOCAL|ZIP_EF_CENTRAL)	/* include in both */
+
+
 /* encoding type */
 enum zip_encoding_type {
-    ZIP_ENCODING_UNKNOWN, /* not yet analyzed */
-    ZIP_ENCODING_ASCII,   /* plain ASCII */
-    ZIP_ENCODING_UTF8,    /* possibly UTF-8 */
-    ZIP_ENCODING_CP437    /* Code Page 437 */
+    ZIP_ENCODING_UNKNOWN,       /* not yet analyzed */
+    ZIP_ENCODING_ASCII,         /* plain ASCII */
+    ZIP_ENCODING_UTF8_KNOWN,    /* is UTF-8 */
+    ZIP_ENCODING_UTF8_GUESSED,  /* possibly UTF-8 */
+    ZIP_ENCODING_CP437,         /* Code Page 437 */
+    ZIP_ENCODING_ERROR          /* should be UTF-8 but isn't */
 };
 
 /* error information */
@@ -215,26 +219,27 @@
 /* zip archive, part of API */
 
 struct zip {
-    char *zn;			/* file name */
-    FILE *zp;			/* file */
-    unsigned int open_flags;	/* flags passed to zip_open */
-    struct zip_error error;	/* error information */
+    char *zn;				/* file name */
+    FILE *zp;				/* file */
+    unsigned int open_flags;		/* flags passed to zip_open */
+    struct zip_error error;		/* error information */
 
-    unsigned int flags;		/* archive global flags */
-    unsigned int ch_flags;	/* changed archive global flags */
+    unsigned int flags;			/* archive global flags */
+    unsigned int ch_flags;		/* changed archive global flags */
 
-    char *default_password;	/* password used when no other supplied */
+    char *default_password;		/* password used when no other supplied */
 
-    struct zip_cdir *cdir;	/* central directory */
-    char *ch_comment;		/* changed archive comment */
-    zip_int32_t ch_comment_len;	/* length of changed zip archive
-				 * comment, -1 if unchanged */
-    zip_uint64_t nentry;	/* number of entries */
-    zip_uint64_t nentry_alloc;	/* number of entries allocated */
-    struct zip_entry *entry;	/* entries */
-    unsigned int nfile;		/* number of opened files within archive */
-    unsigned int nfile_alloc;	/* number of files allocated */
-    struct zip_file **file;	/* opened files within archive */
+    struct zip_string *comment_orig;	/* archive comment */
+    struct zip_string *comment_changes; /* changed archive comment */
+    int comment_changed;		/* whether archive comment was changed */
+
+    zip_uint64_t nentry;		/* number of entries */
+    zip_uint64_t nentry_alloc;		/* number of entries allocated */
+    struct zip_entry *entry;		/* entries */
+
+    unsigned int nfile;			/* number of opened files within archive */
+    unsigned int nfile_alloc;		/* number of files allocated */
+    struct zip_file **file;		/* opened files within archive */
 };
 
 /* file in zip archive, part of API */
@@ -251,59 +256,48 @@
 #define ZIP_DIRENT_COMP_METHOD	0x0001
 #define ZIP_DIRENT_FILENAME	0x0002
 #define ZIP_DIRENT_COMMENT	0x0004
-#define ZIP_DIRENT_EXTRAFIELD	0x0008
+#define ZIP_DIRENT_EXTRA_FIELD	0x0008
 #define ZIP_DIRENT_ALL		0xffff
 
-struct zip_dirent_settable {
-    zip_uint32_t valid;
-    zip_int32_t comp_method;		/* (cl) compression method used (uint16 and ZIP_CM_DEFAULT (-1)) */
-    char *filename;			/* (cl) file name (NUL-terminated) */
-    char *comment;			/* (c)  file comment */
-    zip_uint16_t comment_len;		/* (c)  length of file comment */
-    char *extrafield;			/*  (l) extra field local header */
-    zip_uint16_t extrafield_len;	/*  (l) length of extra field from local header*/
-#if 0
-    char *cdir_extrafield;		/* (c)  extra field from cdir */
-    zip_uint16_t cdir_extrafield_len;	/* (c)  length of extra field from cdir*/
-    struct zip_extrafield *extra_parsed;	/*      parsed extra fields */
-    zip_uint16_t nextra_parsed;		/*      number of parsed extra fields */
-    zip_uint16_t nextra_parsed_alloc;   /*      number of parsed extra fields allocated */
-#endif
-};
-
 struct zip_dirent {
-    zip_uint16_t version_madeby;	/* (c)  version of creator */
-    zip_uint16_t version_needed;	/* (cl) version needed to extract */
-    zip_uint16_t bitflags;		/* (cl) general purpose bit flag */
-    time_t last_mod;			/* (cl) time of last modification */
-    zip_uint32_t crc;			/* (cl) CRC-32 of uncompressed data */
-    zip_uint64_t comp_size;		/* (cl) size of compressed data */
-    zip_uint64_t uncomp_size;		/* (cl) size of uncompressed data */
-    zip_uint32_t disk_number;		/* (c)  disk number start */
-    zip_uint16_t int_attrib;		/* (c)  internal file attributes */
-    zip_uint32_t ext_attrib;		/* (c)  external file attributes */
-    zip_uint64_t offset;		/* (c)  offset of local header */
-    enum zip_encoding_type fn_type;	/*      file name encoding (autorecognition) */
-    char *filename_converted;		/*      file name (autoconverted) */
-    enum zip_encoding_type fc_type;	/*      file comment encoding (autorecognition) */
-    char *comment_converted;		/*      file comment (autoconverted) */
-    zip_uint32_t comment_converted_len;	/*      file comment length (autoconverted) */
-    struct zip_dirent_settable settable;
+    zip_uint32_t changed;
+    int local_extra_fields_read;		/*      whether we already read in local header extra fields */
+
+    zip_uint16_t version_madeby;		/* (c)  version of creator */
+    zip_uint16_t version_needed;		/* (cl) version needed to extract */
+    zip_uint16_t bitflags;			/* (cl) general purpose bit flag */
+    zip_int32_t comp_method;			/* (cl) compression method used (uint16 and ZIP_CM_DEFAULT (-1)) */
+    time_t last_mod;				/* (cl) time of last modification */
+    zip_uint32_t crc;				/* (cl) CRC-32 of uncompressed data */
+    zip_uint64_t comp_size;			/* (cl) size of compressed data */
+    zip_uint64_t uncomp_size;			/* (cl) size of uncompressed data */
+    struct zip_string *filename;		/* (cl) file name (NUL-terminated) */
+    struct zip_extra_field *extra_fields;	/* (cl) extra fields, parsed */
+    struct zip_string *comment;			/* (c)  file comment */
+    zip_uint32_t disk_number;			/* (c)  disk number start */
+    zip_uint16_t int_attrib;			/* (c)  internal file attributes */
+    zip_uint32_t ext_attrib;			/* (c)  external file attributes */
+    zip_uint64_t offset;			/* (c)  offset of local header */
 };
 
 /* zip archive central directory */
 
 struct zip_cdir {
-    struct zip_dirent *entry;	 		/* directory entries */
+    struct zip_entry *entry;	 		/* directory entries */
     unsigned int nentry;			/* number of entries */
+    unsigned int nentry_alloc;			/* number of entries allocated */
 
-    zip_uint64_t size;		 		/* size of central direcotry */
+    zip_uint64_t size;		 		/* size of central directory */
     zip_uint64_t offset;	 		/* offset of central directory in file */
-    char *comment;		 		/* zip archive comment */
-    zip_uint16_t comment_len;	 		/* length of zip archive comment */
-    enum zip_encoding_type comment_type;	/* archive comment encoding (autorecognition) */
-    char *comment_converted;     		/* archive comment (autoconverted) */
-    zip_uint32_t comment_converted_len;		/* archive comment length (autoconverted) */
+    struct zip_string *comment;			/* zip archive comment */
+};
+
+struct zip_extra_field {
+    struct zip_extra_field *next;
+    zip_uint16_t flags;				/* in local/central header */
+    zip_uint16_t id;				/* header id */
+    zip_uint16_t size;				/* data size */
+    zip_uint8_t *data;
 };
 
 
@@ -322,9 +316,31 @@
 /* entry in zip archive directory */
 
 struct zip_entry {
-    enum zip_state state;
+    struct zip_dirent *orig;
+    struct zip_dirent *changes;
     struct zip_source *source;
-    struct zip_dirent_settable changes;
+    int deleted;
+};
+
+
+
+/* file or archive comment, or filename */
+
+struct zip_string {
+    zip_uint8_t *raw;			/* raw string */
+    zip_uint16_t length;		/* length of raw string */
+    unsigned short encoding; 		/* autorecognized encoding */
+    zip_uint8_t *converted;     	/* autoconverted string */
+    zip_uint32_t converted_length;	/* length of converted */
+};
+
+
+
+/* which files to write, and in which order (name is for torrentzip sorting) */
+
+struct zip_filelist {
+    int idx;
+    const char *name;
 };
 
 
@@ -335,30 +351,43 @@
 
 
 
-#define ZIP_ENTRY_DATA_CHANGED(x)	\
-			((x)->state == ZIP_ST_REPLACED  \
-			 || (x)->state == ZIP_ST_ADDED)
+#define ZIP_ENTRY_CHANGED(e, f)	((e)->changes && ((e)->changes->changed & (f)))
+
+#define ZIP_ENTRY_DATA_CHANGED(x)	((x)->source != NULL)
 
 #define ZIP_IS_RDONLY(za)	((za)->ch_flags & ZIP_AFL_RDONLY)
 
 
 
+zip_int64_t _zip_add_entry(struct zip *);
+
 int _zip_cdir_compute_crc(struct zip *, uLong *);
 void _zip_cdir_free(struct zip_cdir *);
 int _zip_cdir_grow(struct zip_cdir *, int, struct zip_error *);
 struct zip_cdir *_zip_cdir_new(int, struct zip_error *);
-int _zip_cdir_write(struct zip_cdir *, FILE *, struct zip_error *);
+zip_int64_t _zip_cdir_write(struct zip *, const struct zip_filelist *, int, FILE *);
 
+struct zip_dirent *_zip_dirent_clone(const struct zip_dirent *);
+void _zip_dirent_free(struct zip_dirent *);
 void _zip_dirent_finalize(struct zip_dirent *);
 void _zip_dirent_init(struct zip_dirent *);
+struct zip_dirent *_zip_dirent_new(void);
 int _zip_dirent_read(struct zip_dirent *, FILE *, const unsigned char **,
 		     zip_uint32_t *, int, struct zip_error *);
+zip_int32_t _zip_dirent_size(FILE *, zip_uint16_t, struct zip_error *);
 void _zip_dirent_torrent_normalize(struct zip_dirent *);
 int _zip_dirent_write(struct zip_dirent *, FILE *, int, struct zip_error *);
 
-void _zip_entry_free(struct zip_entry *);
-void _zip_entry_init(struct zip *, int);
-struct zip_entry *_zip_entry_new(struct zip *);
+void _zip_ef_free(struct zip_extra_field *);
+const zip_uint8_t *_zip_ef_get_by_id(struct zip_extra_field *, zip_uint16_t *, zip_uint16_t, zip_uint16_t, zip_uint16_t, struct zip_error *);
+struct zip_extra_field *_zip_ef_merge(struct zip_extra_field *, struct zip_extra_field *);
+struct zip_extra_field *_zip_ef_new(zip_uint16_t, zip_uint16_t, const zip_uint8_t *, zip_uint16_t);
+struct zip_extra_field *_zip_ef_parse(const zip_uint8_t *, zip_uint16_t, zip_uint16_t, struct zip_error *);
+zip_uint16_t _zip_ef_size(struct zip_extra_field *, zip_uint16_t);
+void _zip_ef_write(struct zip_extra_field *, zip_uint16_t, FILE *);
+
+void _zip_entry_finalize(struct zip_entry *);
+void _zip_entry_init(struct zip_entry *);
 
 void _zip_error_clear(struct zip_error *);
 void _zip_error_copy(struct zip_error *, struct zip_error *);
@@ -376,12 +405,16 @@
 
 int _zip_filerange_crc(FILE *, off_t, off_t, uLong *, struct zip_error *);
 
-enum zip_encoding_type _zip_guess_encoding(const char * const, zip_uint32_t);
-char *_zip_cp437_to_utf8(const char * const, zip_uint32_t,
+struct zip_dirent *_zip_get_dirent(struct zip *, zip_uint64_t, int, struct zip_error *);
+
+enum zip_encoding_type _zip_guess_encoding(struct zip_string *, enum zip_encoding_type);
+zip_uint8_t *_zip_cp437_to_utf8(const zip_uint8_t * const, zip_uint32_t,
 				zip_uint32_t *, struct zip_error *error);
 
 struct zip *_zip_open(const char *, FILE *, int, int, int *);
 
+int _zip_read_local_ef(struct zip *, int);
+
 struct zip_source *_zip_source_file_or_p(struct zip *, const char *, FILE *,
 					 zip_uint64_t, zip_int64_t, int,
 					 const struct zip_stat *);
@@ -389,6 +422,13 @@
 struct zip_source *_zip_source_zip_new(struct zip *, struct zip *, zip_uint64_t, int,
 				       zip_uint64_t, zip_int64_t, const char *);
 
+int _zip_string_equal(const struct zip_string *, const struct zip_string *);
+void _zip_string_free(struct zip_string *);
+const zip_uint8_t *_zip_string_get(struct zip_string *, zip_uint32_t *, int, struct zip_error *);
+zip_uint16_t _zip_string_length(const struct zip_string *);
+struct zip_string *_zip_string_new(const zip_uint8_t *, zip_uint16_t, struct zip_error *);
+void _zip_string_write(const struct zip_string *, FILE *);
+
 int _zip_changed(struct zip *, int *);
 const char *_zip_get_name(struct zip *, zip_uint64_t, int, struct zip_error *);
 int _zip_local_header_read(struct zip *, int);
@@ -398,6 +438,7 @@
 zip_uint16_t _zip_read2(const unsigned char **);
 zip_uint32_t _zip_read4(const unsigned char **);
 zip_uint64_t _zip_read8(const unsigned char **);
+zip_uint8_t *_zip_read_data(const unsigned char **, FILE *, int, int, struct zip_error *);
 zip_int64_t _zip_replace(struct zip *, zip_uint64_t, const char *,
 			 struct zip_source *);
 int _zip_set_name(struct zip *, zip_uint64_t, const char *);
@@ -405,4 +446,8 @@
 int _zip_unchange(struct zip *, zip_uint64_t, int);
 void _zip_unchange_data(struct zip_entry *);
 
+void _zip_write2(zip_uint16_t, FILE *);
+void _zip_write4(zip_uint32_t, FILE *);
+
+
 #endif /* zipint.h */