diff --git a/docs/changelog.txt b/docs/changelog.txt index 9e3060bb7..196ce3d5b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -226,6 +226,9 @@ USERS + Robotic debugger watchpoints can now watch for particular counter or string values. Leaving the value field blank will still check for any value. ++ The NDS, PSP, and DOS ports now write zip archives with the + fastest zlib compression level. This makes saved files that + use compression (worlds, saves, etc.) slightly larger. + editor_show_thing_toggles is now enabled by default. - board_editor_hide_help and robot_editor_hide_help are no longer enabled by default for accessibility. diff --git a/src/io/zip.c b/src/io/zip.c index f1f7a522b..15e978ef0 100644 --- a/src/io/zip.c +++ b/src/io/zip.c @@ -48,11 +48,19 @@ // The 3DS has incredibly slow file access that seems to be negatively impacted // by backwards seeks in particular, so enable data descriptors for it too. // The Switch may similarly benefit with this. +// DJGPP may be running on exceptionally slow hardware. -#if defined(CONFIG_NDS) || defined(CONFIG_3DS) || defined(CONFIG_SWITCH) +#if defined(CONFIG_NDS) || defined(CONFIG_3DS) || defined(CONFIG_SWITCH) || \ + defined(CONFIG_DJGPP) #define ZIP_WRITE_DATA_DESCRIPTOR #endif +// Similarly, some platforms are slow and need faster compression. +// TODO: these should probably be runtime configurable instead. +#if defined(CONFIG_NDS) || defined(CONFIG_PSP) || defined(CONFIG_DJGPP) +#define ZIP_WRITE_DEFLATE_FAST +#endif + #define ZIP_VERSION_MINIMUM 20 #define ZIP64_VERSION_MINIMUM 45 #define ZIP_VERSION(x) ((x) & 0x00ff) @@ -2086,10 +2094,13 @@ static enum zip_error zip_write_open_stream(struct zip_archive *zp, return ZIP_ALLOC_ERROR; // Set up the header -#ifdef ZIP_WRITE_DATA_DESCRIPTOR - fh->flags = ZIP_F_DATA_DESCRIPTOR; -#else fh->flags = 0; +#ifdef ZIP_WRITE_DATA_DESCRIPTOR + fh->flags |= ZIP_F_DATA_DESCRIPTOR; +#endif +#ifdef ZIP_WRITE_DEFLATE_FAST + if(method == ZIP_M_DEFLATE) + fh->flags |= ZIP_F_DEFLATE_FAST; #endif fh->method = method; fh->crc32 = 0; @@ -2124,7 +2135,7 @@ static enum zip_error zip_write_open_stream(struct zip_archive *zp, if(zp->stream) { struct zip_stream_data *stream_data = zp->stream_data; - zp->stream->compress_open(stream_data, method, false); + zp->stream->compress_open(stream_data, method, fh->flags); result = zip_set_stream_buffer_size(zp, ZIP_STREAM_BUFFER_SIZE); if(result != ZIP_SUCCESS) @@ -2165,6 +2176,16 @@ static enum zip_error zip_write_autodetect_zip64(struct zip_archive *zp, if(zp->stream != NULL && zp->stream->compress_bound != NULL) { size_t bound_len = 0; + int flags = 0; + + // Bounding requires initializing the zstream, so initialize first + // using the correct compression level. +#ifdef ZIP_WRITE_DEFLATE_FAST + if(method == ZIP_M_DEFLATE) + flags |= ZIP_F_DEFLATE_FAST; +#endif + + zp->stream->compress_open(zp->stream_data, method, flags); result = zp->stream->compress_bound(zp->stream_data, src_len, &bound_len); if(result) return result; diff --git a/src/io/zip.h b/src/io/zip.h index 9bc8bf939..b04fe8377 100644 --- a/src/io/zip.h +++ b/src/io/zip.h @@ -90,6 +90,10 @@ enum zip_general_purpose_flag #define ZIP_F_UNUSED (ZIP_F_UNUSED_7 | ZIP_F_UNUSED_8 | ZIP_F_UNUSED_9 |\ ZIP_F_UNUSED_10 | ZIP_F_UNUSED_12 | ZIP_F_UNUSED_14 | ZIP_F_UNUSED_15) +// DEFLATE-specific compression flags. +#define ZIP_F_DEFLATE_MAXIMUM ZIP_F_COMPRESSION_1 +#define ZIP_F_DEFLATE_FAST ZIP_F_COMPRESSION_2 + enum zip_internal_state { ZIP_S_READ_UNINITIALIZED, diff --git a/src/io/zip_deflate.h b/src/io/zip_deflate.h index d7474bdbf..0a9fb001e 100644 --- a/src/io/zip_deflate.h +++ b/src/io/zip_deflate.h @@ -41,6 +41,7 @@ struct deflate_stream_data boolean is_inflate; boolean is_deflate; boolean should_finish; + uint16_t flags; }; static inline struct zip_stream_data *deflate_create(void) @@ -72,10 +73,18 @@ static inline void deflate_open(struct zip_stream_data *zs, uint16_t method, uint16_t flags) { struct deflate_stream_data *ds = ((struct deflate_stream_data *)zs); + int prev_flags = ds->flags; // Only clear the common portion of the stream data. memset(zs, 0, sizeof(struct zip_stream_data)); zs->is_compression_stream = true; ds->should_finish = false; + ds->flags = flags & (ZIP_F_DEFLATE_MAXIMUM | ZIP_F_DEFLATE_FAST); + + if(ds->is_deflate && prev_flags != ds->flags) + { + deflateEnd(&(ds->z)); + ds->is_deflate = false; + } } static inline void deflate_close(struct zip_stream_data *zs, @@ -199,9 +208,15 @@ static inline enum zip_error deflate_init(struct zip_stream_data *zs) if(!ds->is_deflate) { + int level = Z_DEFAULT_COMPRESSION; + if(ds->flags & ZIP_F_DEFLATE_MAXIMUM) + level = Z_BEST_COMPRESSION; + if(ds->flags & ZIP_F_DEFLATE_FAST) + level = Z_BEST_SPEED; + // This is a raw deflate stream, so use -MAX_WBITS. - // Note: aside from the windowbits, these are all defaults. - deflateInit2(&(ds->z), Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, + // Note: aside from the compression and windowbits, these are all defaults. + deflateInit2(&(ds->z), level, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); ds->is_deflate = true; }