diff options
-rw-r--r-- | src/textfile_gui.cpp | 77 |
1 files changed, 76 insertions, 1 deletions
diff --git a/src/textfile_gui.cpp b/src/textfile_gui.cpp index 46bbc009a..0b9814f67 100644 --- a/src/textfile_gui.cpp +++ b/src/textfile_gui.cpp @@ -21,6 +21,10 @@ #include "table/strings.h" +#if defined(WITH_ZLIB) +#include <zlib.h> +#endif + #include "safeguards.h" /** Widgets for the textfile window. */ @@ -193,6 +197,62 @@ void TextfileWindow::SetupScrollbars() #endif /* WITH_FREETYPE */ } +#if defined(WITH_ZLIB) + +/** + * Do an in-memory gunzip operation. This works on a raw deflate stream, + * or a file with gzip or zlib header. + * @param bufp A pointer to a buffer containing the input data. This + * buffer will be freed and replaced by a buffer containing + * the uncompressed data. + * @param sizep A pointer to the buffer size. Before the call, the value + * pointed to should contain the size of the input buffer. + * After the call, it contains the size of the uncompressed + * data. + * + * When decompressing fails, *bufp is set to NULL and *sizep to 0. The + * compressed buffer passed in is still freed in this case. + */ +static void Gunzip(byte **bufp, size_t *sizep) +{ + static const int BLOCKSIZE = 8192; + byte *buf = NULL; + size_t alloc_size = 0; + z_stream z; + int res; + + memset(&z, 0, sizeof(z)); + z.next_in = *bufp; + z.avail_in = *sizep; + + /* window size = 15, add 32 to enable gzip or zlib header processing */ + res = inflateInit2(&z, 15 + 32); + /* Z_BUF_ERROR just means we need more space */ + while (res == Z_OK || (res == Z_BUF_ERROR && z.avail_out == 0)) { + /* When we get here, we're either just starting, or + * inflate is out of output space - allocate more */ + alloc_size += BLOCKSIZE; + z.avail_out += BLOCKSIZE; + buf = ReallocT(buf, alloc_size); + z.next_out = buf + alloc_size - z.avail_out; + res = inflate(&z, Z_FINISH); + } + + free(*bufp); + inflateEnd(&z); + + if (res == Z_STREAM_END) { + *bufp = buf; + *sizep = alloc_size - z.avail_out; + } else { + /* Something went wrong */ + *bufp = NULL; + *sizep = 0; + free(buf); + } +} +#endif + /** * Loads the textfile text from file and setup #lines. */ @@ -207,12 +267,24 @@ void TextfileWindow::SetupScrollbars() FILE *handle = FioFOpenFile(textfile, "rb", dir, &filesize); if (handle == NULL) return; - this->text = ReallocT(this->text, filesize + 1); + this->text = ReallocT(this->text, filesize); size_t read = fread(this->text, 1, filesize, handle); fclose(handle); if (read != filesize) return; +#if defined(WITH_ZLIB) + const char *suffix = strrchr(textfile, '.'); + if (suffix == NULL) return; + + /* In-place gunzip */ + if (strcmp(suffix, ".gz") == 0) Gunzip((byte**)&this->text, &filesize); +#endif + + if (!this->text) return; + + /* Add space for trailing \0 */ + this->text = ReallocT(this->text, filesize + 1); this->text[filesize] = '\0'; /* Replace tabs and line feeds with a space since str_validate removes those. */ @@ -266,6 +338,9 @@ const char *GetTextfile(TextfileType type, Subdirectory dir, const char *filenam static const char * const exts[] = { "txt", +#if defined(WITH_ZLIB) + "txt.gz", +#endif }; for (size_t i = 0; i < lengthof(exts); i++) { |