From 8b57424bcfabf8a7337762b58c04e859d1ea30e9 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Fri, 7 Jul 2017 11:30:31 +0200 Subject: [PATCH] + evince-3.24.0-2 Use libarchive to handle compressed comics documents Resolves: #1468488 --- ...-decoding-some-files-in-RAR-archives.patch | 45 + evince-libarchive-gnome-3-24.patch | 11396 ++++++++++++++++ evince.spec | 11 +- 3 files changed, 11451 insertions(+), 1 deletion(-) create mode 100644 0001-comics-Fix-decoding-some-files-in-RAR-archives.patch create mode 100644 evince-libarchive-gnome-3-24.patch diff --git a/0001-comics-Fix-decoding-some-files-in-RAR-archives.patch b/0001-comics-Fix-decoding-some-files-in-RAR-archives.patch new file mode 100644 index 0000000..145bc3e --- /dev/null +++ b/0001-comics-Fix-decoding-some-files-in-RAR-archives.patch @@ -0,0 +1,45 @@ +From 499cd2025bc21ca725915f0e0b618bf8df2a0596 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 12 Jul 2017 12:53:19 +0200 +Subject: [PATCH] comics: Fix decoding some files in RAR archives + +The unarr RAR decoder doesn't like it when we request more data than is +available: +! rar.c:169: Requesting too much data (3563 < 10240) + +Clamp the size of the read request to the data left to read. + +https://bugzilla.gnome.org/show_bug.cgi?id=784842 +--- + backend/comics/comics-document.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c +index ee060091..a913641d 100644 +--- a/backend/comics/comics-document.c ++++ b/backend/comics/comics-document.c +@@ -318,14 +318,19 @@ comics_document_get_page_size (EvDocument *document, + if (g_strcmp0 (name, page_path) == 0) { + char buf[BLOCK_SIZE]; + gssize read; ++ gint64 left; + +- read = ev_archive_read_data (comics_document->archive, buf, sizeof(buf), &error); ++ left = ev_archive_get_entry_size (comics_document->archive); ++ read = ev_archive_read_data (comics_document->archive, buf, ++ MIN(BLOCK_SIZE, left), &error); + while (read > 0 && !info.got_info) { + if (!gdk_pixbuf_loader_write (loader, (guchar *) buf, read, &error)) { + read = -1; + break; + } +- read = ev_archive_read_data (comics_document->archive, buf, BLOCK_SIZE, &error); ++ left -= read; ++ read = ev_archive_read_data (comics_document->archive, buf, ++ MIN(BLOCK_SIZE, left), &error); + } + if (read < 0) { + g_warning ("Fatal error reading '%s' in archive: %s", name, error->message); +-- +2.13.0 + diff --git a/evince-libarchive-gnome-3-24.patch b/evince-libarchive-gnome-3-24.patch new file mode 100644 index 0000000..aa7dbbd --- /dev/null +++ b/evince-libarchive-gnome-3-24.patch @@ -0,0 +1,11396 @@ +From d1b02669443d3b18a224f3d63710c17a0553bdad Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Thu, 29 Jan 2015 16:54:21 +0100 +Subject: [PATCH 1/8] comics: Port to using libarchive for unarchiving + +v8: +- Fix double-free in error path when decompressing images + +v7: +- Bump buffer size in ev-archive, good performance increase for + local files + +v6: +- Fix 2 pretty big memory leaks +- Remove unneeded archive_read_data_skip() calls +- Optimise the "no rotation" case (could also be done for gnome-3-24) +- Use render_pixbuf_size_prepared_cb() +- Add debug to "next header" archive calls + +v5: +- Remove unused members of ComicsDocument struct +- Split archive handling into an EvArchive object +- Fix copy/paste error in configure.ac + +v4: +- Fix crash caused by a bug in comics_document_list() + (the array was not NULL terminated) +- Remove duplicate "!cb_files" check +- Use "size-prepared" instead of "area-prepared" to get the doc size +- Fix link to libarchive bug in code, not working yet :/ + +v3: +- Rebase against latest evince, making sure to bring back: + - Use Unicode in translatable strings + - Sort pages in natural order + - Fix mime-type comparisons +- https://github.com/libarchive/libarchive/issues/373 looks + like it's fixed! + +v2: +- Rebase against latest evince +- Update libarchive bug URL + +https://bugzilla.gnome.org/show_bug.cgi?id=720742 +--- + backend/comics/Makefile.am | 6 +- + backend/comics/comics-document.c | 841 ++++++++++----------------------------- + backend/comics/ev-archive.c | 277 +++++++++++++ + backend/comics/ev-archive.h | 47 +++ + configure.ac | 12 +- + 5 files changed, 560 insertions(+), 623 deletions(-) + create mode 100644 backend/comics/ev-archive.c + create mode 100644 backend/comics/ev-archive.h + +diff --git a/backend/comics/Makefile.am b/backend/comics/Makefile.am +index b047ad36..856f469c 100644 +--- a/backend/comics/Makefile.am ++++ b/backend/comics/Makefile.am +@@ -2,7 +2,9 @@ backend_LTLIBRARIES = libcomicsdocument.la + + libcomicsdocument_la_SOURCES = \ + comics-document.c \ +- comics-document.h ++ comics-document.h \ ++ ev-archive.c \ ++ ev-archive.h + + libcomicsdocument_la_CPPFLAGS = \ + -I$(top_srcdir) \ +@@ -13,12 +15,14 @@ libcomicsdocument_la_CPPFLAGS = \ + + libcomicsdocument_la_CFLAGS = \ + $(BACKEND_CFLAGS) \ ++ $(LIBARCHIVE_CFLAGS) \ + $(LIB_CFLAGS) \ + $(AM_CFLAGS) + + libcomicsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) + libcomicsdocument_la_LIBADD = \ + $(top_builddir)/libdocument/libevdocument3.la \ ++ $(LIBARCHIVE_LIBS) \ + $(BACKEND_LIBS) \ + $(LIB_LIBS) + +diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c +index 4c747310..b55c25b3 100644 +--- a/backend/comics/comics-document.c ++++ b/backend/comics/comics-document.c +@@ -2,6 +2,7 @@ + /* + * Copyright (C) 2009-2010 Juanjo Marín + * Copyright (C) 2005, Teemu Tervo ++ * Copyright (C) 2016-2017, Bastien Nocera + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -30,35 +31,12 @@ + #include + #include + +-#ifdef G_OS_WIN32 +-# define WIFEXITED(x) ((x) != 3) +-# define WEXITSTATUS(x) (x) +-#else +-# include +-#endif +- + #include "comics-document.h" + #include "ev-document-misc.h" + #include "ev-file-helpers.h" ++#include "ev-archive.h" + +-#ifdef G_OS_WIN32 +-/* On windows g_spawn_command_line_sync reads stdout in O_BINARY mode, not in O_TEXT mode. +- * As a consequence, newlines are in a platform dependent representation (\r\n). This +- * might be considered a bug in glib. +- */ +-#define EV_EOL "\r\n" +-#else +-#define EV_EOL "\n" +-#endif +- +-typedef enum +-{ +- RARLABS, +- GNAUNRAR, +- UNZIP, +- P7ZIP, +- TAR +-} ComicBookDecompressType; ++#define BLOCK_SIZE 10240 + + typedef struct _ComicsDocumentClass ComicsDocumentClass; + +@@ -69,383 +47,98 @@ struct _ComicsDocumentClass + + struct _ComicsDocument + { +- EvDocument parent_instance; +- +- gchar *archive, *dir; +- GPtrArray *page_names; +- gchar *selected_command, *alternative_command; +- gchar *extract_command, *list_command, *decompress_tmp; +- gboolean regex_arg; +- gint offset; +- ComicBookDecompressType command_usage; +-}; +- +-#define OFFSET_7Z 53 +-#define OFFSET_ZIP 2 +-#define NO_OFFSET 0 +- +-/* For perfomance reasons of 7z* we've choosen to decompress on the temporary +- * directory instead of decompressing on the stdout */ +- +-/** +- * @extract: command line arguments to pass to extract a file from the archive +- * to stdout. +- * @list: command line arguments to list the archive contents +- * @decompress_tmp: command line arguments to pass to extract the archive +- * into a directory. +- * @regex_arg: whether the command can accept regex expressions +- * @offset: the position offset of the filename on each line in the output of +- * running the @list command +- */ +-typedef struct { +- char *extract; +- char *list; +- char *decompress_tmp; +- gboolean regex_arg; +- gint offset; +-} ComicBookDecompressCommand; +- +-static const ComicBookDecompressCommand command_usage_def[] = { +- /* RARLABS unrar */ +- {"%s p -c- -ierr --", "%s vb -c- -- %s", NULL , FALSE, NO_OFFSET}, +- +- /* GNA! unrar */ +- {NULL , "%s t %s" , "%s -xf %s %s" , FALSE, NO_OFFSET}, +- +- /* unzip */ +- {"%s -p -C --" , "%s %s" , NULL , TRUE , OFFSET_ZIP}, +- +- /* 7zip */ +- {NULL , "%s l -- %s" , "%s x -y %s -o%s", FALSE, OFFSET_7Z}, +- +- /* tar */ +- {"%s -xOf" , "%s -tf %s" , NULL , FALSE, NO_OFFSET} ++ EvDocument parent_instance; ++ EvArchive *a; ++ EvArchiveType a_type; ++ gchar *archive_path; ++ gchar *archive_uri; ++ GPtrArray *page_names; + }; + + static GSList* get_supported_image_extensions (void); +-static void get_page_size_area_prepared_cb (GdkPixbufLoader *loader, +- gpointer data); ++static void get_page_size_prepared_cb (GdkPixbufLoader *loader, ++ int width, ++ int height, ++ gpointer data); + static void render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader, + gint width, + gint height, + EvRenderContext *rc); +-static char** extract_argv (EvDocument *document, +- gint page); +- + + EV_BACKEND_REGISTER (ComicsDocument, comics_document) + +-/** +- * comics_regex_quote: +- * @unquoted_string: a literal string +- * +- * Quotes a string so unzip will not interpret the regex expressions of +- * @unquoted_string. Basically, this functions uses [] to disable regex +- * expressions. The return value must be freed with * g_free() +- * +- * Return value: quoted and disabled-regex string +- **/ +-static gchar * +-comics_regex_quote (const gchar *unquoted_string) ++static void ++comics_document_reset_archive (ComicsDocument *comics_document) + { +- const gchar *p; +- GString *dest; +- +- dest = g_string_new ("'"); +- +- p = unquoted_string; +- +- while (*p) { +- switch (*p) { +- /* * matches a sequence of 0 or more characters */ +- case ('*'): +- /* ? matches exactly 1 charactere */ +- case ('?'): +- /* [...] matches any single character found inside +- * the brackets. Disabling the first bracket is enough. +- */ +- case ('['): +- g_string_append (dest, "["); +- g_string_append_c (dest, *p); +- g_string_append (dest, "]"); +- break; +- /* Because \ escapes regex expressions that we are +- * disabling for unzip, we need to disable \ too */ +- case ('\\'): +- g_string_append (dest, "[\\\\]"); +- break; +- /* Escape single quote inside the string */ +- case ('\''): +- g_string_append (dest, "'\\''"); +- break; +- default: +- g_string_append_c (dest, *p); +- break; +- } +- ++p; +- } +- g_string_append_c (dest, '\''); +- return g_string_free (dest, FALSE); ++ g_clear_object (&comics_document->a); ++ comics_document->a = ev_archive_new (); ++ ev_archive_set_archive_type (comics_document->a, comics_document->a_type); + } + +- +-/* This function manages the command for decompressing a comic book */ +-static gboolean +-comics_decompress_temp_dir (const gchar *command_decompress_tmp, +- const gchar *command, +- GError **error) ++static char ** ++comics_document_list (ComicsDocument *comics_document) + { +- gboolean success; +- gchar *std_out, *basename; +- GError *err = NULL; +- gint retval; +- +- success = g_spawn_command_line_sync (command_decompress_tmp, &std_out, +- NULL, &retval, &err); +- basename = g_path_get_basename (command); +- if (!success) { +- g_set_error (error, +- EV_DOCUMENT_ERROR, +- EV_DOCUMENT_ERROR_INVALID, +- _("Error launching the command “%s” in order to " +- "decompress the comic book: %s"), +- basename, +- err->message); +- g_error_free (err); +- } else if (WIFEXITED (retval)) { +- if (WEXITSTATUS (retval) == EXIT_SUCCESS) { +- g_free (std_out); +- g_free (basename); +- return TRUE; +- } else { +- g_set_error (error, +- EV_DOCUMENT_ERROR, +- EV_DOCUMENT_ERROR_INVALID, +- _("The command “%s” failed at " +- "decompressing the comic book."), +- basename); +- g_free (std_out); ++ char **ret = NULL; ++ GPtrArray *array; ++ ++ if (!ev_archive_open_filename (comics_document->a, comics_document->archive_path, NULL)) ++ goto out; ++ ++ array = g_ptr_array_new (); ++ ++ while (1) { ++ const char *name; ++ GError *error = NULL; ++ ++ if (!ev_archive_read_next_header (comics_document->a, &error)) { ++ if (error != NULL) { ++ g_warning ("Fatal error handling archive: %s", error->message); ++ g_error_free (error); ++ } ++ break; + } +- } else { +- g_set_error (error, +- EV_DOCUMENT_ERROR, +- EV_DOCUMENT_ERROR_INVALID, +- _("The command “%s” did not end normally."), +- basename); +- g_free (std_out); +- } +- g_free (basename); +- return FALSE; +-} + +-/* This function shows how to use the chosen command for decompressing a +- * comic book file. It modifies fields of the ComicsDocument struct with +- * this information */ +-#pragma GCC diagnostic push +-#pragma GCC diagnostic ignored "-Wformat-nonliteral" +-static gboolean +-comics_generate_command_lines (ComicsDocument *comics_document, +- GError **error) +-{ +- gchar *quoted_file, *quoted_file_aux; +- gchar *quoted_command; +- ComicBookDecompressType type; +- +- type = comics_document->command_usage; +- comics_document->regex_arg = command_usage_def[type].regex_arg; +- quoted_command = g_shell_quote (comics_document->selected_command); +- if (comics_document->regex_arg) { +- quoted_file = comics_regex_quote (comics_document->archive); +- quoted_file_aux = g_shell_quote (comics_document->archive); +- comics_document->list_command = +- g_strdup_printf (command_usage_def[type].list, +- comics_document->alternative_command, +- quoted_file_aux); +- g_free (quoted_file_aux); +- } else { +- quoted_file = g_shell_quote (comics_document->archive); +- comics_document->list_command = +- g_strdup_printf (command_usage_def[type].list, +- quoted_command, quoted_file); ++ name = ev_archive_get_entry_pathname (comics_document->a); ++ ++ g_debug ("Adding '%s' to the list of files in the comics", name); ++ g_ptr_array_add (array, g_strdup (name)); + } +- comics_document->extract_command = +- g_strdup_printf (command_usage_def[type].extract, +- quoted_command); +- comics_document->offset = command_usage_def[type].offset; +- if (command_usage_def[type].decompress_tmp) { +- comics_document->dir = ev_mkdtemp ("evince-comics-XXXXXX", error); +- if (comics_document->dir == NULL) +- return FALSE; +- +- /* unrar-free can't create directories, but ev_mkdtemp already created the dir */ +- +- comics_document->decompress_tmp = +- g_strdup_printf (command_usage_def[type].decompress_tmp, +- quoted_command, quoted_file, +- comics_document->dir); +- g_free (quoted_file); +- g_free (quoted_command); +- +- if (!comics_decompress_temp_dir (comics_document->decompress_tmp, +- comics_document->selected_command, error)) +- return FALSE; +- else +- return TRUE; ++ ++ if (array->len == 0) { ++ g_ptr_array_free (array, TRUE); + } else { +- g_free (quoted_file); +- g_free (quoted_command); +- return TRUE; ++ g_ptr_array_add (array, NULL); ++ ret = (char **) g_ptr_array_free (array, FALSE); + } + ++out: ++ comics_document_reset_archive (comics_document); ++ return ret; + } +-#pragma GCC diagnostic pop + +-/* This function chooses an external command for decompressing a comic +- * book based on its mime tipe. */ +-static gboolean +-comics_check_decompress_command (gchar *mime_type, ++/* This function chooses the archive decompression support ++ * book based on its mime type. */ ++static gboolean ++comics_check_decompress_support (gchar *mime_type, + ComicsDocument *comics_document, + GError **error) + { +- gboolean success; +- gchar *std_out, *std_err; +- gint retval; +- GError *err = NULL; +- +- /* FIXME, use proper cbr/cbz mime types once they're +- * included in shared-mime-info */ +- + if (g_content_type_is_a (mime_type, "application/x-cbr") || + g_content_type_is_a (mime_type, "application/x-rar")) { +- /* The RARLAB provides a no-charge proprietary (freeware) +- * decompress-only client for Linux called unrar. Another +- * option is a GPLv2-licensed command-line tool developed by +- * the Gna! project. Confusingly enough, the free software RAR +- * decoder is also named unrar. For this reason we need to add +- * some lines for disambiguation. Sorry for the added the +- * complexity but it's life :) +- * Finally, some distributions, like Debian, rename this free +- * option as unrar-free. +- * */ +- comics_document->selected_command = +- g_find_program_in_path ("unrar"); +- if (comics_document->selected_command) { +- /* We only use std_err to avoid printing useless error +- * messages on the terminal */ +- success = +- g_spawn_command_line_sync ( +- comics_document->selected_command, +- &std_out, &std_err, +- &retval, &err); +- if (!success) { +- g_propagate_error (error, err); +- g_error_free (err); +- return FALSE; +- /* I don't check retval status because RARLAB unrar +- * doesn't have a way to return 0 without involving an +- * operation with a file*/ +- } else if (WIFEXITED (retval)) { +- if (g_strrstr (std_out,"freeware") != NULL) +- /* The RARLAB freeware client */ +- comics_document->command_usage = RARLABS; +- else +- /* The Gna! free software client */ +- comics_document->command_usage = GNAUNRAR; +- +- g_free (std_out); +- g_free (std_err); +- return TRUE; +- } +- } +- /* The Gna! free software client with Debian naming convention */ +- comics_document->selected_command = +- g_find_program_in_path ("unrar-free"); +- if (comics_document->selected_command) { +- comics_document->command_usage = GNAUNRAR; +- return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("bsdtar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = TAR; ++ if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_RAR)) + return TRUE; +- } +- + } else if (g_content_type_is_a (mime_type, "application/x-cbz") || + g_content_type_is_a (mime_type, "application/zip")) { +- /* InfoZIP's unzip program */ +- comics_document->selected_command = +- g_find_program_in_path ("unzip"); +- comics_document->alternative_command = +- g_find_program_in_path ("zipnote"); +- if (comics_document->selected_command && +- comics_document->alternative_command) { +- comics_document->command_usage = UNZIP; +- return TRUE; +- } +- /* fallback mode using 7za and 7z from p7zip project */ +- comics_document->selected_command = +- g_find_program_in_path ("7za"); +- if (comics_document->selected_command) { +- comics_document->command_usage = P7ZIP; +- return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("7z"); +- if (comics_document->selected_command) { +- comics_document->command_usage = P7ZIP; ++ if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_ZIP)) + return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("bsdtar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = TAR; +- return TRUE; +- } +- + } else if (g_content_type_is_a (mime_type, "application/x-cb7") || + g_content_type_is_a (mime_type, "application/x-7z-compressed")) { +- /* 7zr, 7za and 7z are the commands from the p7zip project able +- * to decompress .7z files */ +- comics_document->selected_command = +- g_find_program_in_path ("7zr"); +- if (comics_document->selected_command) { +- comics_document->command_usage = P7ZIP; +- return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("7za"); +- if (comics_document->selected_command) { +- comics_document->command_usage = P7ZIP; +- return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("7z"); +- if (comics_document->selected_command) { +- comics_document->command_usage = P7ZIP; +- return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("bsdtar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = TAR; ++ if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_7Z)) + return TRUE; +- } + } else if (g_content_type_is_a (mime_type, "application/x-cbt") || + g_content_type_is_a (mime_type, "application/x-tar")) { +- /* tar utility (Tape ARchive) */ +- comics_document->selected_command = +- g_find_program_in_path ("tar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = TAR; ++ if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_TAR)) + return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("bsdtar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = TAR; +- return TRUE; +- } + } else { + g_set_error (error, + EV_DOCUMENT_ERROR, +@@ -457,8 +150,8 @@ comics_check_decompress_command (gchar *mime_type, + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, +- _("Can’t find an appropriate command to " +- "decompress this type of comic book")); ++ _("libarchive lacks support for this comic book’s " ++ "compression, please contact your distributor")); + return FALSE; + } + +@@ -487,56 +180,45 @@ comics_document_load (EvDocument *document, + { + ComicsDocument *comics_document = COMICS_DOCUMENT (document); + GSList *supported_extensions; +- gchar *std_out; + gchar *mime_type; + gchar **cb_files, *cb_file; +- gboolean success; +- int i, retval; ++ int i; + GError *err = NULL; ++ GFile *file; + +- comics_document->archive = g_filename_from_uri (uri, NULL, error); +- if (!comics_document->archive) ++ file = g_file_new_for_uri (uri); ++ comics_document->archive_path = g_file_get_path (file); ++ g_object_unref (file); ++ ++ if (!comics_document->archive_path) { ++ g_set_error_literal (error, ++ EV_DOCUMENT_ERROR, ++ EV_DOCUMENT_ERROR_INVALID, ++ _("Can not get local path for archive")); + return FALSE; ++ } ++ ++ comics_document->archive_uri = g_strdup (uri); + + mime_type = ev_file_get_mime_type (uri, FALSE, &err); + if (mime_type == NULL) + return FALSE; +- +- if (!comics_check_decompress_command (mime_type, comics_document, +- error)) { ++ ++ if (!comics_check_decompress_support (mime_type, comics_document, error)) { + g_free (mime_type); + return FALSE; +- } else if (!comics_generate_command_lines (comics_document, error)) { +- g_free (mime_type); +- return FALSE; + } ++ comics_document->a_type = ev_archive_get_archive_type (comics_document->a); + + g_free (mime_type); + + /* Get list of files in archive */ +- success = g_spawn_command_line_sync (comics_document->list_command, +- &std_out, NULL, &retval, error); +- +- if (!success) { +- return FALSE; +- } else if (!WIFEXITED(retval) || WEXITSTATUS(retval) != EXIT_SUCCESS) { ++ cb_files = comics_document_list (comics_document); ++ if (!cb_files) { + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, +- _("File corrupted")); +- return FALSE; +- } +- +- /* FIXME: is this safe against filenames containing \n in the archive ? */ +- cb_files = g_strsplit (std_out, EV_EOL, 0); +- +- g_free (std_out); +- +- if (!cb_files) { +- g_set_error_literal (error, +- EV_DOCUMENT_ERROR, +- EV_DOCUMENT_ERROR_INVALID, +- _("No files in archive")); ++ _("File corrupted or no files in archive")); + return FALSE; + } + +@@ -544,18 +226,7 @@ comics_document_load (EvDocument *document, + + supported_extensions = get_supported_image_extensions (); + for (i = 0; cb_files[i] != NULL; i++) { +- if (comics_document->offset != NO_OFFSET) { +- if (g_utf8_strlen (cb_files[i],-1) > +- comics_document->offset) { +- cb_file = +- g_utf8_offset_to_pointer (cb_files[i], +- comics_document->offset); +- } else { +- continue; +- } +- } else { +- cb_file = cb_files[i]; +- } ++ cb_file = cb_files[i]; + gchar *suffix = g_strrstr (cb_file, "."); + if (!suffix) + continue; +@@ -586,7 +257,6 @@ comics_document_load (EvDocument *document, + return TRUE; + } + +- + static gboolean + comics_document_save (EvDocument *document, + const char *uri, +@@ -594,7 +264,7 @@ comics_document_save (EvDocument *document, + { + ComicsDocument *comics_document = COMICS_DOCUMENT (document); + +- return ev_xfer_uri_simple (comics_document->archive, uri, error); ++ return ev_xfer_uri_simple (comics_document->archive_uri, uri, error); + } + + static int +@@ -608,6 +278,12 @@ comics_document_get_n_pages (EvDocument *document) + return comics_document->page_names->len; + } + ++typedef struct { ++ gboolean got_info; ++ int height; ++ int width; ++} pixbuf_info; ++ + static void + comics_document_get_page_size (EvDocument *document, + EvPage *page, +@@ -615,74 +291,79 @@ comics_document_get_page_size (EvDocument *document, + double *height) + { + GdkPixbufLoader *loader; +- char **argv; +- guchar buf[1024]; +- gboolean success, got_size = FALSE; +- gint outpipe = -1; +- GPid child_pid; +- gssize bytes; +- GdkPixbuf *pixbuf; +- gchar *filename; + ComicsDocument *comics_document = COMICS_DOCUMENT (document); +- +- if (!comics_document->decompress_tmp) { +- argv = extract_argv (document, page->index); +- success = g_spawn_async_with_pipes (NULL, argv, NULL, +- G_SPAWN_SEARCH_PATH | +- G_SPAWN_STDERR_TO_DEV_NULL, +- NULL, NULL, +- &child_pid, +- NULL, &outpipe, NULL, NULL); +- g_strfreev (argv); +- g_return_if_fail (success == TRUE); +- +- loader = gdk_pixbuf_loader_new (); +- g_signal_connect (loader, "area-prepared", +- G_CALLBACK (get_page_size_area_prepared_cb), +- &got_size); +- +- while (outpipe >= 0) { +- bytes = read (outpipe, buf, 1024); +- +- if (bytes > 0) +- gdk_pixbuf_loader_write (loader, buf, bytes, NULL); +- if (bytes <= 0 || got_size) { +- close (outpipe); +- outpipe = -1; +- gdk_pixbuf_loader_close (loader, NULL); ++ const char *page_path; ++ pixbuf_info info; ++ GError *error = NULL; ++ ++ if (!ev_archive_open_filename (comics_document->a, comics_document->archive_path, &error)) { ++ g_warning ("Fatal error opening archive: %s", error->message); ++ g_error_free (error); ++ goto out; ++ } ++ ++ loader = gdk_pixbuf_loader_new (); ++ info.got_info = FALSE; ++ g_signal_connect (loader, "size-prepared", ++ G_CALLBACK (get_page_size_prepared_cb), ++ &info); ++ ++ page_path = g_ptr_array_index (comics_document->page_names, page->index); ++ ++ while (1) { ++ const char *name; ++ GError *error = NULL; ++ ++ if (!ev_archive_read_next_header (comics_document->a, &error)) { ++ if (error != NULL) { ++ g_warning ("Fatal error handling archive: %s", error->message); ++ g_error_free (error); + } ++ break; + } +- pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); +- if (pixbuf) { +- if (width) +- *width = gdk_pixbuf_get_width (pixbuf); +- if (height) +- *height = gdk_pixbuf_get_height (pixbuf); +- } +- g_spawn_close_pid (child_pid); +- g_object_unref (loader); +- } else { +- filename = g_build_filename (comics_document->dir, +- (char *) comics_document->page_names->pdata[page->index], +- NULL); +- pixbuf = gdk_pixbuf_new_from_file (filename, NULL); +- if (pixbuf) { +- if (width) +- *width = gdk_pixbuf_get_width (pixbuf); +- if (height) +- *height = gdk_pixbuf_get_height (pixbuf); +- g_object_unref (pixbuf); ++ ++ name = ev_archive_get_entry_pathname (comics_document->a); ++ if (g_strcmp0 (name, page_path) == 0) { ++ char buf[BLOCK_SIZE]; ++ gssize read; ++ ++ read = ev_archive_read_data (comics_document->a, buf, sizeof(buf), &error); ++ while (read > 0 && !info.got_info) { ++ gdk_pixbuf_loader_write (loader, (guchar *) buf, read, NULL); ++ read = ev_archive_read_data (comics_document->a, buf, BLOCK_SIZE, &error); ++ } ++ if (read < 0) { ++ g_warning ("Fatal error reading '%s' in archive: %s", name, error->message); ++ g_error_free (error); ++ } ++ break; + } +- g_free (filename); + } ++ ++ gdk_pixbuf_loader_close (loader, NULL); ++ g_object_unref (loader); ++ ++ if (info.got_info) { ++ if (width) ++ *width = info.width; ++ if (height) ++ *height = info.height; ++ } ++ ++out: ++ comics_document_reset_archive (comics_document); + } + + static void +-get_page_size_area_prepared_cb (GdkPixbufLoader *loader, +- gpointer data) ++get_page_size_prepared_cb (GdkPixbufLoader *loader, ++ int width, ++ int height, ++ gpointer data) + { +- gboolean *got_size = data; +- *got_size = TRUE; ++ pixbuf_info *info = data; ++ info->got_info = TRUE; ++ info->height = height; ++ info->width = width; + } + + static GdkPixbuf * +@@ -690,73 +371,73 @@ comics_document_render_pixbuf (EvDocument *document, + EvRenderContext *rc) + { + GdkPixbufLoader *loader; +- GdkPixbuf *rotated_pixbuf, *tmp_pixbuf; +- char **argv; +- guchar buf[4096]; +- gboolean success; +- gint outpipe = -1; +- GPid child_pid; +- gssize bytes; +- gint width, height; +- gchar *filename; ++ GdkPixbuf *tmp_pixbuf; ++ GdkPixbuf *rotated_pixbuf; + ComicsDocument *comics_document = COMICS_DOCUMENT (document); +- +- if (!comics_document->decompress_tmp) { +- argv = extract_argv (document, rc->page->index); +- success = g_spawn_async_with_pipes (NULL, argv, NULL, +- G_SPAWN_SEARCH_PATH | +- G_SPAWN_STDERR_TO_DEV_NULL, +- NULL, NULL, +- &child_pid, +- NULL, &outpipe, NULL, NULL); +- g_strfreev (argv); +- g_return_val_if_fail (success == TRUE, NULL); +- +- loader = gdk_pixbuf_loader_new (); +- g_signal_connect (loader, "size-prepared", +- G_CALLBACK (render_pixbuf_size_prepared_cb), +- rc); +- +- while (outpipe >= 0) { +- bytes = read (outpipe, buf, 4096); +- +- if (bytes > 0) { +- gdk_pixbuf_loader_write (loader, buf, bytes, +- NULL); +- } else if (bytes <= 0) { +- close (outpipe); +- gdk_pixbuf_loader_close (loader, NULL); +- outpipe = -1; ++ const char *page_path; ++ GError *error = NULL; ++ ++ if (!ev_archive_open_filename (comics_document->a, comics_document->archive_path, &error)) { ++ g_warning ("Fatal error opening archive: %s", error->message); ++ g_error_free (error); ++ goto out; ++ } ++ ++ loader = gdk_pixbuf_loader_new (); ++ g_signal_connect (loader, "size-prepared", ++ G_CALLBACK (render_pixbuf_size_prepared_cb), ++ rc); ++ ++ page_path = g_ptr_array_index (comics_document->page_names, rc->page->index); ++ ++ while (1) { ++ const char *name; ++ ++ if (!ev_archive_read_next_header (comics_document->a, &error)) { ++ if (error != NULL) { ++ g_warning ("Fatal error handling archive: %s", error->message); ++ g_error_free (error); + } ++ break; ++ } ++ ++ name = ev_archive_get_entry_pathname (comics_document->a); ++ if (g_strcmp0 (name, page_path) == 0) { ++ size_t size = ev_archive_get_entry_size (comics_document->a); ++ char *buf; ++ ssize_t read; ++ ++ buf = g_malloc (size); ++ read = ev_archive_read_data (comics_document->a, buf, size, &error); ++ if (read <= 0) { ++ if (read < 0) { ++ g_warning ("Fatal error reading '%s' in archive: %s", name, error->message); ++ g_error_free (error); ++ } else { ++ g_warning ("Read an empty file from the archive"); ++ } ++ } else { ++ gdk_pixbuf_loader_write (loader, (guchar *) buf, size, NULL); ++ } ++ g_free (buf); ++ gdk_pixbuf_loader_close (loader, NULL); ++ break; + } +- tmp_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); +- rotated_pixbuf = +- gdk_pixbuf_rotate_simple (tmp_pixbuf, +- 360 - rc->rotation); +- g_spawn_close_pid (child_pid); +- g_object_unref (loader); +- } else { +- int scaled_width, scaled_height; +- +- filename = +- g_build_filename (comics_document->dir, +- (char *) comics_document->page_names->pdata[rc->page->index], +- NULL); +- +- gdk_pixbuf_get_file_info (filename, &width, &height); +- +- ev_render_context_compute_scaled_size (rc, width, height, +- &scaled_width, &scaled_height); +- +- tmp_pixbuf = +- gdk_pixbuf_new_from_file_at_size ( +- filename, scaled_width, scaled_height, NULL); +- rotated_pixbuf = +- gdk_pixbuf_rotate_simple (tmp_pixbuf, +- 360 - rc->rotation); +- g_free (filename); +- g_object_unref (tmp_pixbuf); + } ++ ++ tmp_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); ++ rotated_pixbuf = NULL; ++ if (tmp_pixbuf) { ++ if ((rc->rotation % 360) == 0) ++ rotated_pixbuf = g_object_ref (tmp_pixbuf); ++ else ++ rotated_pixbuf = gdk_pixbuf_rotate_simple (tmp_pixbuf, ++ 360 - rc->rotation); ++ } ++ g_object_unref (loader); ++ ++out: ++ comics_document_reset_archive (comics_document); + return rotated_pixbuf; + } + +@@ -770,7 +451,7 @@ comics_document_render (EvDocument *document, + pixbuf = comics_document_render_pixbuf (document, rc); + surface = ev_document_misc_surface_from_pixbuf (pixbuf); + g_object_unref (pixbuf); +- ++ + return surface; + } + +@@ -786,60 +467,19 @@ render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader, + gdk_pixbuf_loader_set_size (loader, scaled_width, scaled_height); + } + +-/** +- * comics_remove_dir: Removes a directory recursively. +- * Returns: +- * 0 if it was successfully deleted, +- * -1 if an error occurred +- */ +-static int +-comics_remove_dir (gchar *path_name) +-{ +- GDir *content_dir; +- const gchar *filename; +- gchar *filename_with_path; +- +- if (g_file_test (path_name, G_FILE_TEST_IS_DIR)) { +- content_dir = g_dir_open (path_name, 0, NULL); +- filename = g_dir_read_name (content_dir); +- while (filename) { +- filename_with_path = +- g_build_filename (path_name, +- filename, NULL); +- comics_remove_dir (filename_with_path); +- g_free (filename_with_path); +- filename = g_dir_read_name (content_dir); +- } +- g_dir_close (content_dir); +- } +- /* Note from g_remove() documentation: on Windows, it is in general not +- * possible to remove a file that is open to some process, or mapped +- * into memory.*/ +- return (g_remove (path_name)); +-} +- + static void + comics_document_finalize (GObject *object) + { + ComicsDocument *comics_document = COMICS_DOCUMENT (object); +- +- if (comics_document->decompress_tmp) { +- if (comics_remove_dir (comics_document->dir) == -1) +- g_warning (_("There was an error deleting “%s”."), +- comics_document->dir); +- g_free (comics_document->dir); +- } +- ++ + if (comics_document->page_names) { + g_ptr_array_foreach (comics_document->page_names, (GFunc) g_free, NULL); + g_ptr_array_free (comics_document->page_names, TRUE); + } + +- g_free (comics_document->archive); +- g_free (comics_document->selected_command); +- g_free (comics_document->alternative_command); +- g_free (comics_document->extract_command); +- g_free (comics_document->list_command); ++ g_clear_object (&comics_document->a); ++ g_free (comics_document->archive_path); ++ g_free (comics_document->archive_uri); + + G_OBJECT_CLASS (comics_document_parent_class)->finalize (object); + } +@@ -862,9 +502,8 @@ comics_document_class_init (ComicsDocumentClass *klass) + static void + comics_document_init (ComicsDocument *comics_document) + { +- comics_document->archive = NULL; ++ comics_document->a = ev_archive_new (); + comics_document->page_names = NULL; +- comics_document->extract_command = NULL; + } + + /* Returns a list of file extensions supported by gdk-pixbuf */ +@@ -890,41 +529,3 @@ get_supported_image_extensions(void) + g_slist_free (formats); + return extensions; + } +- +-static char** +-extract_argv (EvDocument *document, gint page) +-{ +- ComicsDocument *comics_document = COMICS_DOCUMENT (document); +- char **argv; +- char *command_line, *quoted_archive, *quoted_filename; +- GError *err = NULL; +- +- if (page >= comics_document->page_names->len) +- return NULL; +- +- if (comics_document->regex_arg) { +- quoted_archive = g_shell_quote (comics_document->archive); +- quoted_filename = +- comics_regex_quote (comics_document->page_names->pdata[page]); +- } else { +- quoted_archive = g_shell_quote (comics_document->archive); +- quoted_filename = g_shell_quote (comics_document->page_names->pdata[page]); +- } +- +- command_line = g_strdup_printf ("%s %s %s", +- comics_document->extract_command, +- quoted_archive, +- quoted_filename); +- g_shell_parse_argv (command_line, NULL, &argv, &err); +- +- if (err) { +- g_warning (_("Error %s"), err->message); +- g_error_free (err); +- return NULL; +- } +- +- g_free (command_line); +- g_free (quoted_archive); +- g_free (quoted_filename); +- return argv; +-} +diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c +new file mode 100644 +index 00000000..9efa69f8 +--- /dev/null ++++ b/backend/comics/ev-archive.c +@@ -0,0 +1,277 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ ++/* ++ * Copyright (C) 2017, Bastien Nocera ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include "ev-archive.h" ++ ++#include ++#include ++#include ++ ++#define BUFFER_SIZE (64 * 1024) ++ ++struct _EvArchive { ++ GObject parent_instance; ++ EvArchiveType type; ++ ++ /* libarchive */ ++ struct archive *libar; ++ struct archive_entry *libar_entry; ++}; ++ ++G_DEFINE_TYPE(EvArchive, ev_archive, G_TYPE_OBJECT); ++ ++static void ++ev_archive_finalize (GObject *object) ++{ ++ EvArchive *archive = EV_ARCHIVE (object); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_RAR: ++ break; ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ g_clear_pointer (&archive->libar, archive_free); ++ break; ++ default: ++ break; ++ } ++ ++ G_OBJECT_CLASS (ev_archive_parent_class)->finalize (object); ++} ++ ++static void ++ev_archive_class_init (EvArchiveClass *class) ++{ ++ GObjectClass *object_class = (GObjectClass *) class; ++ object_class->finalize = ev_archive_finalize; ++} ++ ++EvArchive * ++ev_archive_new (void) ++{ ++ return g_object_new (EV_TYPE_ARCHIVE, NULL); ++} ++ ++static void ++libarchive_set_archive_type (EvArchive *archive, ++ EvArchiveType archive_type) ++{ ++ archive->type = archive_type; ++ archive->libar = archive_read_new (); ++ ++ if (archive_type == EV_ARCHIVE_TYPE_ZIP) ++ archive_read_support_format_zip (archive->libar); ++ else if (archive_type == EV_ARCHIVE_TYPE_7Z) ++ archive_read_support_format_7zip (archive->libar); ++ else if (archive_type == EV_ARCHIVE_TYPE_TAR) ++ archive_read_support_format_tar (archive->libar); ++} ++ ++EvArchiveType ++ev_archive_get_archive_type (EvArchive *archive) ++{ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), EV_ARCHIVE_TYPE_NONE); ++ return archive->type; ++} ++ ++gboolean ++ev_archive_set_archive_type (EvArchive *archive, ++ EvArchiveType archive_type) ++{ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE); ++ g_return_val_if_fail (archive->type == EV_ARCHIVE_TYPE_NONE, FALSE); ++ ++ switch (archive_type) { ++ case EV_ARCHIVE_TYPE_RAR: ++ /* Disabled until this is fixed: ++ * https://github.com/libarchive/libarchive/issues/373 */ ++ return FALSE; ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ libarchive_set_archive_type (archive, archive_type); ++ break; ++ default: ++ g_assert_not_reached (); ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++ev_archive_open_filename (EvArchive *archive, ++ const char *path, ++ GError **error) ++{ ++ int r; ++ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE); ++ g_return_val_if_fail (path != NULL, FALSE); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); ++ case EV_ARCHIVE_TYPE_RAR: ++ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Archive type 'RAR' not supported"); ++ return FALSE; ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ r = archive_read_open_filename (archive->libar, path, BUFFER_SIZE); ++ if (r != ARCHIVE_OK) { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Error opening archive: %s", archive_error_string (archive->libar)); ++ return FALSE; ++ } ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static gboolean ++libarchive_read_next_header (EvArchive *archive, ++ GError **error) ++{ ++ while (1) { ++ int r; ++ ++ r = archive_read_next_header (archive->libar, &archive->libar_entry); ++ if (r != ARCHIVE_OK) { ++ if (r != ARCHIVE_EOF) ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Error reading archive: %s", archive_error_string (archive->libar)); ++ return FALSE; ++ } ++ ++ if (archive_entry_filetype (archive->libar_entry) != AE_IFREG) { ++ g_debug ("Skipping '%s' as it's not a regular file", ++ archive_entry_pathname (archive->libar_entry)); ++ continue; ++ } ++ ++ g_debug ("At header for file '%s'", archive_entry_pathname (archive->libar_entry)); ++ ++ break; ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++ev_archive_read_next_header (EvArchive *archive, ++ GError **error) ++{ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_NONE: ++ case EV_ARCHIVE_TYPE_RAR: ++ g_assert_not_reached (); ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ return libarchive_read_next_header (archive, error); ++ } ++ ++ return FALSE; ++} ++ ++const char * ++ev_archive_get_entry_pathname (EvArchive *archive) ++{ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), NULL); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, NULL); ++ g_return_val_if_fail (archive->libar_entry != NULL, NULL); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); ++ case EV_ARCHIVE_TYPE_RAR: ++ return NULL; ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ return archive_entry_pathname (archive->libar_entry); ++ } ++ ++ return NULL; ++} ++ ++gint64 ++ev_archive_get_entry_size (EvArchive *archive) ++{ ++ gint64 r; ++ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1); ++ g_return_val_if_fail (archive->libar_entry != NULL, -1); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_RAR: ++ case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ r = archive_entry_size (archive->libar_entry); ++ break; ++ } ++ ++ return r; ++} ++ ++gssize ++ev_archive_read_data (EvArchive *archive, ++ void *buf, ++ gsize count, ++ GError **error) ++{ ++ gssize r = -1; ++ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1); ++ g_return_val_if_fail (archive->libar_entry != NULL, -1); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_RAR: ++ case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ r = archive_read_data (archive->libar, buf, count); ++ if (r < 0) { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to decompress data: %s", archive_error_string (archive->libar)); ++ } ++ break; ++ } ++ ++ return r; ++} ++ ++static void ++ev_archive_init (EvArchive *archive) ++{ ++} +diff --git a/backend/comics/ev-archive.h b/backend/comics/ev-archive.h +new file mode 100644 +index 00000000..38d47d79 +--- /dev/null ++++ b/backend/comics/ev-archive.h +@@ -0,0 +1,47 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ ++/* ++ * Copyright (C) 2017, Bastien Nocera ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include ++ ++#define EV_TYPE_ARCHIVE ev_archive_get_type () ++G_DECLARE_FINAL_TYPE (EvArchive, ev_archive, EV, ARCHIVE, GObject) ++ ++typedef enum { ++ EV_ARCHIVE_TYPE_NONE = 0, ++ EV_ARCHIVE_TYPE_RAR, ++ EV_ARCHIVE_TYPE_ZIP, ++ EV_ARCHIVE_TYPE_7Z, ++ EV_ARCHIVE_TYPE_TAR ++} EvArchiveType; ++ ++EvArchive *ev_archive_new (void); ++gboolean ev_archive_set_archive_type (EvArchive *archive, ++ EvArchiveType archive_type); ++EvArchiveType ev_archive_get_archive_type (EvArchive *archive); ++gboolean ev_archive_open_filename (EvArchive *archive, ++ const char *path, ++ GError **error); ++gboolean ev_archive_read_next_header (EvArchive *archive, ++ GError **error); ++const char *ev_archive_get_entry_pathname (EvArchive *archive); ++gint64 ev_archive_get_entry_size (EvArchive *archive); ++gssize ev_archive_read_data (EvArchive *archive, ++ void *buf, ++ gsize count, ++ GError **error); +diff --git a/configure.ac b/configure.ac +index 9e9f8316..a5ae8ccd 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -688,9 +688,17 @@ AC_ARG_ENABLE(comics, + [Compile with support for comic book archives])], + [enable_comics=$enableval], + [enable_comics=yes]) +- ++ + if test "x$enable_comics" = "xyes"; then +- AC_DEFINE([ENABLE_COMICS], [1], [Enable support for comics.]) ++ LIBARCHIVE_REQUIRED=3.1.2 ++ PKG_CHECK_MODULES(LIBARCHIVE, libarchive >= $LIBARCHIVE_REQUIRED,enable_comics=yes,enable_comics=no) ++ ++ if test "x$enable_comics" = "xyes"; then ++ AC_DEFINE([ENABLE_COMICS], [1], [Enable support for comics.]) ++ else ++ enable_comics="no" ++ AC_MSG_WARN([Comics support is disabled since libarchive (version >= $LIBARCHIVE_REQUIRED) is needed]) ++ fi + fi + AM_CONDITIONAL(ENABLE_COMICS, test x$enable_comics = xyes) + +-- +2.13.0 + + +From ef5fa12af73a698e03356f338d801b39a1e7281d Mon Sep 17 00:00:00 2001 +From: Carlos Garcia Campos +Date: Sat, 25 Mar 2017 11:33:42 +0100 +Subject: [PATCH 2/8] comics: cosmetic changes + +--- + backend/comics/comics-document.c | 109 ++++++++++++++++++--------------------- + backend/comics/ev-archive.c | 30 +++++------ + backend/comics/ev-archive.h | 33 +++++++----- + 3 files changed, 85 insertions(+), 87 deletions(-) + +diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c +index b55c25b3..87c25270 100644 +--- a/backend/comics/comics-document.c ++++ b/backend/comics/comics-document.c +@@ -48,31 +48,23 @@ struct _ComicsDocumentClass + struct _ComicsDocument + { + EvDocument parent_instance; +- EvArchive *a; +- EvArchiveType a_type; ++ EvArchive *archive; ++ EvArchiveType archive_type; + gchar *archive_path; + gchar *archive_uri; + GPtrArray *page_names; + }; + +-static GSList* get_supported_image_extensions (void); +-static void get_page_size_prepared_cb (GdkPixbufLoader *loader, +- int width, +- int height, +- gpointer data); +-static void render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader, +- gint width, +- gint height, +- EvRenderContext *rc); ++static GSList* get_supported_image_extensions (void); + + EV_BACKEND_REGISTER (ComicsDocument, comics_document) + + static void + comics_document_reset_archive (ComicsDocument *comics_document) + { +- g_clear_object (&comics_document->a); +- comics_document->a = ev_archive_new (); +- ev_archive_set_archive_type (comics_document->a, comics_document->a_type); ++ g_clear_object (&comics_document->archive); ++ comics_document->archive = ev_archive_new (); ++ ev_archive_set_archive_type (comics_document->archive, comics_document->archive_type); + } + + static char ** +@@ -81,7 +73,7 @@ comics_document_list (ComicsDocument *comics_document) + char **ret = NULL; + GPtrArray *array; + +- if (!ev_archive_open_filename (comics_document->a, comics_document->archive_path, NULL)) ++ if (!ev_archive_open_filename (comics_document->archive, comics_document->archive_path, NULL)) + goto out; + + array = g_ptr_array_new (); +@@ -90,7 +82,7 @@ comics_document_list (ComicsDocument *comics_document) + const char *name; + GError *error = NULL; + +- if (!ev_archive_read_next_header (comics_document->a, &error)) { ++ if (!ev_archive_read_next_header (comics_document->archive, &error)) { + if (error != NULL) { + g_warning ("Fatal error handling archive: %s", error->message); + g_error_free (error); +@@ -98,7 +90,7 @@ comics_document_list (ComicsDocument *comics_document) + break; + } + +- name = ev_archive_get_entry_pathname (comics_document->a); ++ name = ev_archive_get_entry_pathname (comics_document->archive); + + g_debug ("Adding '%s' to the list of files in the comics", name); + g_ptr_array_add (array, g_strdup (name)); +@@ -125,19 +117,19 @@ comics_check_decompress_support (gchar *mime_type, + { + if (g_content_type_is_a (mime_type, "application/x-cbr") || + g_content_type_is_a (mime_type, "application/x-rar")) { +- if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_RAR)) ++ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_RAR)) + return TRUE; + } else if (g_content_type_is_a (mime_type, "application/x-cbz") || + g_content_type_is_a (mime_type, "application/zip")) { +- if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_ZIP)) ++ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_ZIP)) + return TRUE; + } else if (g_content_type_is_a (mime_type, "application/x-cb7") || + g_content_type_is_a (mime_type, "application/x-7z-compressed")) { +- if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_7Z)) ++ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_7Z)) + return TRUE; + } else if (g_content_type_is_a (mime_type, "application/x-cbt") || + g_content_type_is_a (mime_type, "application/x-tar")) { +- if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_TAR)) ++ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_TAR)) + return TRUE; + } else { + g_set_error (error, +@@ -208,10 +200,10 @@ comics_document_load (EvDocument *document, + g_free (mime_type); + return FALSE; + } +- comics_document->a_type = ev_archive_get_archive_type (comics_document->a); +- + g_free (mime_type); + ++ comics_document->archive_type = ev_archive_get_archive_type (comics_document->archive); ++ + /* Get list of files in archive */ + cb_files = comics_document_list (comics_document); + if (!cb_files) { +@@ -282,7 +274,18 @@ typedef struct { + gboolean got_info; + int height; + int width; +-} pixbuf_info; ++} PixbufInfo; ++ ++static void ++get_page_size_prepared_cb (GdkPixbufLoader *loader, ++ int width, ++ int height, ++ PixbufInfo *info) ++{ ++ info->got_info = TRUE; ++ info->height = height; ++ info->width = width; ++} + + static void + comics_document_get_page_size (EvDocument *document, +@@ -293,10 +296,10 @@ comics_document_get_page_size (EvDocument *document, + GdkPixbufLoader *loader; + ComicsDocument *comics_document = COMICS_DOCUMENT (document); + const char *page_path; +- pixbuf_info info; ++ PixbufInfo info; + GError *error = NULL; + +- if (!ev_archive_open_filename (comics_document->a, comics_document->archive_path, &error)) { ++ if (!ev_archive_open_filename (comics_document->archive, comics_document->archive_path, &error)) { + g_warning ("Fatal error opening archive: %s", error->message); + g_error_free (error); + goto out; +@@ -314,7 +317,7 @@ comics_document_get_page_size (EvDocument *document, + const char *name; + GError *error = NULL; + +- if (!ev_archive_read_next_header (comics_document->a, &error)) { ++ if (!ev_archive_read_next_header (comics_document->archive, &error)) { + if (error != NULL) { + g_warning ("Fatal error handling archive: %s", error->message); + g_error_free (error); +@@ -322,15 +325,15 @@ comics_document_get_page_size (EvDocument *document, + break; + } + +- name = ev_archive_get_entry_pathname (comics_document->a); ++ name = ev_archive_get_entry_pathname (comics_document->archive); + if (g_strcmp0 (name, page_path) == 0) { + char buf[BLOCK_SIZE]; + gssize read; + +- read = ev_archive_read_data (comics_document->a, buf, sizeof(buf), &error); ++ read = ev_archive_read_data (comics_document->archive, buf, sizeof(buf), &error); + while (read > 0 && !info.got_info) { + gdk_pixbuf_loader_write (loader, (guchar *) buf, read, NULL); +- read = ev_archive_read_data (comics_document->a, buf, BLOCK_SIZE, &error); ++ read = ev_archive_read_data (comics_document->archive, buf, BLOCK_SIZE, &error); + } + if (read < 0) { + g_warning ("Fatal error reading '%s' in archive: %s", name, error->message); +@@ -355,15 +358,15 @@ out: + } + + static void +-get_page_size_prepared_cb (GdkPixbufLoader *loader, +- int width, +- int height, +- gpointer data) ++render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader, ++ gint width, ++ gint height, ++ EvRenderContext *rc) + { +- pixbuf_info *info = data; +- info->got_info = TRUE; +- info->height = height; +- info->width = width; ++ int scaled_width, scaled_height; ++ ++ ev_render_context_compute_scaled_size (rc, width, height, &scaled_width, &scaled_height); ++ gdk_pixbuf_loader_set_size (loader, scaled_width, scaled_height); + } + + static GdkPixbuf * +@@ -372,12 +375,12 @@ comics_document_render_pixbuf (EvDocument *document, + { + GdkPixbufLoader *loader; + GdkPixbuf *tmp_pixbuf; +- GdkPixbuf *rotated_pixbuf; ++ GdkPixbuf *rotated_pixbuf = NULL; + ComicsDocument *comics_document = COMICS_DOCUMENT (document); + const char *page_path; + GError *error = NULL; + +- if (!ev_archive_open_filename (comics_document->a, comics_document->archive_path, &error)) { ++ if (!ev_archive_open_filename (comics_document->archive, comics_document->archive_path, &error)) { + g_warning ("Fatal error opening archive: %s", error->message); + g_error_free (error); + goto out; +@@ -393,7 +396,7 @@ comics_document_render_pixbuf (EvDocument *document, + while (1) { + const char *name; + +- if (!ev_archive_read_next_header (comics_document->a, &error)) { ++ if (!ev_archive_read_next_header (comics_document->archive, &error)) { + if (error != NULL) { + g_warning ("Fatal error handling archive: %s", error->message); + g_error_free (error); +@@ -401,14 +404,14 @@ comics_document_render_pixbuf (EvDocument *document, + break; + } + +- name = ev_archive_get_entry_pathname (comics_document->a); ++ name = ev_archive_get_entry_pathname (comics_document->archive); + if (g_strcmp0 (name, page_path) == 0) { +- size_t size = ev_archive_get_entry_size (comics_document->a); ++ size_t size = ev_archive_get_entry_size (comics_document->archive); + char *buf; + ssize_t read; + + buf = g_malloc (size); +- read = ev_archive_read_data (comics_document->a, buf, size, &error); ++ read = ev_archive_read_data (comics_document->archive, buf, size, &error); + if (read <= 0) { + if (read < 0) { + g_warning ("Fatal error reading '%s' in archive: %s", name, error->message); +@@ -426,7 +429,6 @@ comics_document_render_pixbuf (EvDocument *document, + } + + tmp_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); +- rotated_pixbuf = NULL; + if (tmp_pixbuf) { + if ((rc->rotation % 360) == 0) + rotated_pixbuf = g_object_ref (tmp_pixbuf); +@@ -456,18 +458,6 @@ comics_document_render (EvDocument *document, + } + + static void +-render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader, +- gint width, +- gint height, +- EvRenderContext *rc) +-{ +- int scaled_width, scaled_height; +- +- ev_render_context_compute_scaled_size (rc, width, height, &scaled_width, &scaled_height); +- gdk_pixbuf_loader_set_size (loader, scaled_width, scaled_height); +-} +- +-static void + comics_document_finalize (GObject *object) + { + ComicsDocument *comics_document = COMICS_DOCUMENT (object); +@@ -477,7 +467,7 @@ comics_document_finalize (GObject *object) + g_ptr_array_free (comics_document->page_names, TRUE); + } + +- g_clear_object (&comics_document->a); ++ g_clear_object (&comics_document->archive); + g_free (comics_document->archive_path); + g_free (comics_document->archive_uri); + +@@ -502,8 +492,7 @@ comics_document_class_init (ComicsDocumentClass *klass) + static void + comics_document_init (ComicsDocument *comics_document) + { +- comics_document->a = ev_archive_new (); +- comics_document->page_names = NULL; ++ comics_document->archive = ev_archive_new (); + } + + /* Returns a list of file extensions supported by gdk-pixbuf */ +diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c +index 9efa69f8..333db470 100644 +--- a/backend/comics/ev-archive.c ++++ b/backend/comics/ev-archive.c +@@ -17,6 +17,7 @@ + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + ++#include "config.h" + #include "ev-archive.h" + + #include +@@ -57,9 +58,10 @@ ev_archive_finalize (GObject *object) + } + + static void +-ev_archive_class_init (EvArchiveClass *class) ++ev_archive_class_init (EvArchiveClass *klass) + { +- GObjectClass *object_class = (GObjectClass *) class; ++ GObjectClass *object_class = (GObjectClass *) klass; ++ + object_class->finalize = ev_archive_finalize; + } + +@@ -88,6 +90,7 @@ EvArchiveType + ev_archive_get_archive_type (EvArchive *archive) + { + g_return_val_if_fail (EV_IS_ARCHIVE (archive), EV_ARCHIVE_TYPE_NONE); ++ + return archive->type; + } + +@@ -149,8 +152,8 @@ ev_archive_open_filename (EvArchive *archive, + } + + static gboolean +-libarchive_read_next_header (EvArchive *archive, +- GError **error) ++libarchive_read_next_header (EvArchive *archive, ++ GError **error) + { + while (1) { + int r; +@@ -178,8 +181,8 @@ libarchive_read_next_header (EvArchive *archive, + } + + gboolean +-ev_archive_read_next_header (EvArchive *archive, +- GError **error) ++ev_archive_read_next_header (EvArchive *archive, ++ GError **error) + { + g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE); + g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE); +@@ -221,8 +224,6 @@ ev_archive_get_entry_pathname (EvArchive *archive) + gint64 + ev_archive_get_entry_size (EvArchive *archive) + { +- gint64 r; +- + g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1); + g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1); + g_return_val_if_fail (archive->libar_entry != NULL, -1); +@@ -234,18 +235,17 @@ ev_archive_get_entry_size (EvArchive *archive) + case EV_ARCHIVE_TYPE_ZIP: + case EV_ARCHIVE_TYPE_7Z: + case EV_ARCHIVE_TYPE_TAR: +- r = archive_entry_size (archive->libar_entry); +- break; ++ return archive_entry_size (archive->libar_entry); + } + +- return r; ++ return -1; + } + + gssize +-ev_archive_read_data (EvArchive *archive, +- void *buf, +- gsize count, +- GError **error) ++ev_archive_read_data (EvArchive *archive, ++ void *buf, ++ gsize count, ++ GError **error) + { + gssize r = -1; + +diff --git a/backend/comics/ev-archive.h b/backend/comics/ev-archive.h +index 38d47d79..3e206939 100644 +--- a/backend/comics/ev-archive.h ++++ b/backend/comics/ev-archive.h +@@ -17,8 +17,13 @@ + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + ++#ifndef __EV_ARCHIVE_H__ ++#define __EV_ARCHIVE_H__ ++ + #include + ++G_BEGIN_DECLS ++ + #define EV_TYPE_ARCHIVE ev_archive_get_type () + G_DECLARE_FINAL_TYPE (EvArchive, ev_archive, EV, ARCHIVE, GObject) + +@@ -33,15 +38,19 @@ typedef enum { + EvArchive *ev_archive_new (void); + gboolean ev_archive_set_archive_type (EvArchive *archive, + EvArchiveType archive_type); +-EvArchiveType ev_archive_get_archive_type (EvArchive *archive); +-gboolean ev_archive_open_filename (EvArchive *archive, +- const char *path, +- GError **error); +-gboolean ev_archive_read_next_header (EvArchive *archive, +- GError **error); +-const char *ev_archive_get_entry_pathname (EvArchive *archive); +-gint64 ev_archive_get_entry_size (EvArchive *archive); +-gssize ev_archive_read_data (EvArchive *archive, +- void *buf, +- gsize count, +- GError **error); ++EvArchiveType ev_archive_get_archive_type (EvArchive *archive); ++gboolean ev_archive_open_filename (EvArchive *archive, ++ const char *path, ++ GError **error); ++gboolean ev_archive_read_next_header (EvArchive *archive, ++ GError **error); ++const char *ev_archive_get_entry_pathname (EvArchive *archive); ++gint64 ev_archive_get_entry_size (EvArchive *archive); ++gssize ev_archive_read_data (EvArchive *archive, ++ void *buf, ++ gsize count, ++ GError **error); ++ ++G_END_DECLS ++ ++#endif /* __EV_ARCHIVE_H__ */ +-- +2.13.0 + + +From b406f5a0286413a2debb5f978951b1c3f6098a5c Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 15 Mar 2017 18:10:17 +0100 +Subject: [PATCH 3/8] comics: Add unarr copy/paste + +To allow us to decompress CBR comics compressed with recent versions of +RAR, from https://github.com/sumatrapdfreader/sumatrapdf/tree/master/ext/unarr + +https://bugzilla.gnome.org/show_bug.cgi?id=720742 +--- + configure.ac | 1 + + cut-n-paste/Makefile.am | 2 +- + cut-n-paste/unarr/AUTHORS | 12 + + cut-n-paste/unarr/COPYING | 165 +++++ + cut-n-paste/unarr/Makefile.am | 36 + + cut-n-paste/unarr/common/allocator.h | 29 + + cut-n-paste/unarr/common/conv.c | 96 +++ + cut-n-paste/unarr/common/crc32.c | 51 ++ + cut-n-paste/unarr/common/stream.c | 217 ++++++ + cut-n-paste/unarr/common/unarr-imp.h | 81 +++ + cut-n-paste/unarr/common/unarr.c | 109 ++++ + cut-n-paste/unarr/lzmasdk/7zTypes.h | 256 ++++++++ + cut-n-paste/unarr/lzmasdk/CpuArch.c | 190 ++++++ + cut-n-paste/unarr/lzmasdk/CpuArch.h | 157 +++++ + cut-n-paste/unarr/lzmasdk/LzmaDec.c | 1025 +++++++++++++++++++++++++++++ + cut-n-paste/unarr/lzmasdk/LzmaDec.h | 227 +++++++ + cut-n-paste/unarr/lzmasdk/Ppmd.h | 85 +++ + cut-n-paste/unarr/lzmasdk/Ppmd7.c | 710 ++++++++++++++++++++ + cut-n-paste/unarr/lzmasdk/Ppmd7.h | 140 ++++ + cut-n-paste/unarr/lzmasdk/Ppmd7Dec.c | 189 ++++++ + cut-n-paste/unarr/lzmasdk/Ppmd8.c | 1122 ++++++++++++++++++++++++++++++++ + cut-n-paste/unarr/lzmasdk/Ppmd8.h | 137 ++++ + cut-n-paste/unarr/lzmasdk/Ppmd8Dec.c | 157 +++++ + cut-n-paste/unarr/lzmasdk/Precomp.h | 15 + + cut-n-paste/unarr/rar/filter-rar.c | 704 ++++++++++++++++++++ + cut-n-paste/unarr/rar/huffman-rar.c | 142 ++++ + cut-n-paste/unarr/rar/lzss.h | 88 +++ + cut-n-paste/unarr/rar/parse-rar.c | 236 +++++++ + cut-n-paste/unarr/rar/rar.c | 223 +++++++ + cut-n-paste/unarr/rar/rar.h | 252 +++++++ + cut-n-paste/unarr/rar/rarvm.c | 616 ++++++++++++++++++ + cut-n-paste/unarr/rar/rarvm.h | 117 ++++ + cut-n-paste/unarr/rar/uncompress-rar.c | 1038 +++++++++++++++++++++++++++++ + cut-n-paste/unarr/unarr.h | 94 +++ + 34 files changed, 8718 insertions(+), 1 deletion(-) + create mode 100644 cut-n-paste/unarr/AUTHORS + create mode 100644 cut-n-paste/unarr/COPYING + create mode 100644 cut-n-paste/unarr/Makefile.am + create mode 100644 cut-n-paste/unarr/common/allocator.h + create mode 100644 cut-n-paste/unarr/common/conv.c + create mode 100644 cut-n-paste/unarr/common/crc32.c + create mode 100644 cut-n-paste/unarr/common/stream.c + create mode 100644 cut-n-paste/unarr/common/unarr-imp.h + create mode 100644 cut-n-paste/unarr/common/unarr.c + create mode 100644 cut-n-paste/unarr/lzmasdk/7zTypes.h + create mode 100644 cut-n-paste/unarr/lzmasdk/CpuArch.c + create mode 100644 cut-n-paste/unarr/lzmasdk/CpuArch.h + create mode 100644 cut-n-paste/unarr/lzmasdk/LzmaDec.c + create mode 100644 cut-n-paste/unarr/lzmasdk/LzmaDec.h + create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd.h + create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd7.c + create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd7.h + create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd7Dec.c + create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd8.c + create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd8.h + create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd8Dec.c + create mode 100644 cut-n-paste/unarr/lzmasdk/Precomp.h + create mode 100644 cut-n-paste/unarr/rar/filter-rar.c + create mode 100644 cut-n-paste/unarr/rar/huffman-rar.c + create mode 100644 cut-n-paste/unarr/rar/lzss.h + create mode 100644 cut-n-paste/unarr/rar/parse-rar.c + create mode 100644 cut-n-paste/unarr/rar/rar.c + create mode 100644 cut-n-paste/unarr/rar/rar.h + create mode 100644 cut-n-paste/unarr/rar/rarvm.c + create mode 100644 cut-n-paste/unarr/rar/rarvm.h + create mode 100644 cut-n-paste/unarr/rar/uncompress-rar.c + create mode 100644 cut-n-paste/unarr/unarr.h + +diff --git a/configure.ac b/configure.ac +index a5ae8ccd..e7ee360f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -907,6 +907,7 @@ cut-n-paste/Makefile + cut-n-paste/gimpcellrenderertoggle/Makefile + cut-n-paste/synctex/Makefile + cut-n-paste/libgd/Makefile ++cut-n-paste/unarr/Makefile + data/evince.desktop.in + data/evince-previewer.desktop.in + data/Makefile +diff --git a/cut-n-paste/Makefile.am b/cut-n-paste/Makefile.am +index 2d5684d8..a049b7fd 100644 +--- a/cut-n-paste/Makefile.am ++++ b/cut-n-paste/Makefile.am +@@ -1,3 +1,3 @@ +-SUBDIRS = gimpcellrenderertoggle synctex libgd ++SUBDIRS = gimpcellrenderertoggle synctex libgd unarr + + -include $(top_srcdir)/git.mk +diff --git a/cut-n-paste/unarr/AUTHORS b/cut-n-paste/unarr/AUTHORS +new file mode 100644 +index 00000000..4af1be7c +--- /dev/null ++++ b/cut-n-paste/unarr/AUTHORS +@@ -0,0 +1,12 @@ ++unarr contains code by: ++ ++* The Unarchiver project (https://code.google.com/p/theunarchiver/) ++* Simon Bünzli (zeniko at gmail.com, http://www.zeniko.ch/#SumatraPDF) ++ ++Most code is licensed under LGPLv3 (see COPYING). Exceptions are in code ++included from other projects: ++ ++Files License URL ++---------------------------------------------------------------------------------- ++common/crc32.c Public Domain https://gnunet.org/svn/gnunet/src/util/crypto_crc.c ++lzmasdk/*.* Public Domain http://www.7-zip.org/sdk.html +diff --git a/cut-n-paste/unarr/COPYING b/cut-n-paste/unarr/COPYING +new file mode 100644 +index 00000000..65c5ca88 +--- /dev/null ++++ b/cut-n-paste/unarr/COPYING +@@ -0,0 +1,165 @@ ++ GNU LESSER GENERAL PUBLIC LICENSE ++ Version 3, 29 June 2007 ++ ++ Copyright (C) 2007 Free Software Foundation, Inc. ++ Everyone is permitted to copy and distribute verbatim copies ++ of this license document, but changing it is not allowed. ++ ++ ++ This version of the GNU Lesser General Public License incorporates ++the terms and conditions of version 3 of the GNU General Public ++License, supplemented by the additional permissions listed below. ++ ++ 0. Additional Definitions. ++ ++ As used herein, "this License" refers to version 3 of the GNU Lesser ++General Public License, and the "GNU GPL" refers to version 3 of the GNU ++General Public License. ++ ++ "The Library" refers to a covered work governed by this License, ++other than an Application or a Combined Work as defined below. ++ ++ An "Application" is any work that makes use of an interface provided ++by the Library, but which is not otherwise based on the Library. ++Defining a subclass of a class defined by the Library is deemed a mode ++of using an interface provided by the Library. ++ ++ A "Combined Work" is a work produced by combining or linking an ++Application with the Library. The particular version of the Library ++with which the Combined Work was made is also called the "Linked ++Version". ++ ++ The "Minimal Corresponding Source" for a Combined Work means the ++Corresponding Source for the Combined Work, excluding any source code ++for portions of the Combined Work that, considered in isolation, are ++based on the Application, and not on the Linked Version. ++ ++ The "Corresponding Application Code" for a Combined Work means the ++object code and/or source code for the Application, including any data ++and utility programs needed for reproducing the Combined Work from the ++Application, but excluding the System Libraries of the Combined Work. ++ ++ 1. Exception to Section 3 of the GNU GPL. ++ ++ You may convey a covered work under sections 3 and 4 of this License ++without being bound by section 3 of the GNU GPL. ++ ++ 2. Conveying Modified Versions. ++ ++ If you modify a copy of the Library, and, in your modifications, a ++facility refers to a function or data to be supplied by an Application ++that uses the facility (other than as an argument passed when the ++facility is invoked), then you may convey a copy of the modified ++version: ++ ++ a) under this License, provided that you make a good faith effort to ++ ensure that, in the event an Application does not supply the ++ function or data, the facility still operates, and performs ++ whatever part of its purpose remains meaningful, or ++ ++ b) under the GNU GPL, with none of the additional permissions of ++ this License applicable to that copy. ++ ++ 3. Object Code Incorporating Material from Library Header Files. ++ ++ The object code form of an Application may incorporate material from ++a header file that is part of the Library. You may convey such object ++code under terms of your choice, provided that, if the incorporated ++material is not limited to numerical parameters, data structure ++layouts and accessors, or small macros, inline functions and templates ++(ten or fewer lines in length), you do both of the following: ++ ++ a) Give prominent notice with each copy of the object code that the ++ Library is used in it and that the Library and its use are ++ covered by this License. ++ ++ b) Accompany the object code with a copy of the GNU GPL and this license ++ document. ++ ++ 4. Combined Works. ++ ++ You may convey a Combined Work under terms of your choice that, ++taken together, effectively do not restrict modification of the ++portions of the Library contained in the Combined Work and reverse ++engineering for debugging such modifications, if you also do each of ++the following: ++ ++ a) Give prominent notice with each copy of the Combined Work that ++ the Library is used in it and that the Library and its use are ++ covered by this License. ++ ++ b) Accompany the Combined Work with a copy of the GNU GPL and this license ++ document. ++ ++ c) For a Combined Work that displays copyright notices during ++ execution, include the copyright notice for the Library among ++ these notices, as well as a reference directing the user to the ++ copies of the GNU GPL and this license document. ++ ++ d) Do one of the following: ++ ++ 0) Convey the Minimal Corresponding Source under the terms of this ++ License, and the Corresponding Application Code in a form ++ suitable for, and under terms that permit, the user to ++ recombine or relink the Application with a modified version of ++ the Linked Version to produce a modified Combined Work, in the ++ manner specified by section 6 of the GNU GPL for conveying ++ Corresponding Source. ++ ++ 1) Use a suitable shared library mechanism for linking with the ++ Library. A suitable mechanism is one that (a) uses at run time ++ a copy of the Library already present on the user's computer ++ system, and (b) will operate properly with a modified version ++ of the Library that is interface-compatible with the Linked ++ Version. ++ ++ e) Provide Installation Information, but only if you would otherwise ++ be required to provide such information under section 6 of the ++ GNU GPL, and only to the extent that such information is ++ necessary to install and execute a modified version of the ++ Combined Work produced by recombining or relinking the ++ Application with a modified version of the Linked Version. (If ++ you use option 4d0, the Installation Information must accompany ++ the Minimal Corresponding Source and Corresponding Application ++ Code. If you use option 4d1, you must provide the Installation ++ Information in the manner specified by section 6 of the GNU GPL ++ for conveying Corresponding Source.) ++ ++ 5. Combined Libraries. ++ ++ You may place library facilities that are a work based on the ++Library side by side in a single library together with other library ++facilities that are not Applications and are not covered by this ++License, and convey such a combined library under terms of your ++choice, if you do both of the following: ++ ++ a) Accompany the combined library with a copy of the same work based ++ on the Library, uncombined with any other library facilities, ++ conveyed under the terms of this License. ++ ++ b) Give prominent notice with the combined library that part of it ++ is a work based on the Library, and explaining where to find the ++ accompanying uncombined form of the same work. ++ ++ 6. Revised Versions of the GNU Lesser General Public License. ++ ++ The Free Software Foundation may publish revised and/or new versions ++of the GNU Lesser General Public License from time to time. Such new ++versions will be similar in spirit to the present version, but may ++differ in detail to address new problems or concerns. ++ ++ Each version is given a distinguishing version number. If the ++Library as you received it specifies that a certain numbered version ++of the GNU Lesser General Public License "or any later version" ++applies to it, you have the option of following the terms and ++conditions either of that published version or of any later version ++published by the Free Software Foundation. If the Library as you ++received it does not specify a version number of the GNU Lesser ++General Public License, you may choose any version of the GNU Lesser ++General Public License ever published by the Free Software Foundation. ++ ++ If the Library as you received it specifies that a proxy can decide ++whether future versions of the GNU Lesser General Public License shall ++apply, that proxy's public statement of acceptance of any version is ++permanent authorization for you to choose that version for the ++Library. +diff --git a/cut-n-paste/unarr/Makefile.am b/cut-n-paste/unarr/Makefile.am +new file mode 100644 +index 00000000..368efb6a +--- /dev/null ++++ b/cut-n-paste/unarr/Makefile.am +@@ -0,0 +1,36 @@ ++noinst_LTLIBRARIES = libunarr.la ++ ++libunarr_la_SOURCES = \ ++ unarr.h \ ++ rar/filter-rar.c \ ++ rar/lzss.h \ ++ rar/rar.h \ ++ rar/rarvm.h \ ++ rar/huffman-rar.c \ ++ rar/uncompress-rar.c \ ++ rar/rar.c \ ++ rar/rarvm.c \ ++ rar/parse-rar.c \ ++ common/unarr-imp.h \ ++ common/allocator.h \ ++ common/unarr.c \ ++ common/stream.c \ ++ common/conv.c \ ++ common/crc32.c \ ++ lzmasdk/LzmaDec.c \ ++ lzmasdk/Ppmd8.c \ ++ lzmasdk/Precomp.h \ ++ lzmasdk/7zTypes.h \ ++ lzmasdk/Ppmd7.h \ ++ lzmasdk/Ppmd7Dec.c \ ++ lzmasdk/Ppmd8Dec.c \ ++ lzmasdk/CpuArch.h \ ++ lzmasdk/LzmaDec.h \ ++ lzmasdk/CpuArch.c \ ++ lzmasdk/Ppmd8.h \ ++ lzmasdk/Ppmd7.c \ ++ lzmasdk/Ppmd.h ++ ++EXTRA_DIST = COPYING AUTHORS ++ ++-include $(top_srcdir)/git.mk +diff --git a/cut-n-paste/unarr/common/allocator.h b/cut-n-paste/unarr/common/allocator.h +new file mode 100644 +index 00000000..41199c80 +--- /dev/null ++++ b/cut-n-paste/unarr/common/allocator.h +@@ -0,0 +1,29 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++#ifndef common_allocator_h ++#define common_allocator_h ++ ++#ifdef USE_CUSTOM_ALLOCATOR ++ ++#include ++ ++typedef void *(* custom_malloc_fn)(void *opaque, size_t size); ++typedef void (* custom_free_fn)(void *opaque, void *ptr); ++ ++void ar_set_custom_allocator(custom_malloc_fn custom_malloc, custom_free_fn custom_free, void *opaque); ++ ++#define malloc(size) ar_malloc(size) ++#define calloc(count, size) ar_calloc(count, size) ++#define free(ptr) ar_free(ptr) ++ ++#define realloc(ptr, size) _use_malloc_memcpy_free_instead(ptr, size) ++#define strdup(str) _use_malloc_memcpy_instead(str) ++ ++#elif !defined(NDEBUG) && defined(_MSC_VER) ++ ++#include ++ ++#endif ++ ++#endif +diff --git a/cut-n-paste/unarr/common/conv.c b/cut-n-paste/unarr/common/conv.c +new file mode 100644 +index 00000000..4398539b +--- /dev/null ++++ b/cut-n-paste/unarr/common/conv.c +@@ -0,0 +1,96 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++#include "unarr-imp.h" ++ ++#include ++ ++/* data from http://en.wikipedia.org/wiki/Cp437 */ ++static const wchar_t gCp437[256] = { ++ 0, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266C, 0x263C, ++ 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, ++ ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ++ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', ++ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', ++ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', ++ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', ++ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x2302, ++ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, ++ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, ++ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, ++ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, ++ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, ++ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, ++ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, ++ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, ++}; ++ ++size_t ar_conv_rune_to_utf8(wchar_t rune, char *out, size_t size) ++{ ++ if (size < 1) ++ return 0; ++ if (rune < 0x0080) { ++ *out++ = rune & 0x7F; ++ return 1; ++ } ++ if (rune < 0x0800 && size >= 2) { ++ *out++ = 0xC0 | ((rune >> 6) & 0x1F); ++ *out++ = 0x80 | (rune & 0x3F); ++ return 2; ++ } ++ if (size >= 3) { ++ if ((0xD800 <= rune && rune <= 0xDFFF) || rune >= 0x10000) ++ rune = 0xFFFD; ++ *out++ = 0xE0 | ((rune >> 12) & 0x0F); ++ *out++ = 0x80 | ((rune >> 6) & 0x3F); ++ *out++ = 0x80 | (rune & 0x3F); ++ return 3; ++ } ++ *out++ = '?'; ++ return 1; ++} ++ ++char *ar_conv_dos_to_utf8(const char *astr) ++{ ++ char *str, *out; ++ const char *in; ++ size_t size; ++ ++ size = 0; ++ for (in = astr; *in; in++) { ++ char buf[4]; ++ size += ar_conv_rune_to_utf8(gCp437[(uint8_t)*in], buf, sizeof(buf)); ++ } ++ ++ if (size == (size_t)-1) ++ return NULL; ++ str = malloc(size + 1); ++ if (!str) ++ return NULL; ++ ++ for (in = astr, out = str; *in; in++) { ++ out += ar_conv_rune_to_utf8(gCp437[(uint8_t)*in], out, str + size - out); ++ } ++ *out = '\0'; ++ ++ return str; ++} ++ ++time64_t ar_conv_dosdate_to_filetime(uint32_t dosdate) ++{ ++ struct tm tm; ++ time_t t1, t2; ++ ++ tm.tm_sec = (dosdate & 0x1F) * 2; ++ tm.tm_min = (dosdate >> 5) & 0x3F; ++ tm.tm_hour = (dosdate >> 11) & 0x1F; ++ tm.tm_mday = (dosdate >> 16) & 0x1F; ++ tm.tm_mon = ((dosdate >> 21) & 0x0F) - 1; ++ tm.tm_year = ((dosdate >> 25) & 0x7F) + 80; ++ tm.tm_isdst = -1; ++ ++ t1 = mktime(&tm); ++ t2 = mktime(gmtime(&t1)); ++ ++ return (time64_t)(2 * t1 - t2 + 11644473600) * 10000000; ++} +diff --git a/cut-n-paste/unarr/common/crc32.c b/cut-n-paste/unarr/common/crc32.c +new file mode 100644 +index 00000000..b482e6e3 +--- /dev/null ++++ b/cut-n-paste/unarr/common/crc32.c +@@ -0,0 +1,51 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++#include "unarr-imp.h" ++ ++#ifndef HAVE_ZLIB ++ ++/* code adapted from https://gnunet.org/svn/gnunet/src/util/crypto_crc.c (public domain) */ ++ ++static bool crc_table_ready = false; ++static uint32_t crc_table[256]; ++ ++uint32_t ar_crc32(uint32_t crc32, const unsigned char *data, size_t data_len) ++{ ++ if (!crc_table_ready) { ++ uint32_t i, j; ++ uint32_t h = 1; ++ crc_table[0] = 0; ++ for (i = 128; i; i >>= 1) { ++ h = (h >> 1) ^ ((h & 1) ? 0xEDB88320 : 0); ++ for (j = 0; j < 256; j += 2 * i) { ++ crc_table[i + j] = crc_table[j] ^ h; ++ } ++ } ++ crc_table_ready = true; ++ } ++ ++ crc32 = crc32 ^ 0xFFFFFFFF; ++ while (data_len-- > 0) { ++ crc32 = (crc32 >> 8) ^ crc_table[(crc32 ^ *data++) & 0xFF]; ++ } ++ return crc32 ^ 0xFFFFFFFF; ++} ++ ++#else ++ ++#include ++ ++uint32_t ar_crc32(uint32_t crc, const unsigned char *data, size_t data_len) ++{ ++#if SIZE_MAX > UINT32_MAX ++ while (data_len > UINT32_MAX) { ++ crc = crc32(crc, data, UINT32_MAX); ++ data += UINT32_MAX; ++ data_len -= UINT32_MAX; ++ } ++#endif ++ return crc32(crc, data, (uint32_t)data_len); ++} ++ ++#endif +diff --git a/cut-n-paste/unarr/common/stream.c b/cut-n-paste/unarr/common/stream.c +new file mode 100644 +index 00000000..64fe19b3 +--- /dev/null ++++ b/cut-n-paste/unarr/common/stream.c +@@ -0,0 +1,217 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++#include "unarr-imp.h" ++ ++ar_stream *ar_open_stream(void *data, ar_stream_close_fn close, ar_stream_read_fn read, ar_stream_seek_fn seek, ar_stream_tell_fn tell) ++{ ++ ar_stream *stream = malloc(sizeof(ar_stream)); ++ if (!stream) { ++ close(data); ++ return NULL; ++ } ++ stream->data = data; ++ stream->close = close; ++ stream->read = read; ++ stream->seek = seek; ++ stream->tell = tell; ++ return stream; ++} ++ ++void ar_close(ar_stream *stream) ++{ ++ if (stream) ++ stream->close(stream->data); ++ free(stream); ++} ++ ++size_t ar_read(ar_stream *stream, void *buffer, size_t count) ++{ ++ return stream->read(stream->data, buffer, count); ++} ++ ++bool ar_seek(ar_stream *stream, off64_t offset, int origin) ++{ ++ return stream->seek(stream->data, offset, origin); ++} ++ ++bool ar_skip(ar_stream *stream, off64_t count) ++{ ++ return stream->seek(stream->data, count, SEEK_CUR); ++} ++ ++off64_t ar_tell(ar_stream *stream) ++{ ++ return stream->tell(stream->data); ++} ++ ++/***** stream based on FILE *****/ ++ ++static void file_close(void *data) ++{ ++ fclose(data); ++} ++ ++static size_t file_read(void *data, void *buffer, size_t count) ++{ ++ return fread(buffer, 1, count, data); ++} ++ ++static bool file_seek(void *data, off64_t offset, int origin) ++{ ++#ifdef _MSC_VER ++ return _fseeki64(data, offset, origin) == 0; ++#else ++#if _POSIX_C_SOURCE >= 200112L ++ if (sizeof(off_t) == 8) ++ return fseeko(data, offset, origin) == 0; ++#endif ++ if (offset > INT32_MAX || offset < INT32_MIN) ++ return false; ++ return fseek(data, (long)offset, origin) == 0; ++#endif ++} ++ ++static off64_t file_tell(void *data) ++{ ++#ifdef _MSC_VER ++ return _ftelli64(data); ++#elif _POSIX_C_SOURCE >= 200112L ++ return ftello(data); ++#else ++ return ftell(data); ++#endif ++} ++ ++ar_stream *ar_open_file(const char *path) ++{ ++ FILE *f = path ? fopen(path, "rb") : NULL; ++ if (!f) ++ return NULL; ++ return ar_open_stream(f, file_close, file_read, file_seek, file_tell); ++} ++ ++#ifdef _WIN32 ++ar_stream *ar_open_file_w(const wchar_t *path) ++{ ++ FILE *f = path ? _wfopen(path, L"rb") : NULL; ++ if (!f) ++ return NULL; ++ return ar_open_stream(f, file_close, file_read, file_seek, file_tell); ++} ++#endif ++ ++/***** stream based on preallocated memory *****/ ++ ++struct MemoryStream { ++ const uint8_t *data; ++ size_t length; ++ size_t offset; ++}; ++ ++static void memory_close(void *data) ++{ ++ struct MemoryStream *stm = data; ++ free(stm); ++} ++ ++static size_t memory_read(void *data, void *buffer, size_t count) ++{ ++ struct MemoryStream *stm = data; ++ if (count > stm->length - stm->offset) ++ count = stm->length - stm->offset; ++ memcpy(buffer, stm->data + stm->offset, count); ++ stm->offset += count; ++ return count; ++} ++ ++static bool memory_seek(void *data, off64_t offset, int origin) ++{ ++ struct MemoryStream *stm = data; ++ if (origin == SEEK_CUR) ++ offset += stm->offset; ++ else if (origin == SEEK_END) ++ offset += stm->length; ++ if (offset < 0 || offset > (off64_t)stm->length || (size_t)offset > stm->length) ++ return false; ++ stm->offset = (size_t)offset; ++ return true; ++} ++ ++static off64_t memory_tell(void *data) ++{ ++ struct MemoryStream *stm = data; ++ return stm->offset; ++} ++ ++ar_stream *ar_open_memory(const void *data, size_t datalen) ++{ ++ struct MemoryStream *stm = malloc(sizeof(struct MemoryStream)); ++ if (!stm) ++ return NULL; ++ stm->data = data; ++ stm->length = datalen; ++ stm->offset = 0; ++ return ar_open_stream(stm, memory_close, memory_read, memory_seek, memory_tell); ++} ++ ++#ifdef _WIN32 ++/***** stream based on IStream *****/ ++ ++#define COBJMACROS ++#include ++ ++static void stream_close(void *data) ++{ ++ IUnknown_Release((IStream *)data); ++} ++ ++static size_t stream_read(void *data, void *buffer, size_t count) ++{ ++ size_t read = 0; ++ HRESULT res; ++ ULONG cbRead; ++#ifdef _WIN64 ++ while (count > ULONG_MAX) { ++ res = IStream_Read((IStream *)data, buffer, ULONG_MAX, &cbRead); ++ if (FAILED(res)) ++ return read; ++ read += cbRead; ++ buffer = (BYTE *)buffer + ULONG_MAX; ++ count -= ULONG_MAX; ++ } ++#endif ++ res = IStream_Read((IStream *)data, buffer, (ULONG)count, &cbRead); ++ if (SUCCEEDED(res)) ++ read += cbRead; ++ return read; ++} ++ ++static bool stream_seek(void *data, off64_t offset, int origin) ++{ ++ LARGE_INTEGER off; ++ ULARGE_INTEGER n; ++ HRESULT res; ++ off.QuadPart = offset; ++ res = IStream_Seek((IStream *)data, off, origin, &n); ++ return SUCCEEDED(res); ++} ++ ++static off64_t stream_tell(void *data) ++{ ++ LARGE_INTEGER zero = { 0 }; ++ ULARGE_INTEGER n = { 0 }; ++ IStream_Seek((IStream *)data, zero, SEEK_CUR, &n); ++ return (off64_t)n.QuadPart; ++} ++ ++ar_stream *ar_open_istream(IStream *stream) ++{ ++ LARGE_INTEGER zero = { 0 }; ++ HRESULT res = IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL); ++ if (FAILED(res)) ++ return NULL; ++ IUnknown_AddRef(stream); ++ return ar_open_stream(stream, stream_close, stream_read, stream_seek, stream_tell); ++} ++#endif +diff --git a/cut-n-paste/unarr/common/unarr-imp.h b/cut-n-paste/unarr/common/unarr-imp.h +new file mode 100644 +index 00000000..90ad3178 +--- /dev/null ++++ b/cut-n-paste/unarr/common/unarr-imp.h +@@ -0,0 +1,81 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++/* this is the common private/implementation API of unarr which should only be used by unarr code */ ++ ++#ifndef common_unarr_imp_h ++#define common_unarr_imp_h ++ ++#include "../unarr.h" ++#include "allocator.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++/***** conv ****/ ++ ++size_t ar_conv_rune_to_utf8(wchar_t rune, char *out, size_t size); ++char *ar_conv_dos_to_utf8(const char *astr); ++time64_t ar_conv_dosdate_to_filetime(uint32_t dosdate); ++ ++/***** crc32 *****/ ++ ++uint32_t ar_crc32(uint32_t crc32, const unsigned char *data, size_t data_len); ++ ++/***** stream *****/ ++ ++typedef void (* ar_stream_close_fn)(void *data); ++typedef size_t (* ar_stream_read_fn)(void *data, void *buffer, size_t count); ++typedef bool (* ar_stream_seek_fn)(void *data, off64_t offset, int origin); ++typedef off64_t (* ar_stream_tell_fn)(void *data); ++ ++struct ar_stream_s { ++ ar_stream_close_fn close; ++ ar_stream_read_fn read; ++ ar_stream_seek_fn seek; ++ ar_stream_tell_fn tell; ++ void *data; ++}; ++ ++ar_stream *ar_open_stream(void *data, ar_stream_close_fn close, ar_stream_read_fn read, ar_stream_seek_fn seek, ar_stream_tell_fn tell); ++ ++/***** unarr *****/ ++ ++#define warn(...) ar_log("!", __FILE__, __LINE__, __VA_ARGS__) ++#ifndef NDEBUG ++#define log(...) ar_log("-", __FILE__, __LINE__, __VA_ARGS__) ++#else ++#define log(...) ((void)0) ++#endif ++void ar_log(const char *prefix, const char *file, int line, const char *msg, ...); ++ ++typedef void (* ar_archive_close_fn)(ar_archive *ar); ++typedef bool (* ar_parse_entry_fn)(ar_archive *ar, off64_t offset); ++typedef const char *(* ar_entry_get_name_fn)(ar_archive *ar); ++typedef bool (* ar_entry_uncompress_fn)(ar_archive *ar, void *buffer, size_t count); ++typedef size_t (* ar_get_global_comment_fn)(ar_archive *ar, void *buffer, size_t count); ++ ++struct ar_archive_s { ++ ar_archive_close_fn close; ++ ar_parse_entry_fn parse_entry; ++ ar_entry_get_name_fn get_name; ++ ar_entry_uncompress_fn uncompress; ++ ar_get_global_comment_fn get_comment; ++ ++ ar_stream *stream; ++ bool at_eof; ++ off64_t entry_offset; ++ off64_t entry_offset_first; ++ off64_t entry_offset_next; ++ size_t entry_size_uncompressed; ++ time64_t entry_filetime; ++}; ++ ++ar_archive *ar_open_archive(ar_stream *stream, size_t struct_size, ar_archive_close_fn close, ar_parse_entry_fn parse_entry, ++ ar_entry_get_name_fn get_name, ar_entry_uncompress_fn uncompress, ar_get_global_comment_fn get_comment, ++ off64_t first_entry_offset); ++ ++#endif +diff --git a/cut-n-paste/unarr/common/unarr.c b/cut-n-paste/unarr/common/unarr.c +new file mode 100644 +index 00000000..97ec92ae +--- /dev/null ++++ b/cut-n-paste/unarr/common/unarr.c +@@ -0,0 +1,109 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++#include "unarr-imp.h" ++ ++ar_archive *ar_open_archive(ar_stream *stream, size_t struct_size, ar_archive_close_fn close, ar_parse_entry_fn parse_entry, ++ ar_entry_get_name_fn get_name, ar_entry_uncompress_fn uncompress, ar_get_global_comment_fn get_comment, ++ off64_t first_entry_offset) ++{ ++ ar_archive *ar = malloc(struct_size); ++ if (!ar) ++ return NULL; ++ memset(ar, 0, struct_size); ++ ar->close = close; ++ ar->parse_entry = parse_entry; ++ ar->get_name = get_name; ++ ar->uncompress = uncompress; ++ ar->get_comment = get_comment; ++ ar->stream = stream; ++ ar->entry_offset_first = first_entry_offset; ++ ar->entry_offset_next = first_entry_offset; ++ return ar; ++} ++ ++void ar_close_archive(ar_archive *ar) ++{ ++ if (ar) ++ ar->close(ar); ++ free(ar); ++} ++ ++bool ar_at_eof(ar_archive *ar) ++{ ++ return ar->at_eof; ++} ++ ++bool ar_parse_entry(ar_archive *ar) ++{ ++ return ar->parse_entry(ar, ar->entry_offset_next); ++} ++ ++bool ar_parse_entry_at(ar_archive *ar, off64_t offset) ++{ ++ ar->at_eof = false; ++ return ar->parse_entry(ar, offset ? offset : ar->entry_offset_first); ++} ++ ++bool ar_parse_entry_for(ar_archive *ar, const char *entry_name) ++{ ++ ar->at_eof = false; ++ if (!entry_name) ++ return false; ++ if (!ar_parse_entry_at(ar, ar->entry_offset_first)) ++ return false; ++ do { ++ const char *name = ar_entry_get_name(ar); ++ if (name && strcmp(name, entry_name) == 0) ++ return true; ++ } while (ar_parse_entry(ar)); ++ return false; ++} ++ ++const char *ar_entry_get_name(ar_archive *ar) ++{ ++ return ar->get_name(ar); ++} ++ ++off64_t ar_entry_get_offset(ar_archive *ar) ++{ ++ return ar->entry_offset; ++} ++ ++size_t ar_entry_get_size(ar_archive *ar) ++{ ++ return ar->entry_size_uncompressed; ++} ++ ++time64_t ar_entry_get_filetime(ar_archive *ar) ++{ ++ return ar->entry_filetime; ++} ++ ++bool ar_entry_uncompress(ar_archive *ar, void *buffer, size_t count) ++{ ++ return ar->uncompress(ar, buffer, count); ++} ++ ++size_t ar_get_global_comment(ar_archive *ar, void *buffer, size_t count) ++{ ++ if (!ar->get_comment) ++ return 0; ++ return ar->get_comment(ar, buffer, count); ++} ++ ++void ar_log(const char *prefix, const char *file, int line, const char *msg, ...) ++{ ++ va_list args; ++ va_start(args, msg); ++ if (prefix) ++ fprintf(stderr, "%s ", prefix); ++ if (strrchr(file, '/')) ++ file = strrchr(file, '/') + 1; ++ if (strrchr(file, '\\')) ++ file = strrchr(file, '\\') + 1; ++ fprintf(stderr, "%s:%d: ", file, line); ++ vfprintf(stderr, msg, args); ++ fprintf(stderr, "\n"); ++ va_end(args); ++} +diff --git a/cut-n-paste/unarr/lzmasdk/7zTypes.h b/cut-n-paste/unarr/lzmasdk/7zTypes.h +new file mode 100644 +index 00000000..778413ef +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/7zTypes.h +@@ -0,0 +1,256 @@ ++/* 7zTypes.h -- Basic types ++2013-11-12 : Igor Pavlov : Public domain */ ++ ++#ifndef __7Z_TYPES_H ++#define __7Z_TYPES_H ++ ++#ifdef _WIN32 ++/* #include */ ++#endif ++ ++#include ++ ++#ifndef EXTERN_C_BEGIN ++#ifdef __cplusplus ++#define EXTERN_C_BEGIN extern "C" { ++#define EXTERN_C_END } ++#else ++#define EXTERN_C_BEGIN ++#define EXTERN_C_END ++#endif ++#endif ++ ++EXTERN_C_BEGIN ++ ++#define SZ_OK 0 ++ ++#define SZ_ERROR_DATA 1 ++#define SZ_ERROR_MEM 2 ++#define SZ_ERROR_CRC 3 ++#define SZ_ERROR_UNSUPPORTED 4 ++#define SZ_ERROR_PARAM 5 ++#define SZ_ERROR_INPUT_EOF 6 ++#define SZ_ERROR_OUTPUT_EOF 7 ++#define SZ_ERROR_READ 8 ++#define SZ_ERROR_WRITE 9 ++#define SZ_ERROR_PROGRESS 10 ++#define SZ_ERROR_FAIL 11 ++#define SZ_ERROR_THREAD 12 ++ ++#define SZ_ERROR_ARCHIVE 16 ++#define SZ_ERROR_NO_ARCHIVE 17 ++ ++typedef int SRes; ++ ++#ifdef _WIN32 ++/* typedef DWORD WRes; */ ++typedef unsigned WRes; ++#else ++typedef int WRes; ++#endif ++ ++#ifndef RINOK ++#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } ++#endif ++ ++typedef unsigned char Byte; ++typedef short Int16; ++typedef unsigned short UInt16; ++ ++#ifdef _LZMA_UINT32_IS_ULONG ++typedef long Int32; ++typedef unsigned long UInt32; ++#else ++typedef int Int32; ++typedef unsigned int UInt32; ++#endif ++ ++#ifdef _SZ_NO_INT_64 ++ ++/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. ++ NOTES: Some code will work incorrectly in that case! */ ++ ++typedef long Int64; ++typedef unsigned long UInt64; ++ ++#else ++ ++#if defined(_MSC_VER) || defined(__BORLANDC__) ++typedef __int64 Int64; ++typedef unsigned __int64 UInt64; ++#define UINT64_CONST(n) n ++#else ++typedef long long int Int64; ++typedef unsigned long long int UInt64; ++#define UINT64_CONST(n) n ## ULL ++#endif ++ ++#endif ++ ++#ifdef _LZMA_NO_SYSTEM_SIZE_T ++typedef UInt32 SizeT; ++#else ++typedef size_t SizeT; ++#endif ++ ++typedef int Bool; ++#define True 1 ++#define False 0 ++ ++ ++#ifdef _WIN32 ++#define MY_STD_CALL __stdcall ++#else ++#define MY_STD_CALL ++#endif ++ ++#ifdef _MSC_VER ++ ++#if _MSC_VER >= 1300 ++#define MY_NO_INLINE __declspec(noinline) ++#else ++#define MY_NO_INLINE ++#endif ++ ++#define MY_CDECL __cdecl ++#define MY_FAST_CALL __fastcall ++ ++#else ++ ++#define MY_NO_INLINE ++#define MY_CDECL ++#define MY_FAST_CALL ++ ++#endif ++ ++ ++/* The following interfaces use first parameter as pointer to structure */ ++ ++typedef struct ++{ ++ Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ ++} IByteIn; ++ ++typedef struct ++{ ++ void (*Write)(void *p, Byte b); ++} IByteOut; ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) < input(*size)) is allowed */ ++} ISeqInStream; ++ ++/* it can return SZ_ERROR_INPUT_EOF */ ++SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); ++SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); ++SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); ++ ++typedef struct ++{ ++ size_t (*Write)(void *p, const void *buf, size_t size); ++ /* Returns: result - the number of actually written bytes. ++ (result < size) means error */ ++} ISeqOutStream; ++ ++typedef enum ++{ ++ SZ_SEEK_SET = 0, ++ SZ_SEEK_CUR = 1, ++ SZ_SEEK_END = 2 ++} ESzSeek; ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ISeekInStream; ++ ++typedef struct ++{ ++ SRes (*Look)(void *p, const void **buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) > input(*size)) is not allowed ++ (output(*size) < input(*size)) is allowed */ ++ SRes (*Skip)(void *p, size_t offset); ++ /* offset must be <= output(*size) of Look */ ++ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* reads directly (without buffer). It's same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ILookInStream; ++ ++SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); ++SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); ++ ++/* reads via ILookInStream::Read */ ++SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); ++SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); ++ ++#define LookToRead_BUF_SIZE (1 << 14) ++ ++typedef struct ++{ ++ ILookInStream s; ++ ISeekInStream *realStream; ++ size_t pos; ++ size_t size; ++ Byte buf[LookToRead_BUF_SIZE]; ++} CLookToRead; ++ ++void LookToRead_CreateVTable(CLookToRead *p, int lookahead); ++void LookToRead_Init(CLookToRead *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToLook; ++ ++void SecToLook_CreateVTable(CSecToLook *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToRead; ++ ++void SecToRead_CreateVTable(CSecToRead *p); ++ ++typedef struct ++{ ++ SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); ++ /* Returns: result. (result != SZ_OK) means break. ++ Value (UInt64)(Int64)-1 for size means unknown value. */ ++} ICompressProgress; ++ ++typedef struct ++{ ++ void *(*Alloc)(void *p, size_t size); ++ void (*Free)(void *p, void *address); /* address can be 0 */ ++} ISzAlloc; ++ ++#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) ++#define IAlloc_Free(p, a) (p)->Free((p), a) ++ ++#ifdef _WIN32 ++ ++#define CHAR_PATH_SEPARATOR '\\' ++#define WCHAR_PATH_SEPARATOR L'\\' ++#define STRING_PATH_SEPARATOR "\\" ++#define WSTRING_PATH_SEPARATOR L"\\" ++ ++#else ++ ++#define CHAR_PATH_SEPARATOR '/' ++#define WCHAR_PATH_SEPARATOR L'/' ++#define STRING_PATH_SEPARATOR "/" ++#define WSTRING_PATH_SEPARATOR L"/" ++ ++#endif ++ ++EXTERN_C_END ++ ++#endif +diff --git a/cut-n-paste/unarr/lzmasdk/CpuArch.c b/cut-n-paste/unarr/lzmasdk/CpuArch.c +new file mode 100644 +index 00000000..d7f8b1d8 +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/CpuArch.c +@@ -0,0 +1,190 @@ ++/* CpuArch.c -- CPU specific code ++2012-05-29: Igor Pavlov : Public domain */ ++ ++#include "Precomp.h" ++ ++#include "CpuArch.h" ++ ++#ifdef MY_CPU_X86_OR_AMD64 ++ ++#if (defined(_MSC_VER) && !defined(MY_CPU_AMD64)) || defined(__GNUC__) ++#define USE_ASM ++#endif ++ ++#if !defined(USE_ASM) && _MSC_VER >= 1500 ++#include ++#endif ++ ++#if defined(USE_ASM) && !defined(MY_CPU_AMD64) ++static UInt32 CheckFlag(UInt32 flag) ++{ ++ #ifdef _MSC_VER ++ __asm pushfd; ++ __asm pop EAX; ++ __asm mov EDX, EAX; ++ __asm xor EAX, flag; ++ __asm push EAX; ++ __asm popfd; ++ __asm pushfd; ++ __asm pop EAX; ++ __asm xor EAX, EDX; ++ __asm push EDX; ++ __asm popfd; ++ __asm and flag, EAX; ++ #else ++ __asm__ __volatile__ ( ++ "pushf\n\t" ++ "pop %%EAX\n\t" ++ "movl %%EAX,%%EDX\n\t" ++ "xorl %0,%%EAX\n\t" ++ "push %%EAX\n\t" ++ "popf\n\t" ++ "pushf\n\t" ++ "pop %%EAX\n\t" ++ "xorl %%EDX,%%EAX\n\t" ++ "push %%EDX\n\t" ++ "popf\n\t" ++ "andl %%EAX, %0\n\t": ++ "=c" (flag) : "c" (flag)); ++ #endif ++ return flag; ++} ++#define CHECK_CPUID_IS_SUPPORTED if (CheckFlag(1 << 18) == 0 || CheckFlag(1 << 21) == 0) return False; ++#else ++#define CHECK_CPUID_IS_SUPPORTED ++#endif ++ ++static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) ++{ ++ #ifdef USE_ASM ++ ++ #ifdef _MSC_VER ++ ++ UInt32 a2, b2, c2, d2; ++ __asm xor EBX, EBX; ++ __asm xor ECX, ECX; ++ __asm xor EDX, EDX; ++ __asm mov EAX, function; ++ __asm cpuid; ++ __asm mov a2, EAX; ++ __asm mov b2, EBX; ++ __asm mov c2, ECX; ++ __asm mov d2, EDX; ++ ++ *a = a2; ++ *b = b2; ++ *c = c2; ++ *d = d2; ++ ++ #else ++ ++ __asm__ __volatile__ ( ++ #if defined(MY_CPU_X86) && defined(__PIC__) ++ "mov %%ebx, %%edi;" ++ "cpuid;" ++ "xchgl %%ebx, %%edi;" ++ : "=a" (*a) , ++ "=D" (*b) , ++ #else ++ "cpuid" ++ : "=a" (*a) , ++ "=b" (*b) , ++ #endif ++ "=c" (*c) , ++ "=d" (*d) ++ : "0" (function)) ; ++ ++ #endif ++ ++ #else ++ ++ int CPUInfo[4]; ++ __cpuid(CPUInfo, function); ++ *a = CPUInfo[0]; ++ *b = CPUInfo[1]; ++ *c = CPUInfo[2]; ++ *d = CPUInfo[3]; ++ ++ #endif ++} ++ ++Bool x86cpuid_CheckAndRead(Cx86cpuid *p) ++{ ++ CHECK_CPUID_IS_SUPPORTED ++ MyCPUID(0, &p->maxFunc, &p->vendor[0], &p->vendor[2], &p->vendor[1]); ++ MyCPUID(1, &p->ver, &p->b, &p->c, &p->d); ++ return True; ++} ++ ++static UInt32 kVendors[][3] = ++{ ++ { 0x756E6547, 0x49656E69, 0x6C65746E}, ++ { 0x68747541, 0x69746E65, 0x444D4163}, ++ { 0x746E6543, 0x48727561, 0x736C7561} ++}; ++ ++int x86cpuid_GetFirm(const Cx86cpuid *p) ++{ ++ unsigned i; ++ for (i = 0; i < sizeof(kVendors) / sizeof(kVendors[i]); i++) ++ { ++ const UInt32 *v = kVendors[i]; ++ if (v[0] == p->vendor[0] && ++ v[1] == p->vendor[1] && ++ v[2] == p->vendor[2]) ++ return (int)i; ++ } ++ return -1; ++} ++ ++Bool CPU_Is_InOrder() ++{ ++ Cx86cpuid p; ++ int firm; ++ UInt32 family, model; ++ if (!x86cpuid_CheckAndRead(&p)) ++ return True; ++ family = x86cpuid_GetFamily(&p); ++ model = x86cpuid_GetModel(&p); ++ firm = x86cpuid_GetFirm(&p); ++ switch (firm) ++ { ++ case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && ( ++ /* Atom CPU */ ++ model == 0x100C /* 45 nm, N4xx, D4xx, N5xx, D5xx, 230, 330 */ ++ || model == 0x2006 /* 45 nm, Z6xx */ ++ || model == 0x2007 /* 32 nm, Z2460 */ ++ || model == 0x3005 /* 32 nm, Z2760 */ ++ || model == 0x3006 /* 32 nm, N2xxx, D2xxx */ ++ ))); ++ case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA))); ++ case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF)); ++ } ++ return True; ++} ++ ++#if !defined(MY_CPU_AMD64) && defined(_WIN32) ++#include ++static Bool CPU_Sys_Is_SSE_Supported() ++{ ++ OSVERSIONINFO vi; ++ vi.dwOSVersionInfoSize = sizeof(vi); ++ if (!GetVersionEx(&vi)) ++ return False; ++ return (vi.dwMajorVersion >= 5); ++} ++#define CHECK_SYS_SSE_SUPPORT if (!CPU_Sys_Is_SSE_Supported()) return False; ++#else ++#define CHECK_SYS_SSE_SUPPORT ++#endif ++ ++Bool CPU_Is_Aes_Supported() ++{ ++ Cx86cpuid p; ++ CHECK_SYS_SSE_SUPPORT ++ if (!x86cpuid_CheckAndRead(&p)) ++ return False; ++ return (p.c >> 25) & 1; ++} ++ ++#endif +diff --git a/cut-n-paste/unarr/lzmasdk/CpuArch.h b/cut-n-paste/unarr/lzmasdk/CpuArch.h +new file mode 100644 +index 00000000..4fee0093 +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/CpuArch.h +@@ -0,0 +1,157 @@ ++/* CpuArch.h -- CPU specific code ++2013-11-12: Igor Pavlov : Public domain */ ++ ++#ifndef __CPU_ARCH_H ++#define __CPU_ARCH_H ++ ++#include "7zTypes.h" ++ ++EXTERN_C_BEGIN ++ ++/* ++MY_CPU_LE means that CPU is LITTLE ENDIAN. ++If MY_CPU_LE is not defined, we don't know about that property of platform (it can be LITTLE ENDIAN). ++ ++MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. ++If MY_CPU_LE_UNALIGN is not defined, we don't know about these properties of platform. ++*/ ++ ++#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) ++#define MY_CPU_AMD64 ++#endif ++ ++#if defined(MY_CPU_AMD64) || defined(_M_IA64) ++#define MY_CPU_64BIT ++#endif ++ ++#if defined(_M_IX86) || defined(__i386__) ++#define MY_CPU_X86 ++#endif ++ ++#if defined(MY_CPU_X86) || defined(MY_CPU_AMD64) ++#define MY_CPU_X86_OR_AMD64 ++#endif ++ ++#if defined(MY_CPU_X86) || defined(_M_ARM) ++#define MY_CPU_32BIT ++#endif ++ ++#if defined(_WIN32) && defined(_M_ARM) ++#define MY_CPU_ARM_LE ++#endif ++ ++#if defined(_WIN32) && defined(_M_IA64) ++#define MY_CPU_IA64_LE ++#endif ++ ++#if defined(MY_CPU_X86_OR_AMD64) ++#define MY_CPU_LE_UNALIGN ++#endif ++ ++#if defined(MY_CPU_X86_OR_AMD64) || defined(MY_CPU_ARM_LE) || defined(MY_CPU_IA64_LE) || defined(__ARMEL__) || defined(__MIPSEL__) || defined(__LITTLE_ENDIAN__) ++#define MY_CPU_LE ++#endif ++ ++#if defined(__BIG_ENDIAN__) || defined(__m68k__) || defined(__ARMEB__) || defined(__MIPSEB__) ++#define MY_CPU_BE ++#endif ++ ++#if defined(MY_CPU_LE) && defined(MY_CPU_BE) ++Stop_Compiling_Bad_Endian ++#endif ++ ++#ifdef MY_CPU_LE_UNALIGN ++ ++#define GetUi16(p) (*(const UInt16 *)(const void *)(p)) ++#define GetUi32(p) (*(const UInt32 *)(const void *)(p)) ++#define GetUi64(p) (*(const UInt64 *)(const void *)(p)) ++#define SetUi16(p, d) *(UInt16 *)(p) = (d); ++#define SetUi32(p, d) *(UInt32 *)(p) = (d); ++#define SetUi64(p, d) *(UInt64 *)(p) = (d); ++ ++#else ++ ++#define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8)) ++ ++#define GetUi32(p) ( \ ++ ((const Byte *)(p))[0] | \ ++ ((UInt32)((const Byte *)(p))[1] << 8) | \ ++ ((UInt32)((const Byte *)(p))[2] << 16) | \ ++ ((UInt32)((const Byte *)(p))[3] << 24)) ++ ++#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) ++ ++#define SetUi16(p, d) { UInt32 _x_ = (d); \ ++ ((Byte *)(p))[0] = (Byte)_x_; \ ++ ((Byte *)(p))[1] = (Byte)(_x_ >> 8); } ++ ++#define SetUi32(p, d) { UInt32 _x_ = (d); \ ++ ((Byte *)(p))[0] = (Byte)_x_; \ ++ ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \ ++ ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \ ++ ((Byte *)(p))[3] = (Byte)(_x_ >> 24); } ++ ++#define SetUi64(p, d) { UInt64 _x64_ = (d); \ ++ SetUi32(p, (UInt32)_x64_); \ ++ SetUi32(((Byte *)(p)) + 4, (UInt32)(_x64_ >> 32)); } ++ ++#endif ++ ++#if defined(MY_CPU_LE_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300) ++ ++#include ++ ++#pragma intrinsic(_byteswap_ulong) ++#pragma intrinsic(_byteswap_uint64) ++#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) ++#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) ++ ++#else ++ ++#define GetBe32(p) ( \ ++ ((UInt32)((const Byte *)(p))[0] << 24) | \ ++ ((UInt32)((const Byte *)(p))[1] << 16) | \ ++ ((UInt32)((const Byte *)(p))[2] << 8) | \ ++ ((const Byte *)(p))[3] ) ++ ++#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) ++ ++#endif ++ ++#define GetBe16(p) ((UInt16)(((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1])) ++ ++ ++#ifdef MY_CPU_X86_OR_AMD64 ++ ++typedef struct ++{ ++ UInt32 maxFunc; ++ UInt32 vendor[3]; ++ UInt32 ver; ++ UInt32 b; ++ UInt32 c; ++ UInt32 d; ++} Cx86cpuid; ++ ++enum ++{ ++ CPU_FIRM_INTEL, ++ CPU_FIRM_AMD, ++ CPU_FIRM_VIA ++}; ++ ++Bool x86cpuid_CheckAndRead(Cx86cpuid *p); ++int x86cpuid_GetFirm(const Cx86cpuid *p); ++ ++#define x86cpuid_GetFamily(p) (((p)->ver >> 8) & 0xFF00F) ++#define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F) ++#define x86cpuid_GetStepping(p) ((p)->ver & 0xF) ++ ++Bool CPU_Is_InOrder(); ++Bool CPU_Is_Aes_Supported(); ++ ++#endif ++ ++EXTERN_C_END ++ ++#endif +diff --git a/cut-n-paste/unarr/lzmasdk/LzmaDec.c b/cut-n-paste/unarr/lzmasdk/LzmaDec.c +new file mode 100644 +index 00000000..bbf650de +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/LzmaDec.c +@@ -0,0 +1,1025 @@ ++/* LzmaDec.c -- LZMA Decoder ++2015-01-01 : Igor Pavlov : Public domain */ ++ ++#include "Precomp.h" ++ ++#include "LzmaDec.h" ++ ++#include ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++ ++#define RC_INIT_SIZE 5 ++ ++#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); ++#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); ++#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ ++ { UPDATE_0(p); i = (i + i); A0; } else \ ++ { UPDATE_1(p); i = (i + i) + 1; A1; } ++#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) ++ ++#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } ++#define TREE_DECODE(probs, limit, i) \ ++ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } ++ ++/* #define _LZMA_SIZE_OPT */ ++ ++#ifdef _LZMA_SIZE_OPT ++#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) ++#else ++#define TREE_6_DECODE(probs, i) \ ++ { i = 1; \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ i -= 0x40; } ++#endif ++ ++#define NORMAL_LITER_DEC GET_BIT(prob + symbol, symbol) ++#define MATCHED_LITER_DEC \ ++ matchByte <<= 1; \ ++ bit = (matchByte & offs); \ ++ probLit = prob + offs + bit + symbol; \ ++ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) ++ ++#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0_CHECK range = bound; ++#define UPDATE_1_CHECK range -= bound; code -= bound; ++#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ ++ { UPDATE_0_CHECK; i = (i + i); A0; } else \ ++ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } ++#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) ++#define TREE_DECODE_CHECK(probs, limit, i) \ ++ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } ++ ++ ++#define kNumPosBitsMax 4 ++#define kNumPosStatesMax (1 << kNumPosBitsMax) ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define LenChoice 0 ++#define LenChoice2 (LenChoice + 1) ++#define LenLow (LenChoice2 + 1) ++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) ++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) ++#define kNumLenProbs (LenHigh + kLenNumHighSymbols) ++ ++ ++#define kNumStates 12 ++#define kNumLitStates 7 ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#define kNumPosSlotBits 6 ++#define kNumLenToPosStates 4 ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++ ++#define kMatchMinLen 2 ++#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) ++ ++#define IsMatch 0 ++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) ++#define IsRepG0 (IsRep + kNumStates) ++#define IsRepG1 (IsRepG0 + kNumStates) ++#define IsRepG2 (IsRepG1 + kNumStates) ++#define IsRep0Long (IsRepG2 + kNumStates) ++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) ++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) ++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) ++#define LenCoder (Align + kAlignTableSize) ++#define RepLenCoder (LenCoder + kNumLenProbs) ++#define Literal (RepLenCoder + kNumLenProbs) ++ ++#define LZMA_BASE_SIZE 1846 ++#define LZMA_LIT_SIZE 768 ++ ++#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) ++ ++#if Literal != LZMA_BASE_SIZE ++StopCompilingDueBUG ++#endif ++ ++#define LZMA_DIC_MIN (1 << 12) ++ ++/* First LZMA-symbol is always decoded. ++And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization ++Out: ++ Result: ++ SZ_OK - OK ++ SZ_ERROR_DATA - Error ++ p->remainLen: ++ < kMatchSpecLenStart : normal remain ++ = kMatchSpecLenStart : finished ++ = kMatchSpecLenStart + 1 : Flush marker ++ = kMatchSpecLenStart + 2 : State Init Marker ++*/ ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ CLzmaProb *probs = p->probs; ++ ++ unsigned state = p->state; ++ UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; ++ unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; ++ unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; ++ unsigned lc = p->prop.lc; ++ ++ Byte *dic = p->dic; ++ SizeT dicBufSize = p->dicBufSize; ++ SizeT dicPos = p->dicPos; ++ ++ UInt32 processedPos = p->processedPos; ++ UInt32 checkDicSize = p->checkDicSize; ++ unsigned len = 0; ++ ++ const Byte *buf = p->buf; ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ ++ do ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = processedPos & pbMask; ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ unsigned symbol; ++ UPDATE_0(prob); ++ prob = probs + Literal; ++ if (checkDicSize != 0 || processedPos != 0) ++ prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + ++ (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ state -= (state < 4) ? state : 3; ++ symbol = 1; ++ #ifdef _LZMA_SIZE_OPT ++ do { NORMAL_LITER_DEC } while (symbol < 0x100); ++ #else ++ NORMAL_LITER_DEC ++ NORMAL_LITER_DEC ++ NORMAL_LITER_DEC ++ NORMAL_LITER_DEC ++ NORMAL_LITER_DEC ++ NORMAL_LITER_DEC ++ NORMAL_LITER_DEC ++ NORMAL_LITER_DEC ++ #endif ++ } ++ else ++ { ++ unsigned matchByte = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ state -= (state < 10) ? 3 : 6; ++ symbol = 1; ++ #ifdef _LZMA_SIZE_OPT ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ MATCHED_LITER_DEC ++ } ++ while (symbol < 0x100); ++ #else ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ MATCHED_LITER_DEC ++ MATCHED_LITER_DEC ++ MATCHED_LITER_DEC ++ MATCHED_LITER_DEC ++ MATCHED_LITER_DEC ++ MATCHED_LITER_DEC ++ MATCHED_LITER_DEC ++ MATCHED_LITER_DEC ++ } ++ #endif ++ } ++ dic[dicPos++] = (Byte)symbol; ++ processedPos++; ++ continue; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRep + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ state += kNumStates; ++ prob = probs + LenCoder; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ if (checkDicSize == 0 && processedPos == 0) ++ return SZ_ERROR_DATA; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ processedPos++; ++ state = state < kNumLitStates ? 9 : 11; ++ continue; ++ } ++ UPDATE_1(prob); ++ } ++ else ++ { ++ UInt32 distance; ++ UPDATE_1(prob); ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep1; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep2; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ distance = rep3; ++ rep3 = rep2; ++ } ++ rep2 = rep1; ++ } ++ rep1 = rep0; ++ rep0 = distance; ++ } ++ state = state < kNumLitStates ? 8 : 11; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = (1 << kLenNumLowBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenChoice2; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = (1 << kLenNumMidBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = (1 << kLenNumHighBits); ++ } ++ } ++ TREE_DECODE(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state >= kNumStates) ++ { ++ UInt32 distance; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); ++ TREE_6_DECODE(prob, distance); ++ if (distance >= kStartPosModelIndex) ++ { ++ unsigned posSlot = (unsigned)distance; ++ int numDirectBits = (int)(((distance >> 1) - 1)); ++ distance = (2 | (distance & 1)); ++ if (posSlot < kEndPosModelIndex) ++ { ++ distance <<= numDirectBits; ++ prob = probs + SpecPos + distance - posSlot - 1; ++ { ++ UInt32 mask = 1; ++ unsigned i = 1; ++ do ++ { ++ GET_BIT2(prob + i, i, ; , distance |= mask); ++ mask <<= 1; ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE ++ range >>= 1; ++ ++ { ++ UInt32 t; ++ code -= range; ++ t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ ++ distance = (distance << 1) + (t + 1); ++ code += range & t; ++ } ++ /* ++ distance <<= 1; ++ if (code >= range) ++ { ++ code -= range; ++ distance |= 1; ++ } ++ */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ distance <<= kNumAlignBits; ++ { ++ unsigned i = 1; ++ GET_BIT2(prob + i, i, ; , distance |= 1); ++ GET_BIT2(prob + i, i, ; , distance |= 2); ++ GET_BIT2(prob + i, i, ; , distance |= 4); ++ GET_BIT2(prob + i, i, ; , distance |= 8); ++ } ++ if (distance == (UInt32)0xFFFFFFFF) ++ { ++ len += kMatchSpecLenStart; ++ state -= kNumStates; ++ break; ++ } ++ } ++ } ++ rep3 = rep2; ++ rep2 = rep1; ++ rep1 = rep0; ++ rep0 = distance + 1; ++ if (checkDicSize == 0) ++ { ++ if (distance >= processedPos) ++ return SZ_ERROR_DATA; ++ } ++ else if (distance >= checkDicSize) ++ return SZ_ERROR_DATA; ++ state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; ++ } ++ ++ len += kMatchMinLen; ++ ++ if (limit == dicPos) ++ return SZ_ERROR_DATA; ++ { ++ SizeT rem = limit - dicPos; ++ unsigned curLen = ((rem < len) ? (unsigned)rem : len); ++ SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); ++ ++ processedPos += curLen; ++ ++ len -= curLen; ++ if (pos + curLen <= dicBufSize) ++ { ++ Byte *dest = dic + dicPos; ++ ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; ++ const Byte *lim = dest + curLen; ++ dicPos += curLen; ++ do ++ *(dest) = (Byte)*(dest + src); ++ while (++dest != lim); ++ } ++ else ++ { ++ do ++ { ++ dic[dicPos++] = dic[pos]; ++ if (++pos == dicBufSize) ++ pos = 0; ++ } ++ while (--curLen != 0); ++ } ++ } ++ } ++ } ++ while (dicPos < limit && buf < bufLimit); ++ NORMALIZE; ++ p->buf = buf; ++ p->range = range; ++ p->code = code; ++ p->remainLen = len; ++ p->dicPos = dicPos; ++ p->processedPos = processedPos; ++ p->reps[0] = rep0; ++ p->reps[1] = rep1; ++ p->reps[2] = rep2; ++ p->reps[3] = rep3; ++ p->state = state; ++ ++ return SZ_OK; ++} ++ ++static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) ++{ ++ if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) ++ { ++ Byte *dic = p->dic; ++ SizeT dicPos = p->dicPos; ++ SizeT dicBufSize = p->dicBufSize; ++ unsigned len = p->remainLen; ++ UInt32 rep0 = p->reps[0]; ++ if (limit - dicPos < len) ++ len = (unsigned)(limit - dicPos); ++ ++ if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) ++ p->checkDicSize = p->prop.dicSize; ++ ++ p->processedPos += len; ++ p->remainLen -= len; ++ while (len != 0) ++ { ++ len--; ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ } ++ p->dicPos = dicPos; ++ } ++} ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ do ++ { ++ SizeT limit2 = limit; ++ if (p->checkDicSize == 0) ++ { ++ UInt32 rem = p->prop.dicSize - p->processedPos; ++ if (limit - p->dicPos > rem) ++ limit2 = p->dicPos + rem; ++ } ++ RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); ++ if (p->processedPos >= p->prop.dicSize) ++ p->checkDicSize = p->prop.dicSize; ++ LzmaDec_WriteRem(p, limit); ++ } ++ while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); ++ ++ if (p->remainLen > kMatchSpecLenStart) ++ { ++ p->remainLen = kMatchSpecLenStart; ++ } ++ return 0; ++} ++ ++typedef enum ++{ ++ DUMMY_ERROR, /* unexpected end of input stream */ ++ DUMMY_LIT, ++ DUMMY_MATCH, ++ DUMMY_REP ++} ELzmaDummy; ++ ++static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) ++{ ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ const Byte *bufLimit = buf + inSize; ++ CLzmaProb *probs = p->probs; ++ unsigned state = p->state; ++ ELzmaDummy res; ++ ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK ++ ++ /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ ++ ++ prob = probs + Literal; ++ if (p->checkDicSize != 0 || p->processedPos != 0) ++ prob += (LZMA_LIT_SIZE * ++ ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + ++ (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ unsigned symbol = 1; ++ do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); ++ } ++ else ++ { ++ unsigned matchByte = p->dic[p->dicPos - p->reps[0] + ++ ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ unsigned symbol = 1; ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ matchByte <<= 1; ++ bit = (matchByte & offs); ++ probLit = prob + offs + bit + symbol; ++ GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) ++ } ++ while (symbol < 0x100); ++ } ++ res = DUMMY_LIT; ++ } ++ else ++ { ++ unsigned len; ++ UPDATE_1_CHECK; ++ ++ prob = probs + IsRep + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ state = 0; ++ prob = probs + LenCoder; ++ res = DUMMY_MATCH; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ res = DUMMY_REP; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ NORMALIZE_CHECK; ++ return DUMMY_REP; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ } ++ state = kNumStates; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = 1 << kLenNumLowBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenChoice2; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = 1 << kLenNumMidBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = 1 << kLenNumHighBits; ++ } ++ } ++ TREE_DECODE_CHECK(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state < 4) ++ { ++ unsigned posSlot; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << ++ kNumPosSlotBits); ++ TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); ++ if (posSlot >= kStartPosModelIndex) ++ { ++ int numDirectBits = ((posSlot >> 1) - 1); ++ ++ /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ ++ ++ if (posSlot < kEndPosModelIndex) ++ { ++ prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE_CHECK ++ range >>= 1; ++ code -= range & (((code - range) >> 31) - 1); ++ /* if (code >= range) code -= range; */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ numDirectBits = kNumAlignBits; ++ } ++ { ++ unsigned i = 1; ++ do ++ { ++ GET_BIT_CHECK(prob + i, i); ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ } ++ } ++ } ++ NORMALIZE_CHECK; ++ return res; ++} ++ ++ ++static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) ++{ ++ p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); ++ p->range = 0xFFFFFFFF; ++ p->needFlush = 0; ++} ++ ++void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) ++{ ++ p->needFlush = 1; ++ p->remainLen = 0; ++ p->tempBufSize = 0; ++ ++ if (initDic) ++ { ++ p->processedPos = 0; ++ p->checkDicSize = 0; ++ p->needInitState = 1; ++ } ++ if (initState) ++ p->needInitState = 1; ++} ++ ++void LzmaDec_Init(CLzmaDec *p) ++{ ++ p->dicPos = 0; ++ LzmaDec_InitDicAndState(p, True, True); ++} ++ ++static void LzmaDec_InitStateReal(CLzmaDec *p) ++{ ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); ++ UInt32 i; ++ CLzmaProb *probs = p->probs; ++ for (i = 0; i < numProbs; i++) ++ probs[i] = kBitModelTotal >> 1; ++ p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; ++ p->state = 0; ++ p->needInitState = 0; ++} ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ++ ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT inSize = *srcLen; ++ (*srcLen) = 0; ++ LzmaDec_WriteRem(p, dicLimit); ++ ++ *status = LZMA_STATUS_NOT_SPECIFIED; ++ ++ while (p->remainLen != kMatchSpecLenStart) ++ { ++ int checkEndMarkNow; ++ ++ if (p->needFlush != 0) ++ { ++ for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) ++ p->tempBuf[p->tempBufSize++] = *src++; ++ if (p->tempBufSize < RC_INIT_SIZE) ++ { ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (p->tempBuf[0] != 0) ++ return SZ_ERROR_DATA; ++ ++ LzmaDec_InitRc(p, p->tempBuf); ++ p->tempBufSize = 0; ++ } ++ ++ checkEndMarkNow = 0; ++ if (p->dicPos >= dicLimit) ++ { ++ if (p->remainLen == 0 && p->code == 0) ++ { ++ *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; ++ return SZ_OK; ++ } ++ if (finishMode == LZMA_FINISH_ANY) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_OK; ++ } ++ if (p->remainLen != 0) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ checkEndMarkNow = 1; ++ } ++ ++ if (p->needInitState) ++ LzmaDec_InitStateReal(p); ++ ++ if (p->tempBufSize == 0) ++ { ++ SizeT processed; ++ const Byte *bufLimit; ++ if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, src, inSize); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ memcpy(p->tempBuf, src, inSize); ++ p->tempBufSize = (unsigned)inSize; ++ (*srcLen) += inSize; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ bufLimit = src; ++ } ++ else ++ bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; ++ p->buf = src; ++ if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) ++ return SZ_ERROR_DATA; ++ processed = (SizeT)(p->buf - src); ++ (*srcLen) += processed; ++ src += processed; ++ inSize -= processed; ++ } ++ else ++ { ++ unsigned rem = p->tempBufSize, lookAhead = 0; ++ while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) ++ p->tempBuf[rem++] = src[lookAhead++]; ++ p->tempBufSize = rem; ++ if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ (*srcLen) += lookAhead; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ } ++ p->buf = p->tempBuf; ++ if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) ++ return SZ_ERROR_DATA; ++ lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); ++ (*srcLen) += lookAhead; ++ src += lookAhead; ++ inSize -= lookAhead; ++ p->tempBufSize = 0; ++ } ++ } ++ if (p->code == 0) ++ *status = LZMA_STATUS_FINISHED_WITH_MARK; ++ return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; ++} ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT outSize = *destLen; ++ SizeT inSize = *srcLen; ++ *srcLen = *destLen = 0; ++ for (;;) ++ { ++ SizeT inSizeCur = inSize, outSizeCur, dicPos; ++ ELzmaFinishMode curFinishMode; ++ SRes res; ++ if (p->dicPos == p->dicBufSize) ++ p->dicPos = 0; ++ dicPos = p->dicPos; ++ if (outSize > p->dicBufSize - dicPos) ++ { ++ outSizeCur = p->dicBufSize; ++ curFinishMode = LZMA_FINISH_ANY; ++ } ++ else ++ { ++ outSizeCur = dicPos + outSize; ++ curFinishMode = finishMode; ++ } ++ ++ res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); ++ src += inSizeCur; ++ inSize -= inSizeCur; ++ *srcLen += inSizeCur; ++ outSizeCur = p->dicPos - dicPos; ++ memcpy(dest, p->dic + dicPos, outSizeCur); ++ dest += outSizeCur; ++ outSize -= outSizeCur; ++ *destLen += outSizeCur; ++ if (res != 0) ++ return res; ++ if (outSizeCur == 0 || outSize == 0) ++ return SZ_OK; ++ } ++} ++ ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->probs); ++ p->probs = 0; ++} ++ ++static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->dic); ++ p->dic = 0; ++} ++ ++void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ LzmaDec_FreeProbs(p, alloc); ++ LzmaDec_FreeDict(p, alloc); ++} ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) ++{ ++ UInt32 dicSize; ++ Byte d; ++ ++ if (size < LZMA_PROPS_SIZE) ++ return SZ_ERROR_UNSUPPORTED; ++ else ++ dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); ++ ++ if (dicSize < LZMA_DIC_MIN) ++ dicSize = LZMA_DIC_MIN; ++ p->dicSize = dicSize; ++ ++ d = data[0]; ++ if (d >= (9 * 5 * 5)) ++ return SZ_ERROR_UNSUPPORTED; ++ ++ p->lc = d % 9; ++ d /= 9; ++ p->pb = d / 5; ++ p->lp = d % 5; ++ ++ return SZ_OK; ++} ++ ++static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) ++{ ++ UInt32 numProbs = LzmaProps_GetNumProbs(propNew); ++ if (p->probs == 0 || numProbs != p->numProbs) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); ++ p->numProbs = numProbs; ++ if (p->probs == 0) ++ return SZ_ERROR_MEM; ++ } ++ return SZ_OK; ++} ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ SizeT dicBufSize; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ dicBufSize = propNew.dicSize; ++ if (p->dic == 0 || dicBufSize != p->dicBufSize) ++ { ++ LzmaDec_FreeDict(p, alloc); ++ p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); ++ if (p->dic == 0) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ return SZ_ERROR_MEM; ++ } ++ } ++ p->dicBufSize = dicBufSize; ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc) ++{ ++ CLzmaDec p; ++ SRes res; ++ SizeT outSize = *destLen, inSize = *srcLen; ++ *destLen = *srcLen = 0; ++ *status = LZMA_STATUS_NOT_SPECIFIED; ++ if (inSize < RC_INIT_SIZE) ++ return SZ_ERROR_INPUT_EOF; ++ LzmaDec_Construct(&p); ++ RINOK(LzmaDec_AllocateProbs(&p, propData, propSize, alloc)); ++ p.dic = dest; ++ p.dicBufSize = outSize; ++ LzmaDec_Init(&p); ++ *srcLen = inSize; ++ res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); ++ *destLen = p.dicPos; ++ if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) ++ res = SZ_ERROR_INPUT_EOF; ++ LzmaDec_FreeProbs(&p, alloc); ++ return res; ++} +diff --git a/cut-n-paste/unarr/lzmasdk/LzmaDec.h b/cut-n-paste/unarr/lzmasdk/LzmaDec.h +new file mode 100644 +index 00000000..cc44daef +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/LzmaDec.h +@@ -0,0 +1,227 @@ ++/* LzmaDec.h -- LZMA Decoder ++2013-01-18 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_DEC_H ++#define __LZMA_DEC_H ++ ++#include "7zTypes.h" ++ ++EXTERN_C_BEGIN ++ ++/* #define _LZMA_PROB32 */ ++/* _LZMA_PROB32 can increase the speed on some CPUs, ++ but memory usage for CLzmaDec::probs will be doubled in that case */ ++ ++#ifdef _LZMA_PROB32 ++#define CLzmaProb UInt32 ++#else ++#define CLzmaProb UInt16 ++#endif ++ ++ ++/* ---------- LZMA Properties ---------- */ ++ ++#define LZMA_PROPS_SIZE 5 ++ ++typedef struct _CLzmaProps ++{ ++ unsigned lc, lp, pb; ++ UInt32 dicSize; ++} CLzmaProps; ++ ++/* LzmaProps_Decode - decodes properties ++Returns: ++ SZ_OK ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); ++ ++ ++/* ---------- LZMA Decoder state ---------- */ ++ ++/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. ++ Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ ++ ++#define LZMA_REQUIRED_INPUT_MAX 20 ++ ++typedef struct ++{ ++ CLzmaProps prop; ++ CLzmaProb *probs; ++ Byte *dic; ++ const Byte *buf; ++ UInt32 range, code; ++ SizeT dicPos; ++ SizeT dicBufSize; ++ UInt32 processedPos; ++ UInt32 checkDicSize; ++ unsigned state; ++ UInt32 reps[4]; ++ unsigned remainLen; ++ int needFlush; ++ int needInitState; ++ UInt32 numProbs; ++ unsigned tempBufSize; ++ Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; ++} CLzmaDec; ++ ++#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } ++ ++void LzmaDec_Init(CLzmaDec *p); ++ ++/* There are two types of LZMA streams: ++ 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. ++ 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ ++ ++typedef enum ++{ ++ LZMA_FINISH_ANY, /* finish at any point */ ++ LZMA_FINISH_END /* block must be finished at the end */ ++} ELzmaFinishMode; ++ ++/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! ++ ++ You must use LZMA_FINISH_END, when you know that current output buffer ++ covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. ++ ++ If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, ++ and output value of destLen will be less than output buffer size limit. ++ You can check status result also. ++ ++ You can use multiple checks to test data integrity after full decompression: ++ 1) Check Result and "status" variable. ++ 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. ++ 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. ++ You must use correct finish mode in that case. */ ++ ++typedef enum ++{ ++ LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ ++ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ ++ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ ++ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ ++} ELzmaStatus; ++ ++/* ELzmaStatus is used only as output value for function call */ ++ ++ ++/* ---------- Interfaces ---------- */ ++ ++/* There are 3 levels of interfaces: ++ 1) Dictionary Interface ++ 2) Buffer Interface ++ 3) One Call Interface ++ You can select any of these interfaces, but don't mix functions from different ++ groups for same object. */ ++ ++ ++/* There are two variants to allocate state for Dictionary Interface: ++ 1) LzmaDec_Allocate / LzmaDec_Free ++ 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs ++ You can use variant 2, if you set dictionary buffer manually. ++ For Buffer Interface you must always use variant 1. ++ ++LzmaDec_Allocate* can return: ++ SZ_OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); ++ ++SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); ++ ++/* ---------- Dictionary Interface ---------- */ ++ ++/* You can use it, if you want to eliminate the overhead for data copying from ++ dictionary to some other external buffer. ++ You must work with CLzmaDec variables directly in this interface. ++ ++ STEPS: ++ LzmaDec_Constr() ++ LzmaDec_Allocate() ++ for (each new stream) ++ { ++ LzmaDec_Init() ++ while (it needs more decompression) ++ { ++ LzmaDec_DecodeToDic() ++ use data from CLzmaDec::dic and update CLzmaDec::dicPos ++ } ++ } ++ LzmaDec_Free() ++*/ ++ ++/* LzmaDec_DecodeToDic ++ ++ The decoding to internal dictionary buffer (CLzmaDec::dic). ++ You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (dicLimit). ++ LZMA_FINISH_ANY - Decode just dicLimit bytes. ++ LZMA_FINISH_END - Stream must be finished after dicLimit. ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_NEEDS_MORE_INPUT ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++*/ ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- Buffer Interface ---------- */ ++ ++/* It's zlib-like interface. ++ See LzmaDec_DecodeToDic description for information about STEPS and return results, ++ but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need ++ to work with CLzmaDec variables manually. ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++*/ ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- One Call Interface ---------- */ ++ ++/* LzmaDecode ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++ SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). ++*/ ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc); ++ ++EXTERN_C_END ++ ++#endif +diff --git a/cut-n-paste/unarr/lzmasdk/Ppmd.h b/cut-n-paste/unarr/lzmasdk/Ppmd.h +new file mode 100644 +index 00000000..4356dd1d +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/Ppmd.h +@@ -0,0 +1,85 @@ ++/* Ppmd.h -- PPMD codec common code ++2013-01-18 : Igor Pavlov : Public domain ++This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ ++ ++#ifndef __PPMD_H ++#define __PPMD_H ++ ++#include "CpuArch.h" ++ ++EXTERN_C_BEGIN ++ ++#ifdef MY_CPU_32BIT ++ #define PPMD_32BIT ++#endif ++ ++#define PPMD_INT_BITS 7 ++#define PPMD_PERIOD_BITS 7 ++#define PPMD_BIN_SCALE (1 << (PPMD_INT_BITS + PPMD_PERIOD_BITS)) ++ ++#define PPMD_GET_MEAN_SPEC(summ, shift, round) (((summ) + (1 << ((shift) - (round)))) >> (shift)) ++#define PPMD_GET_MEAN(summ) PPMD_GET_MEAN_SPEC((summ), PPMD_PERIOD_BITS, 2) ++#define PPMD_UPDATE_PROB_0(prob) ((prob) + (1 << PPMD_INT_BITS) - PPMD_GET_MEAN(prob)) ++#define PPMD_UPDATE_PROB_1(prob) ((prob) - PPMD_GET_MEAN(prob)) ++ ++#define PPMD_N1 4 ++#define PPMD_N2 4 ++#define PPMD_N3 4 ++#define PPMD_N4 ((128 + 3 - 1 * PPMD_N1 - 2 * PPMD_N2 - 3 * PPMD_N3) / 4) ++#define PPMD_NUM_INDEXES (PPMD_N1 + PPMD_N2 + PPMD_N3 + PPMD_N4) ++ ++#pragma pack(push, 1) ++/* Most compilers works OK here even without #pragma pack(push, 1), but some GCC compilers need it. */ ++ ++/* SEE-contexts for PPM-contexts with masked symbols */ ++typedef struct ++{ ++ UInt16 Summ; /* Freq */ ++ Byte Shift; /* Speed of Freq change; low Shift is for fast change */ ++ Byte Count; /* Count to next change of Shift */ ++} CPpmd_See; ++ ++#define Ppmd_See_Update(p) if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) \ ++ { (p)->Summ <<= 1; (p)->Count = (Byte)(3 << (p)->Shift++); } ++ ++typedef struct ++{ ++ Byte Symbol; ++ Byte Freq; ++ UInt16 SuccessorLow; ++ UInt16 SuccessorHigh; ++} CPpmd_State; ++ ++#pragma pack(pop) ++ ++typedef ++ #ifdef PPMD_32BIT ++ CPpmd_State * ++ #else ++ UInt32 ++ #endif ++ CPpmd_State_Ref; ++ ++typedef ++ #ifdef PPMD_32BIT ++ void * ++ #else ++ UInt32 ++ #endif ++ CPpmd_Void_Ref; ++ ++typedef ++ #ifdef PPMD_32BIT ++ Byte * ++ #else ++ UInt32 ++ #endif ++ CPpmd_Byte_Ref; ++ ++#define PPMD_SetAllBitsIn256Bytes(p) \ ++ { unsigned i; for (i = 0; i < 256 / sizeof(p[0]); i += 8) { \ ++ p[i+7] = p[i+6] = p[i+5] = p[i+4] = p[i+3] = p[i+2] = p[i+1] = p[i+0] = ~(size_t)0; }} ++ ++EXTERN_C_END ++ ++#endif +diff --git a/cut-n-paste/unarr/lzmasdk/Ppmd7.c b/cut-n-paste/unarr/lzmasdk/Ppmd7.c +new file mode 100644 +index 00000000..bb5d175e +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/Ppmd7.c +@@ -0,0 +1,710 @@ ++/* Ppmd7.c -- PPMdH codec ++2010-03-12 : Igor Pavlov : Public domain ++This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ ++ ++#include "Precomp.h" ++ ++#include ++ ++#include "Ppmd7.h" ++ ++const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; ++static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; ++ ++#define MAX_FREQ 124 ++#define UNIT_SIZE 12 ++ ++#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE) ++#define U2I(nu) (p->Units2Indx[(nu) - 1]) ++#define I2U(indx) (p->Indx2Units[indx]) ++ ++#ifdef PPMD_32BIT ++ #define REF(ptr) (ptr) ++#else ++ #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base)) ++#endif ++ ++#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr)) ++ ++#define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref)) ++#define STATS(ctx) Ppmd7_GetStats(p, ctx) ++#define ONE_STATE(ctx) Ppmd7Context_OneState(ctx) ++#define SUFFIX(ctx) CTX((ctx)->Suffix) ++ ++typedef CPpmd7_Context * CTX_PTR; ++ ++struct CPpmd7_Node_; ++ ++typedef ++ #ifdef PPMD_32BIT ++ struct CPpmd7_Node_ * ++ #else ++ UInt32 ++ #endif ++ CPpmd7_Node_Ref; ++ ++typedef struct CPpmd7_Node_ ++{ ++ UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */ ++ UInt16 NU; ++ CPpmd7_Node_Ref Next; /* must be at offset >= 4 */ ++ CPpmd7_Node_Ref Prev; ++} CPpmd7_Node; ++ ++#ifdef PPMD_32BIT ++ #define NODE(ptr) (ptr) ++#else ++ #define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs))) ++#endif ++ ++void Ppmd7_Construct(CPpmd7 *p) ++{ ++ unsigned i, k, m; ++ ++ p->Base = 0; ++ ++ for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++) ++ { ++ unsigned step = (i >= 12 ? 4 : (i >> 2) + 1); ++ do { p->Units2Indx[k++] = (Byte)i; } while(--step); ++ p->Indx2Units[i] = (Byte)k; ++ } ++ ++ p->NS2BSIndx[0] = (0 << 1); ++ p->NS2BSIndx[1] = (1 << 1); ++ memset(p->NS2BSIndx + 2, (2 << 1), 9); ++ memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11); ++ ++ for (i = 0; i < 3; i++) ++ p->NS2Indx[i] = (Byte)i; ++ for (m = i, k = 1; i < 256; i++) ++ { ++ p->NS2Indx[i] = (Byte)m; ++ if (--k == 0) ++ k = (++m) - 2; ++ } ++ ++ memset(p->HB2Flag, 0, 0x40); ++ memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40); ++} ++ ++void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->Base); ++ p->Size = 0; ++ p->Base = 0; ++} ++ ++Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc) ++{ ++ if (p->Base == 0 || p->Size != size) ++ { ++ Ppmd7_Free(p, alloc); ++ p->AlignOffset = ++ #ifdef PPMD_32BIT ++ (4 - size) & 3; ++ #else ++ 4 - (size & 3); ++ #endif ++ if ((p->Base = (Byte *)alloc->Alloc(alloc, p->AlignOffset + size ++ #ifndef PPMD_32BIT ++ + UNIT_SIZE ++ #endif ++ )) == 0) ++ return False; ++ p->Size = size; ++ } ++ return True; ++} ++ ++static void InsertNode(CPpmd7 *p, void *node, unsigned indx) ++{ ++ *((CPpmd_Void_Ref *)node) = p->FreeList[indx]; ++ p->FreeList[indx] = REF(node); ++} ++ ++static void *RemoveNode(CPpmd7 *p, unsigned indx) ++{ ++ CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]); ++ p->FreeList[indx] = *node; ++ return node; ++} ++ ++static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx) ++{ ++ unsigned i, nu = I2U(oldIndx) - I2U(newIndx); ++ ptr = (Byte *)ptr + U2B(I2U(newIndx)); ++ if (I2U(i = U2I(nu)) != nu) ++ { ++ unsigned k = I2U(--i); ++ InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1); ++ } ++ InsertNode(p, ptr, i); ++} ++ ++static void GlueFreeBlocks(CPpmd7 *p) ++{ ++ #ifdef PPMD_32BIT ++ CPpmd7_Node headItem; ++ CPpmd7_Node_Ref head = &headItem; ++ #else ++ CPpmd7_Node_Ref head = p->AlignOffset + p->Size; ++ #endif ++ ++ CPpmd7_Node_Ref n = head; ++ unsigned i; ++ ++ p->GlueCount = 255; ++ ++ /* create doubly-linked list of free blocks */ ++ for (i = 0; i < PPMD_NUM_INDEXES; i++) ++ { ++ UInt16 nu = I2U(i); ++ CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i]; ++ p->FreeList[i] = 0; ++ while (next != 0) ++ { ++ CPpmd7_Node *node = NODE(next); ++ node->Next = n; ++ n = NODE(n)->Prev = next; ++ next = *(const CPpmd7_Node_Ref *)node; ++ node->Stamp = 0; ++ node->NU = (UInt16)nu; ++ } ++ } ++ NODE(head)->Stamp = 1; ++ NODE(head)->Next = n; ++ NODE(n)->Prev = head; ++ if (p->LoUnit != p->HiUnit) ++ ((CPpmd7_Node *)p->LoUnit)->Stamp = 1; ++ ++ /* Glue free blocks */ ++ while (n != head) ++ { ++ CPpmd7_Node *node = NODE(n); ++ UInt32 nu = (UInt32)node->NU; ++ for (;;) ++ { ++ CPpmd7_Node *node2 = NODE(n) + nu; ++ nu += node2->NU; ++ if (node2->Stamp != 0 || nu >= 0x10000) ++ break; ++ NODE(node2->Prev)->Next = node2->Next; ++ NODE(node2->Next)->Prev = node2->Prev; ++ node->NU = (UInt16)nu; ++ } ++ n = node->Next; ++ } ++ ++ /* Fill lists of free blocks */ ++ for (n = NODE(head)->Next; n != head;) ++ { ++ CPpmd7_Node *node = NODE(n); ++ unsigned nu; ++ CPpmd7_Node_Ref next = node->Next; ++ for (nu = node->NU; nu > 128; nu -= 128, node += 128) ++ InsertNode(p, node, PPMD_NUM_INDEXES - 1); ++ if (I2U(i = U2I(nu)) != nu) ++ { ++ unsigned k = I2U(--i); ++ InsertNode(p, node + k, nu - k - 1); ++ } ++ InsertNode(p, node, i); ++ n = next; ++ } ++} ++ ++static void *AllocUnitsRare(CPpmd7 *p, unsigned indx) ++{ ++ unsigned i; ++ void *retVal; ++ if (p->GlueCount == 0) ++ { ++ GlueFreeBlocks(p); ++ if (p->FreeList[indx] != 0) ++ return RemoveNode(p, indx); ++ } ++ i = indx; ++ do ++ { ++ if (++i == PPMD_NUM_INDEXES) ++ { ++ UInt32 numBytes = U2B(I2U(indx)); ++ p->GlueCount--; ++ return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL); ++ } ++ } ++ while (p->FreeList[i] == 0); ++ retVal = RemoveNode(p, i); ++ SplitBlock(p, retVal, i, indx); ++ return retVal; ++} ++ ++static void *AllocUnits(CPpmd7 *p, unsigned indx) ++{ ++ UInt32 numBytes; ++ if (p->FreeList[indx] != 0) ++ return RemoveNode(p, indx); ++ numBytes = U2B(I2U(indx)); ++ if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit)) ++ { ++ void *retVal = p->LoUnit; ++ p->LoUnit += numBytes; ++ return retVal; ++ } ++ return AllocUnitsRare(p, indx); ++} ++ ++#define MyMem12Cpy(dest, src, num) \ ++ { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \ ++ do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); } ++ ++static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU) ++{ ++ unsigned i0 = U2I(oldNU); ++ unsigned i1 = U2I(newNU); ++ if (i0 == i1) ++ return oldPtr; ++ if (p->FreeList[i1] != 0) ++ { ++ void *ptr = RemoveNode(p, i1); ++ MyMem12Cpy(ptr, oldPtr, newNU); ++ InsertNode(p, oldPtr, i0); ++ return ptr; ++ } ++ SplitBlock(p, oldPtr, i0, i1); ++ return oldPtr; ++} ++ ++#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16))) ++ ++static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v) ++{ ++ (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF); ++ (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF); ++} ++ ++static void RestartModel(CPpmd7 *p) ++{ ++ unsigned i, k, m; ++ ++ memset(p->FreeList, 0, sizeof(p->FreeList)); ++ p->Text = p->Base + p->AlignOffset; ++ p->HiUnit = p->Text + p->Size; ++ p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE; ++ p->GlueCount = 0; ++ ++ p->OrderFall = p->MaxOrder; ++ p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1; ++ p->PrevSuccess = 0; ++ ++ p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */ ++ p->MinContext->Suffix = 0; ++ p->MinContext->NumStats = 256; ++ p->MinContext->SummFreq = 256 + 1; ++ p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */ ++ p->LoUnit += U2B(256 / 2); ++ p->MinContext->Stats = REF(p->FoundState); ++ for (i = 0; i < 256; i++) ++ { ++ CPpmd_State *s = &p->FoundState[i]; ++ s->Symbol = (Byte)i; ++ s->Freq = 1; ++ SetSuccessor(s, 0); ++ } ++ ++ for (i = 0; i < 128; i++) ++ for (k = 0; k < 8; k++) ++ { ++ UInt16 *dest = p->BinSumm[i] + k; ++ UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2)); ++ for (m = 0; m < 64; m += 8) ++ dest[m] = val; ++ } ++ ++ for (i = 0; i < 25; i++) ++ for (k = 0; k < 16; k++) ++ { ++ CPpmd_See *s = &p->See[i][k]; ++ s->Summ = (UInt16)((5 * i + 10) << (s->Shift = PPMD_PERIOD_BITS - 4)); ++ s->Count = 4; ++ } ++} ++ ++void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder) ++{ ++ p->MaxOrder = maxOrder; ++ RestartModel(p); ++ p->DummySee.Shift = PPMD_PERIOD_BITS; ++ p->DummySee.Summ = 0; /* unused */ ++ p->DummySee.Count = 64; /* unused */ ++} ++ ++static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip) ++{ ++ CPpmd_State upState; ++ CTX_PTR c = p->MinContext; ++ CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState); ++ CPpmd_State *ps[PPMD7_MAX_ORDER]; ++ unsigned numPs = 0; ++ ++ if (!skip) ++ ps[numPs++] = p->FoundState; ++ ++ while (c->Suffix) ++ { ++ CPpmd_Void_Ref successor; ++ CPpmd_State *s; ++ c = SUFFIX(c); ++ if (c->NumStats != 1) ++ { ++ for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++); ++ } ++ else ++ s = ONE_STATE(c); ++ successor = SUCCESSOR(s); ++ if (successor != upBranch) ++ { ++ c = CTX(successor); ++ if (numPs == 0) ++ return c; ++ break; ++ } ++ ps[numPs++] = s; ++ } ++ ++ upState.Symbol = *(const Byte *)Ppmd7_GetPtr(p, upBranch); ++ SetSuccessor(&upState, upBranch + 1); ++ ++ if (c->NumStats == 1) ++ upState.Freq = ONE_STATE(c)->Freq; ++ else ++ { ++ UInt32 cf, s0; ++ CPpmd_State *s; ++ for (s = STATS(c); s->Symbol != upState.Symbol; s++); ++ cf = s->Freq - 1; ++ s0 = c->SummFreq - c->NumStats - cf; ++ upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0)))); ++ } ++ ++ do ++ { ++ /* Create Child */ ++ CTX_PTR c1; /* = AllocContext(p); */ ++ if (p->HiUnit != p->LoUnit) ++ c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); ++ else if (p->FreeList[0] != 0) ++ c1 = (CTX_PTR)RemoveNode(p, 0); ++ else ++ { ++ c1 = (CTX_PTR)AllocUnitsRare(p, 0); ++ if (!c1) ++ return NULL; ++ } ++ c1->NumStats = 1; ++ *ONE_STATE(c1) = upState; ++ c1->Suffix = REF(c); ++ SetSuccessor(ps[--numPs], REF(c1)); ++ c = c1; ++ } ++ while (numPs != 0); ++ ++ return c; ++} ++ ++static void SwapStates(CPpmd_State *t1, CPpmd_State *t2) ++{ ++ CPpmd_State tmp = *t1; ++ *t1 = *t2; ++ *t2 = tmp; ++} ++ ++static void UpdateModel(CPpmd7 *p) ++{ ++ CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState); ++ CTX_PTR c; ++ unsigned s0, ns; ++ ++ if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0) ++ { ++ c = SUFFIX(p->MinContext); ++ ++ if (c->NumStats == 1) ++ { ++ CPpmd_State *s = ONE_STATE(c); ++ if (s->Freq < 32) ++ s->Freq++; ++ } ++ else ++ { ++ CPpmd_State *s = STATS(c); ++ if (s->Symbol != p->FoundState->Symbol) ++ { ++ do { s++; } while (s->Symbol != p->FoundState->Symbol); ++ if (s[0].Freq >= s[-1].Freq) ++ { ++ SwapStates(&s[0], &s[-1]); ++ s--; ++ } ++ } ++ if (s->Freq < MAX_FREQ - 9) ++ { ++ s->Freq += 2; ++ c->SummFreq += 2; ++ } ++ } ++ } ++ ++ if (p->OrderFall == 0) ++ { ++ p->MinContext = p->MaxContext = CreateSuccessors(p, True); ++ if (p->MinContext == 0) ++ { ++ RestartModel(p); ++ return; ++ } ++ SetSuccessor(p->FoundState, REF(p->MinContext)); ++ return; ++ } ++ ++ *p->Text++ = p->FoundState->Symbol; ++ successor = REF(p->Text); ++ if (p->Text >= p->UnitsStart) ++ { ++ RestartModel(p); ++ return; ++ } ++ ++ if (fSuccessor) ++ { ++ if (fSuccessor <= successor) ++ { ++ CTX_PTR cs = CreateSuccessors(p, False); ++ if (cs == NULL) ++ { ++ RestartModel(p); ++ return; ++ } ++ fSuccessor = REF(cs); ++ } ++ if (--p->OrderFall == 0) ++ { ++ successor = fSuccessor; ++ p->Text -= (p->MaxContext != p->MinContext); ++ } ++ } ++ else ++ { ++ SetSuccessor(p->FoundState, successor); ++ fSuccessor = REF(p->MinContext); ++ } ++ ++ s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - (p->FoundState->Freq - 1); ++ ++ for (c = p->MaxContext; c != p->MinContext; c = SUFFIX(c)) ++ { ++ unsigned ns1; ++ UInt32 cf, sf; ++ if ((ns1 = c->NumStats) != 1) ++ { ++ if ((ns1 & 1) == 0) ++ { ++ /* Expand for one UNIT */ ++ unsigned oldNU = ns1 >> 1; ++ unsigned i = U2I(oldNU); ++ if (i != U2I(oldNU + 1)) ++ { ++ void *ptr = AllocUnits(p, i + 1); ++ void *oldPtr; ++ if (!ptr) ++ { ++ RestartModel(p); ++ return; ++ } ++ oldPtr = STATS(c); ++ MyMem12Cpy(ptr, oldPtr, oldNU); ++ InsertNode(p, oldPtr, i); ++ c->Stats = STATS_REF(ptr); ++ } ++ } ++ c->SummFreq = (UInt16)(c->SummFreq + (2 * ns1 < ns) + 2 * ((4 * ns1 <= ns) & (c->SummFreq <= 8 * ns1))); ++ } ++ else ++ { ++ CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0); ++ if (!s) ++ { ++ RestartModel(p); ++ return; ++ } ++ *s = *ONE_STATE(c); ++ c->Stats = REF(s); ++ if (s->Freq < MAX_FREQ / 4 - 1) ++ s->Freq <<= 1; ++ else ++ s->Freq = MAX_FREQ - 4; ++ c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 3)); ++ } ++ cf = 2 * (UInt32)p->FoundState->Freq * (c->SummFreq + 6); ++ sf = (UInt32)s0 + c->SummFreq; ++ if (cf < 6 * sf) ++ { ++ cf = 1 + (cf > sf) + (cf >= 4 * sf); ++ c->SummFreq += 3; ++ } ++ else ++ { ++ cf = 4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf); ++ c->SummFreq = (UInt16)(c->SummFreq + cf); ++ } ++ { ++ CPpmd_State *s = STATS(c) + ns1; ++ SetSuccessor(s, successor); ++ s->Symbol = p->FoundState->Symbol; ++ s->Freq = (Byte)cf; ++ c->NumStats = (UInt16)(ns1 + 1); ++ } ++ } ++ p->MaxContext = p->MinContext = CTX(fSuccessor); ++} ++ ++static void Rescale(CPpmd7 *p) ++{ ++ unsigned i, adder, sumFreq, escFreq; ++ CPpmd_State *stats = STATS(p->MinContext); ++ CPpmd_State *s = p->FoundState; ++ { ++ CPpmd_State tmp = *s; ++ for (; s != stats; s--) ++ s[0] = s[-1]; ++ *s = tmp; ++ } ++ escFreq = p->MinContext->SummFreq - s->Freq; ++ s->Freq += 4; ++ adder = (p->OrderFall != 0); ++ s->Freq = (Byte)((s->Freq + adder) >> 1); ++ sumFreq = s->Freq; ++ ++ i = p->MinContext->NumStats - 1; ++ do ++ { ++ escFreq -= (++s)->Freq; ++ s->Freq = (Byte)((s->Freq + adder) >> 1); ++ sumFreq += s->Freq; ++ if (s[0].Freq > s[-1].Freq) ++ { ++ CPpmd_State *s1 = s; ++ CPpmd_State tmp = *s1; ++ do ++ s1[0] = s1[-1]; ++ while (--s1 != stats && tmp.Freq > s1[-1].Freq); ++ *s1 = tmp; ++ } ++ } ++ while (--i); ++ ++ if (s->Freq == 0) ++ { ++ unsigned numStats = p->MinContext->NumStats; ++ unsigned n0, n1; ++ do { i++; } while ((--s)->Freq == 0); ++ escFreq += i; ++ p->MinContext->NumStats = (UInt16)(p->MinContext->NumStats - i); ++ if (p->MinContext->NumStats == 1) ++ { ++ CPpmd_State tmp = *stats; ++ do ++ { ++ tmp.Freq = (Byte)(tmp.Freq - (tmp.Freq >> 1)); ++ escFreq >>= 1; ++ } ++ while (escFreq > 1); ++ InsertNode(p, stats, U2I(((numStats + 1) >> 1))); ++ *(p->FoundState = ONE_STATE(p->MinContext)) = tmp; ++ return; ++ } ++ n0 = (numStats + 1) >> 1; ++ n1 = (p->MinContext->NumStats + 1) >> 1; ++ if (n0 != n1) ++ p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1)); ++ } ++ p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1)); ++ p->FoundState = STATS(p->MinContext); ++} ++ ++CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq) ++{ ++ CPpmd_See *see; ++ unsigned nonMasked = p->MinContext->NumStats - numMasked; ++ if (p->MinContext->NumStats != 256) ++ { ++ see = p->See[p->NS2Indx[nonMasked - 1]] + ++ (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) + ++ 2 * (p->MinContext->SummFreq < 11 * p->MinContext->NumStats) + ++ 4 * (numMasked > nonMasked) + ++ p->HiBitsFlag; ++ { ++ unsigned r = (see->Summ >> see->Shift); ++ see->Summ = (UInt16)(see->Summ - r); ++ *escFreq = r + (r == 0); ++ } ++ } ++ else ++ { ++ see = &p->DummySee; ++ *escFreq = 1; ++ } ++ return see; ++} ++ ++static void NextContext(CPpmd7 *p) ++{ ++ CTX_PTR c = CTX(SUCCESSOR(p->FoundState)); ++ if (p->OrderFall == 0 && (Byte *)c > p->Text) ++ p->MinContext = p->MaxContext = c; ++ else ++ UpdateModel(p); ++} ++ ++void Ppmd7_Update1(CPpmd7 *p) ++{ ++ CPpmd_State *s = p->FoundState; ++ s->Freq += 4; ++ p->MinContext->SummFreq += 4; ++ if (s[0].Freq > s[-1].Freq) ++ { ++ SwapStates(&s[0], &s[-1]); ++ p->FoundState = --s; ++ if (s->Freq > MAX_FREQ) ++ Rescale(p); ++ } ++ NextContext(p); ++} ++ ++void Ppmd7_Update1_0(CPpmd7 *p) ++{ ++ p->PrevSuccess = (2 * p->FoundState->Freq > p->MinContext->SummFreq); ++ p->RunLength += p->PrevSuccess; ++ p->MinContext->SummFreq += 4; ++ if ((p->FoundState->Freq += 4) > MAX_FREQ) ++ Rescale(p); ++ NextContext(p); ++} ++ ++void Ppmd7_UpdateBin(CPpmd7 *p) ++{ ++ p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 128 ? 1: 0)); ++ p->PrevSuccess = 1; ++ p->RunLength++; ++ NextContext(p); ++} ++ ++void Ppmd7_Update2(CPpmd7 *p) ++{ ++ p->MinContext->SummFreq += 4; ++ if ((p->FoundState->Freq += 4) > MAX_FREQ) ++ Rescale(p); ++ p->RunLength = p->InitRL; ++ UpdateModel(p); ++} +diff --git a/cut-n-paste/unarr/lzmasdk/Ppmd7.h b/cut-n-paste/unarr/lzmasdk/Ppmd7.h +new file mode 100644 +index 00000000..96521c31 +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/Ppmd7.h +@@ -0,0 +1,140 @@ ++/* Ppmd7.h -- PPMdH compression codec ++2010-03-12 : Igor Pavlov : Public domain ++This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ ++ ++/* This code supports virtual RangeDecoder and includes the implementation ++of RangeCoder from 7z, instead of RangeCoder from original PPMd var.H. ++If you need the compatibility with original PPMd var.H, you can use external RangeDecoder */ ++ ++#ifndef __PPMD7_H ++#define __PPMD7_H ++ ++#include "Ppmd.h" ++ ++EXTERN_C_BEGIN ++ ++#define PPMD7_MIN_ORDER 2 ++#define PPMD7_MAX_ORDER 64 ++ ++#define PPMD7_MIN_MEM_SIZE (1 << 11) ++#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFF - 12 * 3) ++ ++struct CPpmd7_Context_; ++ ++typedef ++ #ifdef PPMD_32BIT ++ struct CPpmd7_Context_ * ++ #else ++ UInt32 ++ #endif ++ CPpmd7_Context_Ref; ++ ++typedef struct CPpmd7_Context_ ++{ ++ UInt16 NumStats; ++ UInt16 SummFreq; ++ CPpmd_State_Ref Stats; ++ CPpmd7_Context_Ref Suffix; ++} CPpmd7_Context; ++ ++#define Ppmd7Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq) ++ ++typedef struct ++{ ++ CPpmd7_Context *MinContext, *MaxContext; ++ CPpmd_State *FoundState; ++ unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder, HiBitsFlag; ++ Int32 RunLength, InitRL; /* must be 32-bit at least */ ++ ++ UInt32 Size; ++ UInt32 GlueCount; ++ Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart; ++ UInt32 AlignOffset; ++ ++ Byte Indx2Units[PPMD_NUM_INDEXES]; ++ Byte Units2Indx[128]; ++ CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES]; ++ Byte NS2Indx[256], NS2BSIndx[256], HB2Flag[256]; ++ CPpmd_See DummySee, See[25][16]; ++ UInt16 BinSumm[128][64]; ++} CPpmd7; ++ ++void Ppmd7_Construct(CPpmd7 *p); ++Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc); ++void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc); ++void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder); ++#define Ppmd7_WasAllocated(p) ((p)->Base != NULL) ++ ++ ++/* ---------- Internal Functions ---------- */ ++ ++extern const Byte PPMD7_kExpEscape[16]; ++ ++#ifdef PPMD_32BIT ++ #define Ppmd7_GetPtr(p, ptr) (ptr) ++ #define Ppmd7_GetContext(p, ptr) (ptr) ++ #define Ppmd7_GetStats(p, ctx) ((ctx)->Stats) ++#else ++ #define Ppmd7_GetPtr(p, offs) ((void *)((p)->Base + (offs))) ++ #define Ppmd7_GetContext(p, offs) ((CPpmd7_Context *)Ppmd7_GetPtr((p), (offs))) ++ #define Ppmd7_GetStats(p, ctx) ((CPpmd_State *)Ppmd7_GetPtr((p), ((ctx)->Stats))) ++#endif ++ ++void Ppmd7_Update1(CPpmd7 *p); ++void Ppmd7_Update1_0(CPpmd7 *p); ++void Ppmd7_Update2(CPpmd7 *p); ++void Ppmd7_UpdateBin(CPpmd7 *p); ++ ++#define Ppmd7_GetBinSumm(p) \ ++ &p->BinSumm[Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \ ++ p->NS2BSIndx[Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \ ++ (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \ ++ 2 * p->HB2Flag[Ppmd7Context_OneState(p->MinContext)->Symbol] + \ ++ ((p->RunLength >> 26) & 0x20)] ++ ++CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *scale); ++ ++ ++/* ---------- Decode ---------- */ ++ ++typedef struct ++{ ++ UInt32 (*GetThreshold)(void *p, UInt32 total); ++ void (*Decode)(void *p, UInt32 start, UInt32 size); ++ UInt32 (*DecodeBit)(void *p, UInt32 size0); ++} IPpmd7_RangeDec; ++ ++typedef struct ++{ ++ IPpmd7_RangeDec p; ++ UInt32 Range; ++ UInt32 Code; ++ IByteIn *Stream; ++} CPpmd7z_RangeDec; ++ ++void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p); ++Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p); ++#define Ppmd7z_RangeDec_IsFinishedOK(p) ((p)->Code == 0) ++ ++int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc); ++ ++ ++/* ---------- Encode ---------- */ ++ ++typedef struct ++{ ++ UInt64 Low; ++ UInt32 Range; ++ Byte Cache; ++ UInt64 CacheSize; ++ IByteOut *Stream; ++} CPpmd7z_RangeEnc; ++ ++void Ppmd7z_RangeEnc_Init(CPpmd7z_RangeEnc *p); ++void Ppmd7z_RangeEnc_FlushData(CPpmd7z_RangeEnc *p); ++ ++void Ppmd7_EncodeSymbol(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol); ++ ++EXTERN_C_END ++ ++#endif +diff --git a/cut-n-paste/unarr/lzmasdk/Ppmd7Dec.c b/cut-n-paste/unarr/lzmasdk/Ppmd7Dec.c +new file mode 100644 +index 00000000..04b4b09e +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/Ppmd7Dec.c +@@ -0,0 +1,189 @@ ++/* Ppmd7Dec.c -- PPMdH Decoder ++2010-03-12 : Igor Pavlov : Public domain ++This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ ++ ++#include "Precomp.h" ++ ++#include "Ppmd7.h" ++ ++#define kTopValue (1 << 24) ++ ++Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p) ++{ ++ unsigned i; ++ p->Code = 0; ++ p->Range = 0xFFFFFFFF; ++ if (p->Stream->Read((void *)p->Stream) != 0) ++ return False; ++ for (i = 0; i < 4; i++) ++ p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream); ++ return (p->Code < 0xFFFFFFFF); ++} ++ ++static UInt32 Range_GetThreshold(void *pp, UInt32 total) ++{ ++ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; ++ return (p->Code) / (p->Range /= total); ++} ++ ++static void Range_Normalize(CPpmd7z_RangeDec *p) ++{ ++ if (p->Range < kTopValue) ++ { ++ p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream); ++ p->Range <<= 8; ++ if (p->Range < kTopValue) ++ { ++ p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream); ++ p->Range <<= 8; ++ } ++ } ++} ++ ++static void Range_Decode(void *pp, UInt32 start, UInt32 size) ++{ ++ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; ++ p->Code -= start * p->Range; ++ p->Range *= size; ++ Range_Normalize(p); ++} ++ ++static UInt32 Range_DecodeBit(void *pp, UInt32 size0) ++{ ++ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; ++ UInt32 newBound = (p->Range >> 14) * size0; ++ UInt32 symbol; ++ if (p->Code < newBound) ++ { ++ symbol = 0; ++ p->Range = newBound; ++ } ++ else ++ { ++ symbol = 1; ++ p->Code -= newBound; ++ p->Range -= newBound; ++ } ++ Range_Normalize(p); ++ return symbol; ++} ++ ++void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p) ++{ ++ p->p.GetThreshold = Range_GetThreshold; ++ p->p.Decode = Range_Decode; ++ p->p.DecodeBit = Range_DecodeBit; ++} ++ ++ ++#define MASK(sym) ((signed char *)charMask)[sym] ++ ++int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc) ++{ ++ size_t charMask[256 / sizeof(size_t)]; ++ if (p->MinContext->NumStats != 1) ++ { ++ CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext); ++ unsigned i; ++ UInt32 count, hiCnt; ++ if ((count = rc->GetThreshold(rc, p->MinContext->SummFreq)) < (hiCnt = s->Freq)) ++ { ++ Byte symbol; ++ rc->Decode(rc, 0, s->Freq); ++ p->FoundState = s; ++ symbol = s->Symbol; ++ Ppmd7_Update1_0(p); ++ return symbol; ++ } ++ p->PrevSuccess = 0; ++ i = p->MinContext->NumStats - 1; ++ do ++ { ++ if ((hiCnt += (++s)->Freq) > count) ++ { ++ Byte symbol; ++ rc->Decode(rc, hiCnt - s->Freq, s->Freq); ++ p->FoundState = s; ++ symbol = s->Symbol; ++ Ppmd7_Update1(p); ++ return symbol; ++ } ++ } ++ while (--i); ++ if (count >= p->MinContext->SummFreq) ++ return -2; ++ p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]; ++ rc->Decode(rc, hiCnt, p->MinContext->SummFreq - hiCnt); ++ PPMD_SetAllBitsIn256Bytes(charMask); ++ MASK(s->Symbol) = 0; ++ i = p->MinContext->NumStats - 1; ++ do { MASK((--s)->Symbol) = 0; } while (--i); ++ } ++ else ++ { ++ UInt16 *prob = Ppmd7_GetBinSumm(p); ++ if (rc->DecodeBit(rc, *prob) == 0) ++ { ++ Byte symbol; ++ *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob); ++ symbol = (p->FoundState = Ppmd7Context_OneState(p->MinContext))->Symbol; ++ Ppmd7_UpdateBin(p); ++ return symbol; ++ } ++ *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob); ++ p->InitEsc = PPMD7_kExpEscape[*prob >> 10]; ++ PPMD_SetAllBitsIn256Bytes(charMask); ++ MASK(Ppmd7Context_OneState(p->MinContext)->Symbol) = 0; ++ p->PrevSuccess = 0; ++ } ++ for (;;) ++ { ++ CPpmd_State *ps[256], *s; ++ UInt32 freqSum, count, hiCnt; ++ CPpmd_See *see; ++ unsigned i, num, numMasked = p->MinContext->NumStats; ++ do ++ { ++ p->OrderFall++; ++ if (!p->MinContext->Suffix) ++ return -1; ++ p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix); ++ } ++ while (p->MinContext->NumStats == numMasked); ++ hiCnt = 0; ++ s = Ppmd7_GetStats(p, p->MinContext); ++ i = 0; ++ num = p->MinContext->NumStats - numMasked; ++ do ++ { ++ int k = (int)(MASK(s->Symbol)); ++ hiCnt += (s->Freq & k); ++ ps[i] = s++; ++ i -= k; ++ } ++ while (i != num); ++ ++ see = Ppmd7_MakeEscFreq(p, numMasked, &freqSum); ++ freqSum += hiCnt; ++ count = rc->GetThreshold(rc, freqSum); ++ ++ if (count < hiCnt) ++ { ++ Byte symbol; ++ CPpmd_State **pps = ps; ++ for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++); ++ s = *pps; ++ rc->Decode(rc, hiCnt - s->Freq, s->Freq); ++ Ppmd_See_Update(see); ++ p->FoundState = s; ++ symbol = s->Symbol; ++ Ppmd7_Update2(p); ++ return symbol; ++ } ++ if (count >= freqSum) ++ return -2; ++ rc->Decode(rc, hiCnt, freqSum - hiCnt); ++ see->Summ = (UInt16)(see->Summ + freqSum); ++ do { MASK(ps[--i]->Symbol) = 0; } while (i != 0); ++ } ++} +diff --git a/cut-n-paste/unarr/lzmasdk/Ppmd8.c b/cut-n-paste/unarr/lzmasdk/Ppmd8.c +new file mode 100644 +index 00000000..8beef23b +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/Ppmd8.c +@@ -0,0 +1,1122 @@ ++/* Ppmd8.c -- PPMdI codec ++2010-03-24 : Igor Pavlov : Public domain ++This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */ ++ ++#include "Precomp.h" ++ ++#include ++ ++#include "Ppmd8.h" ++ ++const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; ++static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; ++ ++#define MAX_FREQ 124 ++#define UNIT_SIZE 12 ++ ++#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE) ++#define U2I(nu) (p->Units2Indx[(nu) - 1]) ++#define I2U(indx) (p->Indx2Units[indx]) ++ ++#ifdef PPMD_32BIT ++ #define REF(ptr) (ptr) ++#else ++ #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base)) ++#endif ++ ++#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr)) ++ ++#define CTX(ref) ((CPpmd8_Context *)Ppmd8_GetContext(p, ref)) ++#define STATS(ctx) Ppmd8_GetStats(p, ctx) ++#define ONE_STATE(ctx) Ppmd8Context_OneState(ctx) ++#define SUFFIX(ctx) CTX((ctx)->Suffix) ++ ++typedef CPpmd8_Context * CTX_PTR; ++ ++struct CPpmd8_Node_; ++ ++typedef ++ #ifdef PPMD_32BIT ++ struct CPpmd8_Node_ * ++ #else ++ UInt32 ++ #endif ++ CPpmd8_Node_Ref; ++ ++typedef struct CPpmd8_Node_ ++{ ++ UInt32 Stamp; ++ CPpmd8_Node_Ref Next; ++ UInt32 NU; ++} CPpmd8_Node; ++ ++#ifdef PPMD_32BIT ++ #define NODE(ptr) (ptr) ++#else ++ #define NODE(offs) ((CPpmd8_Node *)(p->Base + (offs))) ++#endif ++ ++#define EMPTY_NODE 0xFFFFFFFF ++ ++void Ppmd8_Construct(CPpmd8 *p) ++{ ++ unsigned i, k, m; ++ ++ p->Base = 0; ++ ++ for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++) ++ { ++ unsigned step = (i >= 12 ? 4 : (i >> 2) + 1); ++ do { p->Units2Indx[k++] = (Byte)i; } while(--step); ++ p->Indx2Units[i] = (Byte)k; ++ } ++ ++ p->NS2BSIndx[0] = (0 << 1); ++ p->NS2BSIndx[1] = (1 << 1); ++ memset(p->NS2BSIndx + 2, (2 << 1), 9); ++ memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11); ++ ++ for (i = 0; i < 5; i++) ++ p->NS2Indx[i] = (Byte)i; ++ for (m = i, k = 1; i < 260; i++) ++ { ++ p->NS2Indx[i] = (Byte)m; ++ if (--k == 0) ++ k = (++m) - 4; ++ } ++} ++ ++void Ppmd8_Free(CPpmd8 *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->Base); ++ p->Size = 0; ++ p->Base = 0; ++} ++ ++Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size, ISzAlloc *alloc) ++{ ++ if (p->Base == 0 || p->Size != size) ++ { ++ Ppmd8_Free(p, alloc); ++ p->AlignOffset = ++ #ifdef PPMD_32BIT ++ (4 - size) & 3; ++ #else ++ 4 - (size & 3); ++ #endif ++ if ((p->Base = (Byte *)alloc->Alloc(alloc, p->AlignOffset + size)) == 0) ++ return False; ++ p->Size = size; ++ } ++ return True; ++} ++ ++static void InsertNode(CPpmd8 *p, void *node, unsigned indx) ++{ ++ ((CPpmd8_Node *)node)->Stamp = EMPTY_NODE; ++ ((CPpmd8_Node *)node)->Next = (CPpmd8_Node_Ref)p->FreeList[indx]; ++ ((CPpmd8_Node *)node)->NU = I2U(indx); ++ p->FreeList[indx] = REF(node); ++ p->Stamps[indx]++; ++} ++ ++static void *RemoveNode(CPpmd8 *p, unsigned indx) ++{ ++ CPpmd8_Node *node = NODE((CPpmd8_Node_Ref)p->FreeList[indx]); ++ p->FreeList[indx] = node->Next; ++ p->Stamps[indx]--; ++ return node; ++} ++ ++static void SplitBlock(CPpmd8 *p, void *ptr, unsigned oldIndx, unsigned newIndx) ++{ ++ unsigned i, nu = I2U(oldIndx) - I2U(newIndx); ++ ptr = (Byte *)ptr + U2B(I2U(newIndx)); ++ if (I2U(i = U2I(nu)) != nu) ++ { ++ unsigned k = I2U(--i); ++ InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1); ++ } ++ InsertNode(p, ptr, i); ++} ++ ++static void GlueFreeBlocks(CPpmd8 *p) ++{ ++ CPpmd8_Node_Ref head = 0; ++ CPpmd8_Node_Ref *prev = &head; ++ unsigned i; ++ ++ p->GlueCount = 1 << 13; ++ memset(p->Stamps, 0, sizeof(p->Stamps)); ++ ++ /* Order-0 context is always at top UNIT, so we don't need guard NODE at the end. ++ All blocks up to p->LoUnit can be free, so we need guard NODE at LoUnit. */ ++ if (p->LoUnit != p->HiUnit) ++ ((CPpmd8_Node *)p->LoUnit)->Stamp = 0; ++ ++ /* Glue free blocks */ ++ for (i = 0; i < PPMD_NUM_INDEXES; i++) ++ { ++ CPpmd8_Node_Ref next = (CPpmd8_Node_Ref)p->FreeList[i]; ++ p->FreeList[i] = 0; ++ while (next != 0) ++ { ++ CPpmd8_Node *node = NODE(next); ++ if (node->NU != 0) ++ { ++ CPpmd8_Node *node2; ++ *prev = next; ++ prev = &(node->Next); ++ while ((node2 = node + node->NU)->Stamp == EMPTY_NODE) ++ { ++ node->NU += node2->NU; ++ node2->NU = 0; ++ } ++ } ++ next = node->Next; ++ } ++ } ++ *prev = 0; ++ ++ /* Fill lists of free blocks */ ++ while (head != 0) ++ { ++ CPpmd8_Node *node = NODE(head); ++ unsigned nu; ++ head = node->Next; ++ nu = node->NU; ++ if (nu == 0) ++ continue; ++ for (; nu > 128; nu -= 128, node += 128) ++ InsertNode(p, node, PPMD_NUM_INDEXES - 1); ++ if (I2U(i = U2I(nu)) != nu) ++ { ++ unsigned k = I2U(--i); ++ InsertNode(p, node + k, nu - k - 1); ++ } ++ InsertNode(p, node, i); ++ } ++} ++ ++static void *AllocUnitsRare(CPpmd8 *p, unsigned indx) ++{ ++ unsigned i; ++ void *retVal; ++ if (p->GlueCount == 0) ++ { ++ GlueFreeBlocks(p); ++ if (p->FreeList[indx] != 0) ++ return RemoveNode(p, indx); ++ } ++ i = indx; ++ do ++ { ++ if (++i == PPMD_NUM_INDEXES) ++ { ++ UInt32 numBytes = U2B(I2U(indx)); ++ p->GlueCount--; ++ return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL); ++ } ++ } ++ while (p->FreeList[i] == 0); ++ retVal = RemoveNode(p, i); ++ SplitBlock(p, retVal, i, indx); ++ return retVal; ++} ++ ++static void *AllocUnits(CPpmd8 *p, unsigned indx) ++{ ++ UInt32 numBytes; ++ if (p->FreeList[indx] != 0) ++ return RemoveNode(p, indx); ++ numBytes = U2B(I2U(indx)); ++ if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit)) ++ { ++ void *retVal = p->LoUnit; ++ p->LoUnit += numBytes; ++ return retVal; ++ } ++ return AllocUnitsRare(p, indx); ++} ++ ++#define MyMem12Cpy(dest, src, num) \ ++ { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \ ++ do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); } ++ ++static void *ShrinkUnits(CPpmd8 *p, void *oldPtr, unsigned oldNU, unsigned newNU) ++{ ++ unsigned i0 = U2I(oldNU); ++ unsigned i1 = U2I(newNU); ++ if (i0 == i1) ++ return oldPtr; ++ if (p->FreeList[i1] != 0) ++ { ++ void *ptr = RemoveNode(p, i1); ++ MyMem12Cpy(ptr, oldPtr, newNU); ++ InsertNode(p, oldPtr, i0); ++ return ptr; ++ } ++ SplitBlock(p, oldPtr, i0, i1); ++ return oldPtr; ++} ++ ++static void FreeUnits(CPpmd8 *p, void *ptr, unsigned nu) ++{ ++ InsertNode(p, ptr, U2I(nu)); ++} ++ ++static void SpecialFreeUnit(CPpmd8 *p, void *ptr) ++{ ++ if ((Byte *)ptr != p->UnitsStart) ++ InsertNode(p, ptr, 0); ++ else ++ { ++ #ifdef PPMD8_FREEZE_SUPPORT ++ *(UInt32 *)ptr = EMPTY_NODE; /* it's used for (Flags == 0xFF) check in RemoveBinContexts */ ++ #endif ++ p->UnitsStart += UNIT_SIZE; ++ } ++} ++ ++static void *MoveUnitsUp(CPpmd8 *p, void *oldPtr, unsigned nu) ++{ ++ unsigned indx = U2I(nu); ++ void *ptr; ++ if ((Byte *)oldPtr > p->UnitsStart + 16 * 1024 || REF(oldPtr) > p->FreeList[indx]) ++ return oldPtr; ++ ptr = RemoveNode(p, indx); ++ MyMem12Cpy(ptr, oldPtr, nu); ++ if ((Byte*)oldPtr != p->UnitsStart) ++ InsertNode(p, oldPtr, indx); ++ else ++ p->UnitsStart += U2B(I2U(indx)); ++ return ptr; ++} ++ ++static void ExpandTextArea(CPpmd8 *p) ++{ ++ UInt32 count[PPMD_NUM_INDEXES]; ++ unsigned i; ++ memset(count, 0, sizeof(count)); ++ if (p->LoUnit != p->HiUnit) ++ ((CPpmd8_Node *)p->LoUnit)->Stamp = 0; ++ ++ { ++ CPpmd8_Node *node = (CPpmd8_Node *)p->UnitsStart; ++ for (; node->Stamp == EMPTY_NODE; node += node->NU) ++ { ++ node->Stamp = 0; ++ count[U2I(node->NU)]++; ++ } ++ p->UnitsStart = (Byte *)node; ++ } ++ ++ for (i = 0; i < PPMD_NUM_INDEXES; i++) ++ { ++ CPpmd8_Node_Ref *next = (CPpmd8_Node_Ref *)&p->FreeList[i]; ++ while (count[i] != 0) ++ { ++ CPpmd8_Node *node = NODE(*next); ++ while (node->Stamp == 0) ++ { ++ *next = node->Next; ++ node = NODE(*next); ++ p->Stamps[i]--; ++ if (--count[i] == 0) ++ break; ++ } ++ next = &node->Next; ++ } ++ } ++} ++ ++#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16))) ++ ++static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v) ++{ ++ (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF); ++ (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF); ++} ++ ++#define RESET_TEXT(offs) { p->Text = p->Base + p->AlignOffset + (offs); } ++ ++static void RestartModel(CPpmd8 *p) ++{ ++ unsigned i, k, m, r; ++ ++ memset(p->FreeList, 0, sizeof(p->FreeList)); ++ memset(p->Stamps, 0, sizeof(p->Stamps)); ++ RESET_TEXT(0); ++ p->HiUnit = p->Text + p->Size; ++ p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE; ++ p->GlueCount = 0; ++ ++ p->OrderFall = p->MaxOrder; ++ p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1; ++ p->PrevSuccess = 0; ++ ++ p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */ ++ p->MinContext->Suffix = 0; ++ p->MinContext->NumStats = 255; ++ p->MinContext->Flags = 0; ++ p->MinContext->SummFreq = 256 + 1; ++ p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */ ++ p->LoUnit += U2B(256 / 2); ++ p->MinContext->Stats = REF(p->FoundState); ++ for (i = 0; i < 256; i++) ++ { ++ CPpmd_State *s = &p->FoundState[i]; ++ s->Symbol = (Byte)i; ++ s->Freq = 1; ++ SetSuccessor(s, 0); ++ } ++ ++ for (i = m = 0; m < 25; m++) ++ { ++ while (p->NS2Indx[i] == m) ++ i++; ++ for (k = 0; k < 8; k++) ++ { ++ UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 1)); ++ UInt16 *dest = p->BinSumm[m] + k; ++ for (r = 0; r < 64; r += 8) ++ dest[r] = val; ++ } ++ } ++ ++ for (i = m = 0; m < 24; m++) ++ { ++ while (p->NS2Indx[i + 3] == m + 3) ++ i++; ++ for (k = 0; k < 32; k++) ++ { ++ CPpmd_See *s = &p->See[m][k]; ++ s->Summ = (UInt16)((2 * i + 5) << (s->Shift = PPMD_PERIOD_BITS - 4)); ++ s->Count = 7; ++ } ++ } ++} ++ ++void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod) ++{ ++ p->MaxOrder = maxOrder; ++ p->RestoreMethod = restoreMethod; ++ RestartModel(p); ++ p->DummySee.Shift = PPMD_PERIOD_BITS; ++ p->DummySee.Summ = 0; /* unused */ ++ p->DummySee.Count = 64; /* unused */ ++} ++ ++static void Refresh(CPpmd8 *p, CTX_PTR ctx, unsigned oldNU, unsigned scale) ++{ ++ unsigned i = ctx->NumStats, escFreq, sumFreq, flags; ++ CPpmd_State *s = (CPpmd_State *)ShrinkUnits(p, STATS(ctx), oldNU, (i + 2) >> 1); ++ ctx->Stats = REF(s); ++ #ifdef PPMD8_FREEZE_SUPPORT ++ /* fixed over Shkarin's code. Fixed code is not compatible with original code for some files in FREEZE mode. */ ++ scale |= (ctx->SummFreq >= ((UInt32)1 << 15)); ++ #endif ++ flags = (ctx->Flags & (0x10 + 0x04 * scale)) + 0x08 * (s->Symbol >= 0x40); ++ escFreq = ctx->SummFreq - s->Freq; ++ sumFreq = (s->Freq = (Byte)((s->Freq + scale) >> scale)); ++ do ++ { ++ escFreq -= (++s)->Freq; ++ sumFreq += (s->Freq = (Byte)((s->Freq + scale) >> scale)); ++ flags |= 0x08 * (s->Symbol >= 0x40); ++ } ++ while (--i); ++ ctx->SummFreq = (UInt16)(sumFreq + ((escFreq + scale) >> scale)); ++ ctx->Flags = (Byte)flags; ++} ++ ++static void SwapStates(CPpmd_State *t1, CPpmd_State *t2) ++{ ++ CPpmd_State tmp = *t1; ++ *t1 = *t2; ++ *t2 = tmp; ++} ++ ++static CPpmd_Void_Ref CutOff(CPpmd8 *p, CTX_PTR ctx, unsigned order) ++{ ++ int i; ++ unsigned tmp; ++ CPpmd_State *s; ++ ++ if (!ctx->NumStats) ++ { ++ s = ONE_STATE(ctx); ++ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart) ++ { ++ if (order < p->MaxOrder) ++ SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1)); ++ else ++ SetSuccessor(s, 0); ++ if (SUCCESSOR(s) || order <= 9) /* O_BOUND */ ++ return REF(ctx); ++ } ++ SpecialFreeUnit(p, ctx); ++ return 0; ++ } ++ ++ ctx->Stats = STATS_REF(MoveUnitsUp(p, STATS(ctx), tmp = ((unsigned)ctx->NumStats + 2) >> 1)); ++ ++ for (s = STATS(ctx) + (i = ctx->NumStats); s >= STATS(ctx); s--) ++ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) < p->UnitsStart) ++ { ++ CPpmd_State *s2 = STATS(ctx) + (i--); ++ SetSuccessor(s, 0); ++ SwapStates(s, s2); ++ } ++ else if (order < p->MaxOrder) ++ SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1)); ++ else ++ SetSuccessor(s, 0); ++ ++ if (i != ctx->NumStats && order) ++ { ++ ctx->NumStats = (Byte)i; ++ s = STATS(ctx); ++ if (i < 0) ++ { ++ FreeUnits(p, s, tmp); ++ SpecialFreeUnit(p, ctx); ++ return 0; ++ } ++ if (i == 0) ++ { ++ ctx->Flags = (ctx->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40); ++ *ONE_STATE(ctx) = *s; ++ FreeUnits(p, s, tmp); ++ ONE_STATE(ctx)->Freq = (Byte)((unsigned)ONE_STATE(ctx)->Freq + 11) >> 3; ++ } ++ else ++ Refresh(p, ctx, tmp, ctx->SummFreq > 16 * i); ++ } ++ return REF(ctx); ++} ++ ++#ifdef PPMD8_FREEZE_SUPPORT ++static CPpmd_Void_Ref RemoveBinContexts(CPpmd8 *p, CTX_PTR ctx, unsigned order) ++{ ++ CPpmd_State *s; ++ if (!ctx->NumStats) ++ { ++ s = ONE_STATE(ctx); ++ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder) ++ SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1)); ++ else ++ SetSuccessor(s, 0); ++ /* Suffix context can be removed already, since different (high-order) ++ Successors may refer to same context. So we check Flags == 0xFF (Stamp == EMPTY_NODE) */ ++ if (!SUCCESSOR(s) && (!SUFFIX(ctx)->NumStats || SUFFIX(ctx)->Flags == 0xFF)) ++ { ++ FreeUnits(p, ctx, 1); ++ return 0; ++ } ++ else ++ return REF(ctx); ++ } ++ ++ for (s = STATS(ctx) + ctx->NumStats; s >= STATS(ctx); s--) ++ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder) ++ SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1)); ++ else ++ SetSuccessor(s, 0); ++ ++ return REF(ctx); ++} ++#endif ++ ++static UInt32 GetUsedMemory(const CPpmd8 *p) ++{ ++ UInt32 v = 0; ++ unsigned i; ++ for (i = 0; i < PPMD_NUM_INDEXES; i++) ++ v += p->Stamps[i] * I2U(i); ++ return p->Size - (UInt32)(p->HiUnit - p->LoUnit) - (UInt32)(p->UnitsStart - p->Text) - U2B(v); ++} ++ ++#ifdef PPMD8_FREEZE_SUPPORT ++ #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1, fSuccessor) ++#else ++ #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1) ++#endif ++ ++static void RestoreModel(CPpmd8 *p, CTX_PTR c1 ++ #ifdef PPMD8_FREEZE_SUPPORT ++ , CTX_PTR fSuccessor ++ #endif ++ ) ++{ ++ CTX_PTR c; ++ CPpmd_State *s; ++ RESET_TEXT(0); ++ for (c = p->MaxContext; c != c1; c = SUFFIX(c)) ++ if (--(c->NumStats) == 0) ++ { ++ s = STATS(c); ++ c->Flags = (c->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40); ++ *ONE_STATE(c) = *s; ++ SpecialFreeUnit(p, s); ++ ONE_STATE(c)->Freq = (ONE_STATE(c)->Freq + 11) >> 3; ++ } ++ else ++ Refresh(p, c, (c->NumStats+3) >> 1, 0); ++ ++ for (; c != p->MinContext; c = SUFFIX(c)) ++ if (!c->NumStats) ++ ONE_STATE(c)->Freq -= ONE_STATE(c)->Freq >> 1; ++ else if ((c->SummFreq += 4) > 128 + 4 * c->NumStats) ++ Refresh(p, c, (c->NumStats + 2) >> 1, 1); ++ ++ #ifdef PPMD8_FREEZE_SUPPORT ++ if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) ++ { ++ p->MaxContext = fSuccessor; ++ p->GlueCount += !(p->Stamps[1] & 1); ++ } ++ else if (p->RestoreMethod == PPMD8_RESTORE_METHOD_FREEZE) ++ { ++ while (p->MaxContext->Suffix) ++ p->MaxContext = SUFFIX(p->MaxContext); ++ RemoveBinContexts(p, p->MaxContext, 0); ++ p->RestoreMethod++; ++ p->GlueCount = 0; ++ p->OrderFall = p->MaxOrder; ++ } ++ else ++ #endif ++ if (p->RestoreMethod == PPMD8_RESTORE_METHOD_RESTART || GetUsedMemory(p) < (p->Size >> 1)) ++ RestartModel(p); ++ else ++ { ++ while (p->MaxContext->Suffix) ++ p->MaxContext = SUFFIX(p->MaxContext); ++ do ++ { ++ CutOff(p, p->MaxContext, 0); ++ ExpandTextArea(p); ++ } ++ while (GetUsedMemory(p) > 3 * (p->Size >> 2)); ++ p->GlueCount = 0; ++ p->OrderFall = p->MaxOrder; ++ } ++} ++ ++static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c) ++{ ++ CPpmd_State upState; ++ Byte flags; ++ CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState); ++ /* fixed over Shkarin's code. Maybe it could work without + 1 too. */ ++ CPpmd_State *ps[PPMD8_MAX_ORDER + 1]; ++ unsigned numPs = 0; ++ ++ if (!skip) ++ ps[numPs++] = p->FoundState; ++ ++ while (c->Suffix) ++ { ++ CPpmd_Void_Ref successor; ++ CPpmd_State *s; ++ c = SUFFIX(c); ++ if (s1) ++ { ++ s = s1; ++ s1 = NULL; ++ } ++ else if (c->NumStats != 0) ++ { ++ for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++); ++ if (s->Freq < MAX_FREQ - 9) ++ { ++ s->Freq++; ++ c->SummFreq++; ++ } ++ } ++ else ++ { ++ s = ONE_STATE(c); ++ s->Freq += (!SUFFIX(c)->NumStats & (s->Freq < 24)); ++ } ++ successor = SUCCESSOR(s); ++ if (successor != upBranch) ++ { ++ c = CTX(successor); ++ if (numPs == 0) ++ return c; ++ break; ++ } ++ ps[numPs++] = s; ++ } ++ ++ upState.Symbol = *(const Byte *)Ppmd8_GetPtr(p, upBranch); ++ SetSuccessor(&upState, upBranch + 1); ++ flags = 0x10 * (p->FoundState->Symbol >= 0x40) + 0x08 * (upState.Symbol >= 0x40); ++ ++ if (c->NumStats == 0) ++ upState.Freq = ONE_STATE(c)->Freq; ++ else ++ { ++ UInt32 cf, s0; ++ CPpmd_State *s; ++ for (s = STATS(c); s->Symbol != upState.Symbol; s++); ++ cf = s->Freq - 1; ++ s0 = c->SummFreq - c->NumStats - cf; ++ upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0))); ++ } ++ ++ do ++ { ++ /* Create Child */ ++ CTX_PTR c1; /* = AllocContext(p); */ ++ if (p->HiUnit != p->LoUnit) ++ c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); ++ else if (p->FreeList[0] != 0) ++ c1 = (CTX_PTR)RemoveNode(p, 0); ++ else ++ { ++ c1 = (CTX_PTR)AllocUnitsRare(p, 0); ++ if (!c1) ++ return NULL; ++ } ++ c1->NumStats = 0; ++ c1->Flags = flags; ++ *ONE_STATE(c1) = upState; ++ c1->Suffix = REF(c); ++ SetSuccessor(ps[--numPs], REF(c1)); ++ c = c1; ++ } ++ while (numPs != 0); ++ ++ return c; ++} ++ ++static CTX_PTR ReduceOrder(CPpmd8 *p, CPpmd_State *s1, CTX_PTR c) ++{ ++ CPpmd_State *s = NULL; ++ CTX_PTR c1 = c; ++ CPpmd_Void_Ref upBranch = REF(p->Text); ++ ++ #ifdef PPMD8_FREEZE_SUPPORT ++ /* The BUG in Shkarin's code was fixed: ps could overflow in CUT_OFF mode. */ ++ CPpmd_State *ps[PPMD8_MAX_ORDER + 1]; ++ unsigned numPs = 0; ++ ps[numPs++] = p->FoundState; ++ #endif ++ ++ SetSuccessor(p->FoundState, upBranch); ++ p->OrderFall++; ++ ++ for (;;) ++ { ++ if (s1) ++ { ++ c = SUFFIX(c); ++ s = s1; ++ s1 = NULL; ++ } ++ else ++ { ++ if (!c->Suffix) ++ { ++ #ifdef PPMD8_FREEZE_SUPPORT ++ if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) ++ { ++ do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs); ++ RESET_TEXT(1); ++ p->OrderFall = 1; ++ } ++ #endif ++ return c; ++ } ++ c = SUFFIX(c); ++ if (c->NumStats) ++ { ++ if ((s = STATS(c))->Symbol != p->FoundState->Symbol) ++ do { s++; } while (s->Symbol != p->FoundState->Symbol); ++ if (s->Freq < MAX_FREQ - 9) ++ { ++ s->Freq += 2; ++ c->SummFreq += 2; ++ } ++ } ++ else ++ { ++ s = ONE_STATE(c); ++ s->Freq += (s->Freq < 32); ++ } ++ } ++ if (SUCCESSOR(s)) ++ break; ++ #ifdef PPMD8_FREEZE_SUPPORT ++ ps[numPs++] = s; ++ #endif ++ SetSuccessor(s, upBranch); ++ p->OrderFall++; ++ } ++ ++ #ifdef PPMD8_FREEZE_SUPPORT ++ if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) ++ { ++ c = CTX(SUCCESSOR(s)); ++ do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs); ++ RESET_TEXT(1); ++ p->OrderFall = 1; ++ return c; ++ } ++ else ++ #endif ++ if (SUCCESSOR(s) <= upBranch) ++ { ++ CTX_PTR successor; ++ CPpmd_State *s1 = p->FoundState; ++ p->FoundState = s; ++ ++ successor = CreateSuccessors(p, False, NULL, c); ++ if (successor == NULL) ++ SetSuccessor(s, 0); ++ else ++ SetSuccessor(s, REF(successor)); ++ p->FoundState = s1; ++ } ++ ++ if (p->OrderFall == 1 && c1 == p->MaxContext) ++ { ++ SetSuccessor(p->FoundState, SUCCESSOR(s)); ++ p->Text--; ++ } ++ if (SUCCESSOR(s) == 0) ++ return NULL; ++ return CTX(SUCCESSOR(s)); ++} ++ ++static void UpdateModel(CPpmd8 *p) ++{ ++ CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState); ++ CTX_PTR c; ++ unsigned s0, ns, fFreq = p->FoundState->Freq; ++ Byte flag, fSymbol = p->FoundState->Symbol; ++ CPpmd_State *s = NULL; ++ ++ if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0) ++ { ++ c = SUFFIX(p->MinContext); ++ ++ if (c->NumStats == 0) ++ { ++ s = ONE_STATE(c); ++ if (s->Freq < 32) ++ s->Freq++; ++ } ++ else ++ { ++ s = STATS(c); ++ if (s->Symbol != p->FoundState->Symbol) ++ { ++ do { s++; } while (s->Symbol != p->FoundState->Symbol); ++ if (s[0].Freq >= s[-1].Freq) ++ { ++ SwapStates(&s[0], &s[-1]); ++ s--; ++ } ++ } ++ if (s->Freq < MAX_FREQ - 9) ++ { ++ s->Freq += 2; ++ c->SummFreq += 2; ++ } ++ } ++ } ++ ++ c = p->MaxContext; ++ if (p->OrderFall == 0 && fSuccessor) ++ { ++ CTX_PTR cs = CreateSuccessors(p, True, s, p->MinContext); ++ if (cs == 0) ++ { ++ SetSuccessor(p->FoundState, 0); ++ RESTORE_MODEL(c, CTX(fSuccessor)); ++ } ++ else ++ { ++ SetSuccessor(p->FoundState, REF(cs)); ++ p->MaxContext = cs; ++ } ++ return; ++ } ++ ++ *p->Text++ = p->FoundState->Symbol; ++ successor = REF(p->Text); ++ if (p->Text >= p->UnitsStart) ++ { ++ RESTORE_MODEL(c, CTX(fSuccessor)); /* check it */ ++ return; ++ } ++ ++ if (!fSuccessor) ++ { ++ CTX_PTR cs = ReduceOrder(p, s, p->MinContext); ++ if (cs == NULL) ++ { ++ RESTORE_MODEL(c, 0); ++ return; ++ } ++ fSuccessor = REF(cs); ++ } ++ else if ((Byte *)Ppmd8_GetPtr(p, fSuccessor) < p->UnitsStart) ++ { ++ CTX_PTR cs = CreateSuccessors(p, False, s, p->MinContext); ++ if (cs == NULL) ++ { ++ RESTORE_MODEL(c, 0); ++ return; ++ } ++ fSuccessor = REF(cs); ++ } ++ ++ if (--p->OrderFall == 0) ++ { ++ successor = fSuccessor; ++ p->Text -= (p->MaxContext != p->MinContext); ++ } ++ #ifdef PPMD8_FREEZE_SUPPORT ++ else if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) ++ { ++ successor = fSuccessor; ++ RESET_TEXT(0); ++ p->OrderFall = 0; ++ } ++ #endif ++ ++ s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - fFreq; ++ flag = 0x08 * (fSymbol >= 0x40); ++ ++ for (; c != p->MinContext; c = SUFFIX(c)) ++ { ++ unsigned ns1; ++ UInt32 cf, sf; ++ if ((ns1 = c->NumStats) != 0) ++ { ++ if ((ns1 & 1) != 0) ++ { ++ /* Expand for one UNIT */ ++ unsigned oldNU = (ns1 + 1) >> 1; ++ unsigned i = U2I(oldNU); ++ if (i != U2I(oldNU + 1)) ++ { ++ void *ptr = AllocUnits(p, i + 1); ++ void *oldPtr; ++ if (!ptr) ++ { ++ RESTORE_MODEL(c, CTX(fSuccessor)); ++ return; ++ } ++ oldPtr = STATS(c); ++ MyMem12Cpy(ptr, oldPtr, oldNU); ++ InsertNode(p, oldPtr, i); ++ c->Stats = STATS_REF(ptr); ++ } ++ } ++ c->SummFreq = (UInt16)(c->SummFreq + (3 * ns1 + 1 < ns)); ++ } ++ else ++ { ++ CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0); ++ if (!s) ++ { ++ RESTORE_MODEL(c, CTX(fSuccessor)); ++ return; ++ } ++ *s = *ONE_STATE(c); ++ c->Stats = REF(s); ++ if (s->Freq < MAX_FREQ / 4 - 1) ++ s->Freq <<= 1; ++ else ++ s->Freq = MAX_FREQ - 4; ++ c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 2)); ++ } ++ cf = 2 * fFreq * (c->SummFreq + 6); ++ sf = (UInt32)s0 + c->SummFreq; ++ if (cf < 6 * sf) ++ { ++ cf = 1 + (cf > sf) + (cf >= 4 * sf); ++ c->SummFreq += 4; ++ } ++ else ++ { ++ cf = 4 + (cf > 9 * sf) + (cf > 12 * sf) + (cf > 15 * sf); ++ c->SummFreq = (UInt16)(c->SummFreq + cf); ++ } ++ { ++ CPpmd_State *s = STATS(c) + ns1 + 1; ++ SetSuccessor(s, successor); ++ s->Symbol = fSymbol; ++ s->Freq = (Byte)cf; ++ c->Flags |= flag; ++ c->NumStats = (Byte)(ns1 + 1); ++ } ++ } ++ p->MaxContext = p->MinContext = CTX(fSuccessor); ++} ++ ++static void Rescale(CPpmd8 *p) ++{ ++ unsigned i, adder, sumFreq, escFreq; ++ CPpmd_State *stats = STATS(p->MinContext); ++ CPpmd_State *s = p->FoundState; ++ { ++ CPpmd_State tmp = *s; ++ for (; s != stats; s--) ++ s[0] = s[-1]; ++ *s = tmp; ++ } ++ escFreq = p->MinContext->SummFreq - s->Freq; ++ s->Freq += 4; ++ adder = (p->OrderFall != 0 ++ #ifdef PPMD8_FREEZE_SUPPORT ++ || p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE ++ #endif ++ ); ++ s->Freq = (Byte)((s->Freq + adder) >> 1); ++ sumFreq = s->Freq; ++ ++ i = p->MinContext->NumStats; ++ do ++ { ++ escFreq -= (++s)->Freq; ++ s->Freq = (Byte)((s->Freq + adder) >> 1); ++ sumFreq += s->Freq; ++ if (s[0].Freq > s[-1].Freq) ++ { ++ CPpmd_State *s1 = s; ++ CPpmd_State tmp = *s1; ++ do ++ s1[0] = s1[-1]; ++ while (--s1 != stats && tmp.Freq > s1[-1].Freq); ++ *s1 = tmp; ++ } ++ } ++ while (--i); ++ ++ if (s->Freq == 0) ++ { ++ unsigned numStats = p->MinContext->NumStats; ++ unsigned n0, n1; ++ do { i++; } while ((--s)->Freq == 0); ++ escFreq += i; ++ p->MinContext->NumStats = (Byte)(p->MinContext->NumStats - i); ++ if (p->MinContext->NumStats == 0) ++ { ++ CPpmd_State tmp = *stats; ++ tmp.Freq = (Byte)((2 * tmp.Freq + escFreq - 1) / escFreq); ++ if (tmp.Freq > MAX_FREQ / 3) ++ tmp.Freq = MAX_FREQ / 3; ++ InsertNode(p, stats, U2I((numStats + 2) >> 1)); ++ p->MinContext->Flags = (p->MinContext->Flags & 0x10) + 0x08 * (tmp.Symbol >= 0x40); ++ *(p->FoundState = ONE_STATE(p->MinContext)) = tmp; ++ return; ++ } ++ n0 = (numStats + 2) >> 1; ++ n1 = (p->MinContext->NumStats + 2) >> 1; ++ if (n0 != n1) ++ p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1)); ++ p->MinContext->Flags &= ~0x08; ++ p->MinContext->Flags |= 0x08 * ((s = STATS(p->MinContext))->Symbol >= 0x40); ++ i = p->MinContext->NumStats; ++ do { p->MinContext->Flags |= 0x08*((++s)->Symbol >= 0x40); } while (--i); ++ } ++ p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1)); ++ p->MinContext->Flags |= 0x4; ++ p->FoundState = STATS(p->MinContext); ++} ++ ++CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq) ++{ ++ CPpmd_See *see; ++ if (p->MinContext->NumStats != 0xFF) ++ { ++ see = p->See[p->NS2Indx[p->MinContext->NumStats + 2] - 3] + ++ (p->MinContext->SummFreq > 11 * ((unsigned)p->MinContext->NumStats + 1)) + ++ 2 * (2 * (unsigned)p->MinContext->NumStats < ++ ((unsigned)SUFFIX(p->MinContext)->NumStats + numMasked1)) + ++ p->MinContext->Flags; ++ { ++ unsigned r = (see->Summ >> see->Shift); ++ see->Summ = (UInt16)(see->Summ - r); ++ *escFreq = r + (r == 0); ++ } ++ } ++ else ++ { ++ see = &p->DummySee; ++ *escFreq = 1; ++ } ++ return see; ++} ++ ++static void NextContext(CPpmd8 *p) ++{ ++ CTX_PTR c = CTX(SUCCESSOR(p->FoundState)); ++ if (p->OrderFall == 0 && (Byte *)c >= p->UnitsStart) ++ p->MinContext = p->MaxContext = c; ++ else ++ { ++ UpdateModel(p); ++ p->MinContext = p->MaxContext; ++ } ++} ++ ++void Ppmd8_Update1(CPpmd8 *p) ++{ ++ CPpmd_State *s = p->FoundState; ++ s->Freq += 4; ++ p->MinContext->SummFreq += 4; ++ if (s[0].Freq > s[-1].Freq) ++ { ++ SwapStates(&s[0], &s[-1]); ++ p->FoundState = --s; ++ if (s->Freq > MAX_FREQ) ++ Rescale(p); ++ } ++ NextContext(p); ++} ++ ++void Ppmd8_Update1_0(CPpmd8 *p) ++{ ++ p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq); ++ p->RunLength += p->PrevSuccess; ++ p->MinContext->SummFreq += 4; ++ if ((p->FoundState->Freq += 4) > MAX_FREQ) ++ Rescale(p); ++ NextContext(p); ++} ++ ++void Ppmd8_UpdateBin(CPpmd8 *p) ++{ ++ p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196)); ++ p->PrevSuccess = 1; ++ p->RunLength++; ++ NextContext(p); ++} ++ ++void Ppmd8_Update2(CPpmd8 *p) ++{ ++ p->MinContext->SummFreq += 4; ++ if ((p->FoundState->Freq += 4) > MAX_FREQ) ++ Rescale(p); ++ p->RunLength = p->InitRL; ++ UpdateModel(p); ++ p->MinContext = p->MaxContext; ++} ++ ++/* H->I changes: ++ NS2Indx ++ GlewCount, and Glue method ++ BinSum ++ See / EscFreq ++ CreateSuccessors updates more suffix contexts ++ UpdateModel consts. ++ PrevSuccess Update ++*/ +diff --git a/cut-n-paste/unarr/lzmasdk/Ppmd8.h b/cut-n-paste/unarr/lzmasdk/Ppmd8.h +new file mode 100644 +index 00000000..7dcfc916 +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/Ppmd8.h +@@ -0,0 +1,137 @@ ++/* Ppmd8.h -- PPMdI codec ++2011-01-27 : Igor Pavlov : Public domain ++This code is based on: ++ PPMd var.I (2002): Dmitry Shkarin : Public domain ++ Carryless rangecoder (1999): Dmitry Subbotin : Public domain */ ++ ++#ifndef __PPMD8_H ++#define __PPMD8_H ++ ++#include "Ppmd.h" ++ ++EXTERN_C_BEGIN ++ ++#define PPMD8_MIN_ORDER 2 ++#define PPMD8_MAX_ORDER 16 ++ ++struct CPpmd8_Context_; ++ ++typedef ++ #ifdef PPMD_32BIT ++ struct CPpmd8_Context_ * ++ #else ++ UInt32 ++ #endif ++ CPpmd8_Context_Ref; ++ ++#pragma pack(push, 1) ++ ++typedef struct CPpmd8_Context_ ++{ ++ Byte NumStats; ++ Byte Flags; ++ UInt16 SummFreq; ++ CPpmd_State_Ref Stats; ++ CPpmd8_Context_Ref Suffix; ++} CPpmd8_Context; ++ ++#pragma pack(pop) ++ ++#define Ppmd8Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq) ++ ++/* The BUG in Shkarin's code for FREEZE mode was fixed, but that fixed ++ code is not compatible with original code for some files compressed ++ in FREEZE mode. So we disable FREEZE mode support. */ ++ ++enum ++{ ++ PPMD8_RESTORE_METHOD_RESTART, ++ PPMD8_RESTORE_METHOD_CUT_OFF ++ #ifdef PPMD8_FREEZE_SUPPORT ++ , PPMD8_RESTORE_METHOD_FREEZE ++ #endif ++}; ++ ++typedef struct ++{ ++ CPpmd8_Context *MinContext, *MaxContext; ++ CPpmd_State *FoundState; ++ unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder; ++ Int32 RunLength, InitRL; /* must be 32-bit at least */ ++ ++ UInt32 Size; ++ UInt32 GlueCount; ++ Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart; ++ UInt32 AlignOffset; ++ unsigned RestoreMethod; ++ ++ /* Range Coder */ ++ UInt32 Range; ++ UInt32 Code; ++ UInt32 Low; ++ union ++ { ++ IByteIn *In; ++ IByteOut *Out; ++ } Stream; ++ ++ Byte Indx2Units[PPMD_NUM_INDEXES]; ++ Byte Units2Indx[128]; ++ CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES]; ++ UInt32 Stamps[PPMD_NUM_INDEXES]; ++ ++ Byte NS2BSIndx[256], NS2Indx[260]; ++ CPpmd_See DummySee, See[24][32]; ++ UInt16 BinSumm[25][64]; ++} CPpmd8; ++ ++void Ppmd8_Construct(CPpmd8 *p); ++Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size, ISzAlloc *alloc); ++void Ppmd8_Free(CPpmd8 *p, ISzAlloc *alloc); ++void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod); ++#define Ppmd8_WasAllocated(p) ((p)->Base != NULL) ++ ++ ++/* ---------- Internal Functions ---------- */ ++ ++extern const Byte PPMD8_kExpEscape[16]; ++ ++#ifdef PPMD_32BIT ++ #define Ppmd8_GetPtr(p, ptr) (ptr) ++ #define Ppmd8_GetContext(p, ptr) (ptr) ++ #define Ppmd8_GetStats(p, ctx) ((ctx)->Stats) ++#else ++ #define Ppmd8_GetPtr(p, offs) ((void *)((p)->Base + (offs))) ++ #define Ppmd8_GetContext(p, offs) ((CPpmd8_Context *)Ppmd8_GetPtr((p), (offs))) ++ #define Ppmd8_GetStats(p, ctx) ((CPpmd_State *)Ppmd8_GetPtr((p), ((ctx)->Stats))) ++#endif ++ ++void Ppmd8_Update1(CPpmd8 *p); ++void Ppmd8_Update1_0(CPpmd8 *p); ++void Ppmd8_Update2(CPpmd8 *p); ++void Ppmd8_UpdateBin(CPpmd8 *p); ++ ++#define Ppmd8_GetBinSumm(p) \ ++ &p->BinSumm[p->NS2Indx[Ppmd8Context_OneState(p->MinContext)->Freq - 1]][ \ ++ p->NS2BSIndx[Ppmd8_GetContext(p, p->MinContext->Suffix)->NumStats] + \ ++ p->PrevSuccess + p->MinContext->Flags + ((p->RunLength >> 26) & 0x20)] ++ ++CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked, UInt32 *scale); ++ ++ ++/* ---------- Decode ---------- */ ++ ++Bool Ppmd8_RangeDec_Init(CPpmd8 *p); ++#define Ppmd8_RangeDec_IsFinishedOK(p) ((p)->Code == 0) ++int Ppmd8_DecodeSymbol(CPpmd8 *p); /* returns: -1 as EndMarker, -2 as DataError */ ++ ++ ++/* ---------- Encode ---------- */ ++ ++#define Ppmd8_RangeEnc_Init(p) { (p)->Low = 0; (p)->Range = 0xFFFFFFFF; } ++void Ppmd8_RangeEnc_FlushData(CPpmd8 *p); ++void Ppmd8_EncodeSymbol(CPpmd8 *p, int symbol); /* symbol = -1 means EndMarker */ ++ ++EXTERN_C_END ++ ++#endif +diff --git a/cut-n-paste/unarr/lzmasdk/Ppmd8Dec.c b/cut-n-paste/unarr/lzmasdk/Ppmd8Dec.c +new file mode 100644 +index 00000000..317bd651 +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/Ppmd8Dec.c +@@ -0,0 +1,157 @@ ++/* Ppmd8Dec.c -- PPMdI Decoder ++2010-04-16 : Igor Pavlov : Public domain ++This code is based on: ++ PPMd var.I (2002): Dmitry Shkarin : Public domain ++ Carryless rangecoder (1999): Dmitry Subbotin : Public domain */ ++ ++#include "Precomp.h" ++ ++#include "Ppmd8.h" ++ ++#define kTop (1 << 24) ++#define kBot (1 << 15) ++ ++Bool Ppmd8_RangeDec_Init(CPpmd8 *p) ++{ ++ unsigned i; ++ p->Low = 0; ++ p->Range = 0xFFFFFFFF; ++ p->Code = 0; ++ for (i = 0; i < 4; i++) ++ p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In); ++ return (p->Code < 0xFFFFFFFF); ++} ++ ++static UInt32 RangeDec_GetThreshold(CPpmd8 *p, UInt32 total) ++{ ++ return p->Code / (p->Range /= total); ++} ++ ++static void RangeDec_Decode(CPpmd8 *p, UInt32 start, UInt32 size) ++{ ++ start *= p->Range; ++ p->Low += start; ++ p->Code -= start; ++ p->Range *= size; ++ ++ while ((p->Low ^ (p->Low + p->Range)) < kTop || ++ (p->Range < kBot && ((p->Range = (0 - p->Low) & (kBot - 1)), 1))) ++ { ++ p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In); ++ p->Range <<= 8; ++ p->Low <<= 8; ++ } ++} ++ ++#define MASK(sym) ((signed char *)charMask)[sym] ++ ++int Ppmd8_DecodeSymbol(CPpmd8 *p) ++{ ++ size_t charMask[256 / sizeof(size_t)]; ++ if (p->MinContext->NumStats != 0) ++ { ++ CPpmd_State *s = Ppmd8_GetStats(p, p->MinContext); ++ unsigned i; ++ UInt32 count, hiCnt; ++ if ((count = RangeDec_GetThreshold(p, p->MinContext->SummFreq)) < (hiCnt = s->Freq)) ++ { ++ Byte symbol; ++ RangeDec_Decode(p, 0, s->Freq); ++ p->FoundState = s; ++ symbol = s->Symbol; ++ Ppmd8_Update1_0(p); ++ return symbol; ++ } ++ p->PrevSuccess = 0; ++ i = p->MinContext->NumStats; ++ do ++ { ++ if ((hiCnt += (++s)->Freq) > count) ++ { ++ Byte symbol; ++ RangeDec_Decode(p, hiCnt - s->Freq, s->Freq); ++ p->FoundState = s; ++ symbol = s->Symbol; ++ Ppmd8_Update1(p); ++ return symbol; ++ } ++ } ++ while (--i); ++ if (count >= p->MinContext->SummFreq) ++ return -2; ++ RangeDec_Decode(p, hiCnt, p->MinContext->SummFreq - hiCnt); ++ PPMD_SetAllBitsIn256Bytes(charMask); ++ MASK(s->Symbol) = 0; ++ i = p->MinContext->NumStats; ++ do { MASK((--s)->Symbol) = 0; } while (--i); ++ } ++ else ++ { ++ UInt16 *prob = Ppmd8_GetBinSumm(p); ++ if (((p->Code / (p->Range >>= 14)) < *prob)) ++ { ++ Byte symbol; ++ RangeDec_Decode(p, 0, *prob); ++ *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob); ++ symbol = (p->FoundState = Ppmd8Context_OneState(p->MinContext))->Symbol; ++ Ppmd8_UpdateBin(p); ++ return symbol; ++ } ++ RangeDec_Decode(p, *prob, (1 << 14) - *prob); ++ *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob); ++ p->InitEsc = PPMD8_kExpEscape[*prob >> 10]; ++ PPMD_SetAllBitsIn256Bytes(charMask); ++ MASK(Ppmd8Context_OneState(p->MinContext)->Symbol) = 0; ++ p->PrevSuccess = 0; ++ } ++ for (;;) ++ { ++ CPpmd_State *ps[256], *s; ++ UInt32 freqSum, count, hiCnt; ++ CPpmd_See *see; ++ unsigned i, num, numMasked = p->MinContext->NumStats; ++ do ++ { ++ p->OrderFall++; ++ if (!p->MinContext->Suffix) ++ return -1; ++ p->MinContext = Ppmd8_GetContext(p, p->MinContext->Suffix); ++ } ++ while (p->MinContext->NumStats == numMasked); ++ hiCnt = 0; ++ s = Ppmd8_GetStats(p, p->MinContext); ++ i = 0; ++ num = p->MinContext->NumStats - numMasked; ++ do ++ { ++ int k = (int)(MASK(s->Symbol)); ++ hiCnt += (s->Freq & k); ++ ps[i] = s++; ++ i -= k; ++ } ++ while (i != num); ++ ++ see = Ppmd8_MakeEscFreq(p, numMasked, &freqSum); ++ freqSum += hiCnt; ++ count = RangeDec_GetThreshold(p, freqSum); ++ ++ if (count < hiCnt) ++ { ++ Byte symbol; ++ CPpmd_State **pps = ps; ++ for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++); ++ s = *pps; ++ RangeDec_Decode(p, hiCnt - s->Freq, s->Freq); ++ Ppmd_See_Update(see); ++ p->FoundState = s; ++ symbol = s->Symbol; ++ Ppmd8_Update2(p); ++ return symbol; ++ } ++ if (count >= freqSum) ++ return -2; ++ RangeDec_Decode(p, hiCnt, freqSum - hiCnt); ++ see->Summ = (UInt16)(see->Summ + freqSum); ++ do { MASK(ps[--i]->Symbol) = 0; } while (i != 0); ++ } ++} +diff --git a/cut-n-paste/unarr/lzmasdk/Precomp.h b/cut-n-paste/unarr/lzmasdk/Precomp.h +new file mode 100644 +index 00000000..895bc35e +--- /dev/null ++++ b/cut-n-paste/unarr/lzmasdk/Precomp.h +@@ -0,0 +1,15 @@ ++/* Precomp.h -- StdAfx ++2013-11-12 : Igor Pavlov : Public domain */ ++ ++#ifndef __7Z_PRECOMP_H ++#define __7Z_PRECOMP_H ++ ++/* #include "Compiler.h" */ ++#ifdef _MSC_VER ++#pragma warning(disable : 4456) // declaration of * hides previous local declaration ++#pragma warning(disable : 4457) // declaration of * hides function parameter ++#pragma warning(disable : 4996) // This function or variable may be unsafe ++#endif ++/* #include "7zTypes.h" */ ++ ++#endif +diff --git a/cut-n-paste/unarr/rar/filter-rar.c b/cut-n-paste/unarr/rar/filter-rar.c +new file mode 100644 +index 00000000..cbb2916d +--- /dev/null ++++ b/cut-n-paste/unarr/rar/filter-rar.c +@@ -0,0 +1,704 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++#include "rar.h" ++#include "rarvm.h" ++ ++/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADRARVirtualMachine.m */ ++/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADRAR30Filter.m */ ++ ++struct MemBitReader { ++ const uint8_t *bytes; ++ size_t length; ++ size_t offset; ++ uint64_t bits; ++ int available; ++ bool at_eof; ++}; ++ ++struct RARProgramCode { ++ RARProgram *prog; ++ uint8_t *staticdata; ++ uint32_t staticdatalen; ++ uint8_t *globalbackup; ++ uint32_t globalbackuplen; ++ uint64_t fingerprint; ++ uint32_t usagecount; ++ uint32_t oldfilterlength; ++ struct RARProgramCode *next; ++}; ++ ++struct RARFilter { ++ struct RARProgramCode *prog; ++ uint32_t initialregisters[8]; ++ uint8_t *globaldata; ++ uint32_t globaldatalen; ++ size_t blockstartpos; ++ uint32_t blocklength; ++ uint32_t filteredblockaddress; ++ uint32_t filteredblocklength; ++ struct RARFilter *next; ++}; ++ ++static bool br_fill(struct MemBitReader *br, int bits) ++{ ++ while (br->available < bits && br->offset < br->length) { ++ br->bits = (br->bits << 8) | br->bytes[br->offset++]; ++ br->available += 8; ++ } ++ if (bits > br->available) { ++ br->at_eof = true; ++ return false; ++ } ++ return true; ++} ++ ++static inline uint32_t br_bits(struct MemBitReader *br, int bits) ++{ ++ if (bits > br->available && (br->at_eof || !br_fill(br, bits))) ++ return 0; ++ return (uint32_t)((br->bits >> (br->available -= bits)) & (((uint64_t)1 << bits) - 1)); ++} ++ ++static inline bool br_available(struct MemBitReader *br, int bits) ++{ ++ return !br->at_eof && (bits <= br->available || br_fill(br, bits)); ++} ++ ++static uint32_t br_next_rarvm_number(struct MemBitReader *br) ++{ ++ uint32_t val; ++ switch (br_bits(br, 2)) { ++ case 0: ++ return br_bits(br, 4); ++ case 1: ++ val = br_bits(br, 8); ++ if (val >= 16) ++ return val; ++ return 0xFFFFFF00 | (val << 4) | br_bits(br, 4); ++ case 2: ++ return br_bits(br, 16); ++ default: ++ return br_bits(br, 32); ++ } ++} ++ ++static void bw_write32le(uint8_t *dst, uint32_t value) ++{ ++ dst[0] = value & 0xFF; ++ dst[1] = (value >> 8) & 0xFF; ++ dst[2] = (value >> 16) & 0xFF; ++ dst[3] = (value >> 24) & 0xFF; ++} ++ ++static void rar_delete_program(struct RARProgramCode *prog) ++{ ++ while (prog) { ++ struct RARProgramCode *next = prog->next; ++ RARDeleteProgram(prog->prog); ++ free(prog->staticdata); ++ free(prog->globalbackup); ++ free(prog); ++ prog = next; ++ } ++} ++ ++static bool rar_parse_operand(struct MemBitReader *br, uint8_t instruction, bool bytemode, uint32_t instrcount, uint8_t *addressmode, uint32_t *value) ++{ ++ if (br_bits(br, 1)) { ++ *addressmode = RARRegisterAddressingMode((uint8_t)br_bits(br, 3)); ++ *value = 0; ++ } ++ else if (br_bits(br, 1)) { ++ if (br_bits(br, 1)) { ++ if (br_bits(br, 1)) ++ *addressmode = RARAbsoluteAddressingMode; ++ else ++ *addressmode = RARIndexedAbsoluteAddressingMode((uint8_t)br_bits(br, 3)); ++ *value = br_next_rarvm_number(br); ++ } ++ else { ++ *addressmode = RARRegisterIndirectAddressingMode((uint8_t)br_bits(br, 3)); ++ *value = 0; ++ } ++ } ++ else { ++ *addressmode = RARImmediateAddressingMode; ++ if (!bytemode) ++ *value = br_next_rarvm_number(br); ++ else ++ *value = br_bits(br, 8); ++ if (instrcount != (uint32_t)-1 && RARInstructionIsRelativeJump(instruction)) { ++ if (*value >= 256) /* absolute address */ ++ *value -= 256; ++ else { /* relative address */ ++ if (*value >= 136) ++ *value -= 264; ++ else if (*value >= 16) ++ *value -= 8; ++ else if (*value >= 8) ++ *value -= 16; ++ *value += instrcount; ++ } ++ } ++ } ++ return !br->at_eof; ++} ++ ++static struct RARProgramCode *rar_compile_program(const uint8_t *bytes, size_t length) ++{ ++ struct MemBitReader br = { 0 }; ++ struct RARProgramCode *prog; ++ uint32_t instrcount = 0; ++ uint8_t xor; ++ size_t i; ++ ++ xor = 0; ++ for (i = 1; i < length; i++) ++ xor ^= bytes[i]; ++ if (!length || xor != bytes[0]) ++ return NULL; ++ ++ br.bytes = bytes; ++ br.length = length; ++ br.offset = 1; ++ ++ prog = calloc(1, sizeof(*prog)); ++ if (!prog) ++ return NULL; ++ prog->prog = RARCreateProgram(); ++ if (!prog->prog) { ++ rar_delete_program(prog); ++ return NULL; ++ } ++ prog->fingerprint = ar_crc32(0, bytes, length) | ((uint64_t)length << 32); ++ ++ if (br_bits(&br, 1)) { ++ prog->staticdatalen = br_next_rarvm_number(&br) + 1; ++ prog->staticdata = malloc(prog->staticdatalen); ++ if (!prog->staticdata) { ++ rar_delete_program(prog); ++ return NULL; ++ } ++ for (i = 0; i < prog->staticdatalen; i++) ++ prog->staticdata[i] = (uint8_t)br_bits(&br, 8); ++ } ++ ++ while (br_available(&br, 8)) { ++ bool ok = true; ++ uint8_t instruction = (uint8_t)br_bits(&br, 4); ++ bool bytemode = false; ++ int numargs = 0; ++ uint8_t addrmode1 = 0, addrmode2 = 0; ++ uint32_t value1 = 0, value2 = 0; ++ ++ if ((instruction & 0x08)) ++ instruction = ((instruction << 2) | (uint8_t)br_bits(&br, 2)) - 24; ++ if (RARInstructionHasByteMode(instruction)) ++ bytemode = br_bits(&br, 1) != 0; ++ ok = RARProgramAddInstr(prog->prog, instruction, bytemode); ++ numargs = NumberOfRARInstructionOperands(instruction); ++ if (ok && numargs >= 1) ++ ok = rar_parse_operand(&br, instruction, bytemode, instrcount, &addrmode1, &value1); ++ if (ok && numargs == 2) ++ ok = rar_parse_operand(&br, instruction, bytemode, (uint32_t)-1, &addrmode2, &value2); ++ if (ok) ++ ok = RARSetLastInstrOperands(prog->prog, addrmode1, value1, addrmode2, value2); ++ if (!ok) { ++ warn("Invalid RAR program instruction"); ++ rar_delete_program(prog); ++ return NULL; ++ } ++ instrcount++; ++ } ++ ++ if (!RARIsProgramTerminated(prog->prog)) { ++ if (!RARProgramAddInstr(prog->prog, RARRetInstruction, false)) { ++ rar_delete_program(prog); ++ return NULL; ++ } ++ } ++ ++ return prog; ++} ++ ++static bool rar_execute_filter_prog(struct RARFilter *filter, RARVirtualMachine *vm) ++{ ++ uint32_t newgloballength; ++ uint32_t globallength = filter->globaldatalen; ++ if (globallength > RARProgramSystemGlobalSize) ++ globallength = RARProgramSystemGlobalSize; ++ memcpy(&vm->memory[RARProgramSystemGlobalAddress], filter->globaldata, globallength); ++ if (filter->prog->staticdata) { ++ uint32_t staticlength = filter->prog->staticdatalen; ++ if (staticlength > RARProgramUserGlobalSize - globallength) ++ staticlength = RARProgramUserGlobalSize - globallength; ++ memcpy(&vm->memory[RARProgramUserGlobalAddress], filter->prog->staticdata, staticlength); ++ } ++ RARSetVirtualMachineRegisters(vm, filter->initialregisters); ++ ++ if (!RARExecuteProgram(vm, filter->prog->prog)) { ++ warn("Error while executing program in RAR VM"); ++ return false; ++ } ++ ++ newgloballength = RARVirtualMachineRead32(vm, RARProgramSystemGlobalAddress + 0x30); ++ if (newgloballength > RARProgramUserGlobalSize) ++ newgloballength = RARProgramUserGlobalSize; ++ if (newgloballength > 0) { ++ uint32_t newglobaldatalength = RARProgramSystemGlobalSize + newgloballength; ++ if (newglobaldatalength > filter->globaldatalen) { ++ uint8_t *newglobaldata = malloc(newglobaldatalength); ++ if (!newglobaldata) ++ return false; ++ free(filter->globaldata); ++ filter->globaldata = newglobaldata; ++ } ++ filter->globaldatalen = newglobaldatalength; ++ memcpy(filter->globaldata, &vm->memory[RARProgramSystemGlobalAddress], filter->globaldatalen); ++ } ++ else ++ filter->globaldatalen = 0; ++ ++ return true; ++} ++ ++static struct RARFilter *rar_create_filter(struct RARProgramCode *prog, const uint8_t *globaldata, uint32_t globaldatalen, uint32_t registers[8], size_t startpos, uint32_t length) ++{ ++ struct RARFilter *filter; ++ ++ filter = calloc(1, sizeof(*filter)); ++ if (!filter) ++ return NULL; ++ filter->prog = prog; ++ filter->globaldatalen = globaldatalen > RARProgramSystemGlobalSize ? globaldatalen : RARProgramSystemGlobalSize; ++ filter->globaldata = calloc(1, filter->globaldatalen); ++ if (!filter->globaldata) ++ return NULL; ++ if (globaldata) ++ memcpy(filter->globaldata, globaldata, globaldatalen); ++ if (registers) ++ memcpy(filter->initialregisters, registers, sizeof(filter->initialregisters)); ++ filter->blockstartpos = startpos; ++ filter->blocklength = length; ++ ++ return filter; ++} ++ ++static void rar_delete_filter(struct RARFilter *filter) ++{ ++ while (filter) { ++ struct RARFilter *next = filter->next; ++ free(filter->globaldata); ++ free(filter); ++ filter = next; ++ } ++} ++ ++static bool rar_execute_filter_delta(struct RARFilter *filter, RARVirtualMachine *vm) ++{ ++ uint32_t length = filter->initialregisters[4]; ++ uint32_t numchannels = filter->initialregisters[0]; ++ uint8_t *src, *dst; ++ uint32_t i, idx; ++ ++ if (length > RARProgramWorkSize / 2) ++ return false; ++ ++ src = &vm->memory[0]; ++ dst = &vm->memory[length]; ++ for (i = 0; i < numchannels; i++) { ++ uint8_t lastbyte = 0; ++ for (idx = i; idx < length; idx += numchannels) ++ lastbyte = dst[idx] = lastbyte - *src++; ++ } ++ ++ filter->filteredblockaddress = length; ++ filter->filteredblocklength = length; ++ ++ return true; ++} ++ ++static bool rar_execute_filter_e8(struct RARFilter *filter, RARVirtualMachine *vm, size_t pos, bool e9also) ++{ ++ uint32_t length = filter->initialregisters[4]; ++ uint32_t filesize = 0x1000000; ++ uint32_t i; ++ ++ if (length > RARProgramWorkSize || length < 4) ++ return false; ++ ++ for (i = 0; i <= length - 5; i++) { ++ if (vm->memory[i] == 0xE8 || (e9also && vm->memory[i] == 0xE9)) { ++ uint32_t currpos = (uint32_t)pos + i + 1; ++ int32_t address = (int32_t)RARVirtualMachineRead32(vm, i + 1); ++ if (address < 0 && currpos >= (uint32_t)-address) ++ RARVirtualMachineWrite32(vm, i + 1, address + filesize); ++ else if (address >= 0 && (uint32_t)address < filesize) ++ RARVirtualMachineWrite32(vm, i + 1, address - currpos); ++ i += 4; ++ } ++ } ++ ++ filter->filteredblockaddress = 0; ++ filter->filteredblocklength = length; ++ ++ return true; ++} ++ ++static bool rar_execute_filter_rgb(struct RARFilter *filter, RARVirtualMachine *vm) ++{ ++ uint32_t stride = filter->initialregisters[0]; ++ uint32_t byteoffset = filter->initialregisters[1]; ++ uint32_t blocklength = filter->initialregisters[4]; ++ uint8_t *src, *dst; ++ uint32_t i, j; ++ ++ if (blocklength > RARProgramWorkSize / 2 || stride > blocklength) ++ return false; ++ ++ src = &vm->memory[0]; ++ dst = &vm->memory[blocklength]; ++ for (i = 0; i < 3; i++) { ++ uint8_t byte = 0; ++ uint8_t *prev = dst + i - stride; ++ for (j = i; j < blocklength; j += 3) { ++ if (prev >= dst) { ++ uint32_t delta1 = abs(prev[3] - prev[0]); ++ uint32_t delta2 = abs(byte - prev[0]); ++ uint32_t delta3 = abs(prev[3] - prev[0] + byte - prev[0]); ++ if (delta1 > delta2 || delta1 > delta3) ++ byte = delta2 <= delta3 ? prev[3] : prev[0]; ++ } ++ byte -= *src++; ++ dst[j] = byte; ++ prev += 3; ++ } ++ } ++ for (i = byteoffset; i < blocklength - 2; i += 3) { ++ dst[i] += dst[i + 1]; ++ dst[i + 2] += dst[i + 1]; ++ } ++ ++ filter->filteredblockaddress = blocklength; ++ filter->filteredblocklength = blocklength; ++ ++ return true; ++} ++ ++static bool rar_execute_filter_audio(struct RARFilter *filter, RARVirtualMachine *vm) ++{ ++ uint32_t length = filter->initialregisters[4]; ++ uint32_t numchannels = filter->initialregisters[0]; ++ uint8_t *src, *dst; ++ uint32_t i, j; ++ ++ if (length > RARProgramWorkSize / 2) ++ return false; ++ ++ src = &vm->memory[0]; ++ dst = &vm->memory[length]; ++ for (i = 0; i < numchannels; i++) { ++ struct AudioState state; ++ memset(&state, 0, sizeof(state)); ++ for (j = i; j < length; j += numchannels) { ++ int8_t delta = (int8_t)*src++; ++ uint8_t predbyte, byte; ++ int prederror; ++ state.delta[2] = state.delta[1]; ++ state.delta[1] = state.lastdelta - state.delta[0]; ++ state.delta[0] = state.lastdelta; ++ predbyte = ((8 * state.lastbyte + state.weight[0] * state.delta[0] + state.weight[1] * state.delta[1] + state.weight[2] * state.delta[2]) >> 3) & 0xFF; ++ byte = (predbyte - delta) & 0xFF; ++ prederror = delta << 3; ++ state.error[0] += abs(prederror); ++ state.error[1] += abs(prederror - state.delta[0]); state.error[2] += abs(prederror + state.delta[0]); ++ state.error[3] += abs(prederror - state.delta[1]); state.error[4] += abs(prederror + state.delta[1]); ++ state.error[5] += abs(prederror - state.delta[2]); state.error[6] += abs(prederror + state.delta[2]); ++ state.lastdelta = (int8_t)(byte - state.lastbyte); ++ dst[j] = state.lastbyte = byte; ++ if (!(state.count++ & 0x1F)) { ++ uint8_t k, idx = 0; ++ for (k = 1; k < 7; k++) { ++ if (state.error[k] < state.error[idx]) ++ idx = k; ++ } ++ memset(state.error, 0, sizeof(state.error)); ++ switch (idx) { ++ case 1: if (state.weight[0] >= -16) state.weight[0]--; break; ++ case 2: if (state.weight[0] < 16) state.weight[0]++; break; ++ case 3: if (state.weight[1] >= -16) state.weight[1]--; break; ++ case 4: if (state.weight[1] < 16) state.weight[1]++; break; ++ case 5: if (state.weight[2] >= -16) state.weight[2]--; break; ++ case 6: if (state.weight[2] < 16) state.weight[2]++; break; ++ } ++ } ++ } ++ } ++ ++ filter->filteredblockaddress = length; ++ filter->filteredblocklength = length; ++ ++ return true; ++} ++ ++static bool rar_execute_filter(struct RARFilter *filter, RARVirtualMachine *vm, size_t pos) ++{ ++ if (filter->prog->fingerprint == 0x1D0E06077D) ++ return rar_execute_filter_delta(filter, vm); ++ if (filter->prog->fingerprint == 0x35AD576887) ++ return rar_execute_filter_e8(filter, vm, pos, false); ++ if (filter->prog->fingerprint == 0x393CD7E57E) ++ return rar_execute_filter_e8(filter, vm, pos, true); ++ if (filter->prog->fingerprint == 0x951C2C5DC8) ++ return rar_execute_filter_rgb(filter, vm); ++ if (filter->prog->fingerprint == 0xD8BC85E701) ++ return rar_execute_filter_audio(filter, vm); ++ log("Unknown parsing filter 0x%x%08x", (uint32_t)(filter->prog->fingerprint >> 32), (uint32_t)filter->prog->fingerprint); ++ ++ /* XADRAR30Filter.m @executeOnVirtualMachine claims that this is required */ ++ if (filter->prog->globalbackuplen > RARProgramSystemGlobalSize) { ++ uint8_t *newglobaldata = malloc(filter->prog->globalbackuplen); ++ if (newglobaldata) { ++ free(filter->globaldata); ++ filter->globaldata = newglobaldata; ++ filter->globaldatalen = filter->prog->globalbackuplen; ++ memcpy(filter->globaldata, filter->prog->globalbackup, filter->prog->globalbackuplen); ++ } ++ } ++ ++ filter->initialregisters[6] = (uint32_t)pos; ++ bw_write32le(&filter->globaldata[0x24], (uint32_t)pos); ++ bw_write32le(&filter->globaldata[0x28], (uint32_t)((uint64_t)pos >> 32)); ++ ++ if (!rar_execute_filter_prog(filter, vm)) ++ return false; ++ ++ filter->filteredblockaddress = RARVirtualMachineRead32(vm, RARProgramSystemGlobalAddress + 0x20) & RARProgramMemoryMask; ++ filter->filteredblocklength = RARVirtualMachineRead32(vm, RARProgramSystemGlobalAddress + 0x1C) & RARProgramMemoryMask; ++ if (filter->filteredblockaddress + filter->filteredblocklength >= RARProgramMemorySize) { ++ filter->filteredblockaddress = filter->filteredblocklength = 0; ++ return false; ++ } ++ ++ if (filter->globaldatalen > RARProgramSystemGlobalSize) { ++ uint8_t *newglobalbackup = malloc(filter->globaldatalen); ++ if (newglobalbackup) { ++ free(filter->prog->globalbackup); ++ filter->prog->globalbackup = newglobalbackup; ++ filter->prog->globalbackuplen = filter->globaldatalen; ++ memcpy(filter->prog->globalbackup, filter->globaldata, filter->globaldatalen); ++ } ++ } ++ else ++ filter->prog->globalbackuplen = 0; ++ ++ return true; ++} ++ ++bool rar_parse_filter(ar_archive_rar *rar, const uint8_t *bytes, uint16_t length, uint8_t flags) ++{ ++ struct ar_archive_rar_uncomp_v3 *uncomp = &rar->uncomp.state.v3; ++ struct ar_archive_rar_filters *filters = &uncomp->filters; ++ ++ struct MemBitReader br = { 0 }; ++ struct RARProgramCode *prog; ++ struct RARFilter *filter, **nextfilter; ++ ++ uint32_t numprogs, num, blocklength, globaldatalen; ++ uint8_t *globaldata; ++ size_t blockstartpos; ++ uint32_t registers[8] = { 0 }; ++ uint32_t i; ++ ++ br.bytes = bytes; ++ br.length = length; ++ ++ numprogs = 0; ++ for (prog = filters->progs; prog; prog = prog->next) ++ numprogs++; ++ ++ if ((flags & 0x80)) { ++ num = br_next_rarvm_number(&br); ++ if (num == 0) { ++ rar_delete_filter(filters->stack); ++ filters->stack = NULL; ++ rar_delete_program(filters->progs); ++ filters->progs = NULL; ++ } ++ else ++ num--; ++ if (num > numprogs) { ++ warn("Invalid program number"); ++ return false; ++ } ++ filters->lastfilternum = num; ++ } ++ else ++ num = filters->lastfilternum; ++ ++ prog = filters->progs; ++ for (i = 0; i < num; i++) ++ prog = prog->next; ++ if (prog) ++ prog->usagecount++; ++ ++ blockstartpos = br_next_rarvm_number(&br) + (size_t)lzss_position(&rar->uncomp.lzss); ++ if ((flags & 0x40)) ++ blockstartpos += 258; ++ if ((flags & 0x20)) ++ blocklength = br_next_rarvm_number(&br); ++ else ++ blocklength = prog ? prog->oldfilterlength : 0; ++ ++ registers[3] = RARProgramSystemGlobalAddress; ++ registers[4] = blocklength; ++ registers[5] = prog ? prog->usagecount : 0; ++ registers[7] = RARProgramMemorySize; ++ ++ if ((flags & 0x10)) { ++ uint8_t mask = (uint8_t)br_bits(&br, 7); ++ for (i = 0; i < 7; i++) { ++ if ((mask & (1 << i))) ++ registers[i] = br_next_rarvm_number(&br); ++ } ++ } ++ ++ if (!prog) { ++ uint32_t len = br_next_rarvm_number(&br); ++ uint8_t *bytecode; ++ struct RARProgramCode **next; ++ ++ if (len == 0 || len > 0x10000) { ++ warn("Invalid RARVM bytecode length"); ++ return false; ++ } ++ bytecode = malloc(len); ++ if (!bytecode) ++ return false; ++ for (i = 0; i < len; i++) ++ bytecode[i] = (uint8_t)br_bits(&br, 8); ++ prog = rar_compile_program(bytecode, len); ++ if (!prog) { ++ free(bytecode); ++ return false; ++ } ++ free(bytecode); ++ next = &filters->progs; ++ while (*next) ++ next = &(*next)->next; ++ *next = prog; ++ } ++ prog->oldfilterlength = blocklength; ++ ++ globaldata = NULL; ++ globaldatalen = 0; ++ if ((flags & 0x08)) { ++ globaldatalen = br_next_rarvm_number(&br); ++ if (globaldatalen > RARProgramUserGlobalSize) { ++ warn("Invalid RARVM data length"); ++ return false; ++ } ++ globaldata = malloc(globaldatalen + RARProgramSystemGlobalSize); ++ if (!globaldata) ++ return false; ++ for (i = 0; i < globaldatalen; i++) ++ globaldata[i + RARProgramSystemGlobalSize] = (uint8_t)br_bits(&br, 8); ++ } ++ ++ if (br.at_eof) { ++ free(globaldata); ++ return false; ++ } ++ ++ filter = rar_create_filter(prog, globaldata, globaldatalen, registers, blockstartpos, blocklength); ++ free(globaldata); ++ if (!filter) ++ return false; ++ ++ for (i = 0; i < 7; i++) ++ bw_write32le(&filter->globaldata[i * 4], registers[i]); ++ bw_write32le(&filter->globaldata[0x1C], blocklength); ++ bw_write32le(&filter->globaldata[0x20], 0); ++ bw_write32le(&filter->globaldata[0x2C], prog->usagecount); ++ ++ nextfilter = &filters->stack; ++ while (*nextfilter) ++ nextfilter = &(*nextfilter)->next; ++ *nextfilter = filter; ++ ++ if (!filters->stack->next) ++ filters->filterstart = blockstartpos; ++ ++ return true; ++} ++ ++bool rar_run_filters(ar_archive_rar *rar) ++{ ++ struct ar_archive_rar_filters *filters = &rar->uncomp.state.v3.filters; ++ struct RARFilter *filter = filters->stack; ++ size_t start = filters->filterstart; ++ size_t end = start + filter->blocklength; ++ uint32_t lastfilteraddress; ++ uint32_t lastfilterlength; ++ ++ filters->filterstart = SIZE_MAX; ++ end = (size_t)rar_expand(rar, end); ++ if (end != start + filter->blocklength) { ++ warn("Failed to expand the expected amout of bytes"); ++ return false; ++ } ++ ++ if (!filters->vm) { ++ filters->vm = calloc(1, sizeof(*filters->vm)); ++ if (!filters->vm) ++ return false; ++ } ++ ++ lzss_copy_bytes_from_window(&rar->uncomp.lzss, filters->vm->memory, start, filter->blocklength); ++ if (!rar_execute_filter(filter, filters->vm, rar->progress.bytes_done)) { ++ warn("Failed to execute parsing filter"); ++ return false; ++ } ++ ++ lastfilteraddress = filter->filteredblockaddress; ++ lastfilterlength = filter->filteredblocklength; ++ filters->stack = filter->next; ++ filter->next = NULL; ++ rar_delete_filter(filter); ++ ++ while ((filter = filters->stack) != NULL && filter->blockstartpos == filters->filterstart && filter->blocklength == lastfilterlength) { ++ memmove(&filters->vm->memory[0], &filters->vm->memory[lastfilteraddress], lastfilterlength); ++ if (!rar_execute_filter(filter, filters->vm, rar->progress.bytes_done)) { ++ warn("Failed to execute parsing filter"); ++ return false; ++ } ++ ++ lastfilteraddress = filter->filteredblockaddress; ++ lastfilterlength = filter->filteredblocklength; ++ filters->stack = filter->next; ++ filter->next = NULL; ++ rar_delete_filter(filter); ++ } ++ ++ if (filters->stack) { ++ if (filters->stack->blockstartpos < end) { ++ warn("Bad filter order"); ++ return false; ++ } ++ filters->filterstart = filters->stack->blockstartpos; ++ } ++ ++ filters->lastend = end; ++ filters->bytes = &filters->vm->memory[lastfilteraddress]; ++ filters->bytes_ready = lastfilterlength; ++ ++ return true; ++} ++ ++void rar_clear_filters(struct ar_archive_rar_filters *filters) ++{ ++ rar_delete_filter(filters->stack); ++ rar_delete_program(filters->progs); ++ free(filters->vm); ++} +diff --git a/cut-n-paste/unarr/rar/huffman-rar.c b/cut-n-paste/unarr/rar/huffman-rar.c +new file mode 100644 +index 00000000..c77eed93 +--- /dev/null ++++ b/cut-n-paste/unarr/rar/huffman-rar.c +@@ -0,0 +1,142 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADPrefixCode.m */ ++ ++#include "rar.h" ++ ++bool rar_new_node(struct huffman_code *code) ++{ ++ if (!code->tree) { ++ code->minlength = INT_MAX; ++ code->maxlength = INT_MIN; ++ } ++ if (code->numentries + 1 >= code->capacity) { ++ /* in my small file sample, 1024 is the value needed most often */ ++ int new_capacity = code->capacity ? code->capacity * 2 : 1024; ++ void *new_tree = calloc(new_capacity, sizeof(*code->tree)); ++ if (!new_tree) { ++ warn("OOM during decompression"); ++ return false; ++ } ++ memcpy(new_tree, code->tree, code->capacity * sizeof(*code->tree)); ++ free(code->tree); ++ code->tree = new_tree; ++ code->capacity = new_capacity; ++ } ++ code->tree[code->numentries].branches[0] = -1; ++ code->tree[code->numentries].branches[1] = -2; ++ code->numentries++; ++ return true; ++} ++ ++bool rar_add_value(struct huffman_code *code, int value, int codebits, int length) ++{ ++ int lastnode, bitpos, bit; ++ ++ free(code->table); ++ code->table = NULL; ++ ++ if (length > code->maxlength) ++ code->maxlength = length; ++ if (length < code->minlength) ++ code->minlength = length; ++ ++ lastnode = 0; ++ for (bitpos = length - 1; bitpos >= 0; bitpos--) { ++ bit = (codebits >> bitpos) & 1; ++ if (rar_is_leaf_node(code, lastnode)) { ++ warn("Invalid data in bitstream"); /* prefix found */ ++ return false; ++ } ++ if (code->tree[lastnode].branches[bit] < 0) { ++ if (!rar_new_node(code)) ++ return false; ++ code->tree[lastnode].branches[bit] = code->numentries - 1; ++ } ++ lastnode = code->tree[lastnode].branches[bit]; ++ } ++ ++ if (code->tree[lastnode].branches[0] != -1 || code->tree[lastnode].branches[1] != -2) { ++ warn("Invalid data in bitstream"); /* prefix found */ ++ return false; ++ } ++ code->tree[lastnode].branches[0] = code->tree[lastnode].branches[1] = value; ++ return true; ++} ++ ++bool rar_create_code(struct huffman_code *code, uint8_t *lengths, int numsymbols) ++{ ++ int symbolsleft = numsymbols; ++ int codebits = 0; ++ int i, j; ++ ++ if (!rar_new_node(code)) ++ return false; ++ ++ for (i = 1; i <= 0x0F; i++) { ++ for (j = 0; j < numsymbols; j++) { ++ if (lengths[j] != i) ++ continue; ++ if (!rar_add_value(code, j, codebits, i)) ++ return false; ++ if (--symbolsleft <= 0) ++ return true; ++ codebits++; ++ } ++ codebits <<= 1; ++ } ++ return true; ++} ++ ++static bool rar_make_table_rec(struct huffman_code *code, int node, int offset, int depth, int maxdepth) ++{ ++ int currtablesize = 1 << (maxdepth - depth); ++ ++ if (node < 0 || code->numentries <= node) { ++ warn("Invalid data in bitstream"); /* invalid location to Huffman tree specified */ ++ return false; ++ } ++ ++ if (rar_is_leaf_node(code, node)) { ++ int i; ++ for (i = 0; i < currtablesize; i++) { ++ code->table[offset + i].length = depth; ++ code->table[offset + i].value = code->tree[node].branches[0]; ++ } ++ } ++ else if (depth == maxdepth) { ++ code->table[offset].length = maxdepth + 1; ++ code->table[offset].value = node; ++ } ++ else { ++ if (!rar_make_table_rec(code, code->tree[node].branches[0], offset, depth + 1, maxdepth)) ++ return false; ++ if (!rar_make_table_rec(code, code->tree[node].branches[1], offset + currtablesize / 2, depth + 1, maxdepth)) ++ return false; ++ } ++ return true; ++} ++ ++bool rar_make_table(struct huffman_code *code) ++{ ++ if (code->minlength <= code->maxlength && code->maxlength <= 10) ++ code->tablesize = code->maxlength; ++ else ++ code->tablesize = 10; ++ ++ code->table = calloc(1ULL << code->tablesize, sizeof(*code->table)); ++ if (!code->table) { ++ warn("OOM during decompression"); ++ return false; ++ } ++ ++ return rar_make_table_rec(code, 0, 0, 0, code->tablesize); ++} ++ ++void rar_free_code(struct huffman_code *code) ++{ ++ free(code->tree); ++ free(code->table); ++ memset(code, 0, sizeof(*code)); ++} +diff --git a/cut-n-paste/unarr/rar/lzss.h b/cut-n-paste/unarr/rar/lzss.h +new file mode 100644 +index 00000000..580fe4c5 +--- /dev/null ++++ b/cut-n-paste/unarr/rar/lzss.h +@@ -0,0 +1,88 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/LZSS.h */ ++ ++#ifndef rar_lzss_h ++#define rar_lzss_h ++ ++#include ++#include ++#include ++#include ++ ++#if defined(_MSC_VER) && !defined(inline) ++#define inline __inline ++#endif ++ ++typedef struct { ++ uint8_t *window; ++ int mask; ++ int64_t position; ++} LZSS; ++ ++static inline int64_t lzss_position(LZSS *self) { return self->position; } ++ ++static inline int lzss_mask(LZSS *self) { return self->mask; } ++ ++static inline int lzss_size(LZSS *self) { return self->mask + 1; } ++ ++static inline uint8_t *lzss_window_pointer(LZSS *self) { return self->window; } ++ ++static inline int lzss_offset_for_position(LZSS *self, int64_t pos) { return (int)(pos & self->mask); } ++ ++static inline uint8_t *lzss_window_pointer_for_position(LZSS *self, int64_t pos) { return &self->window[lzss_offset_for_position(self, pos)]; } ++ ++static inline int lzss_current_window_offset(LZSS *self) { return lzss_offset_for_position(self, self->position); } ++ ++static inline uint8_t *lzss_current_window_pointer(LZSS *self) { return lzss_window_pointer_for_position(self, self->position); } ++ ++static inline int64_t lzss_next_window_edge_after_position(LZSS *self, int64_t pos) { return (pos + lzss_size(self)) & ~(int64_t)lzss_mask(self); } ++ ++static inline int64_t lzss_next_window_edge(LZSS *self) { return lzss_next_window_edge_after_position(self, self->position); } ++ ++static inline uint8_t lzss_get_byte_from_window(LZSS *self, int64_t pos) { return *lzss_window_pointer_for_position(self, pos); } ++ ++static inline void lzss_emit_literal(LZSS *self, uint8_t literal) { ++ /* self->window[(self->position & self->mask)] = literal; */ ++ *lzss_current_window_pointer(self) = literal; ++ self->position++; ++} ++ ++static inline void lzss_emit_match(LZSS *self, int offset, int length) { ++ int windowoffs = lzss_current_window_offset(self); ++ int i; ++ for (i = 0; i < length; i++) { ++ self->window[(windowoffs + i) & lzss_mask(self)] = self->window[(windowoffs + i - offset) & lzss_mask(self)]; ++ } ++ self->position += length; ++} ++ ++static inline void lzss_copy_bytes_from_window(LZSS *self, uint8_t *buffer, int64_t startpos, int length) { ++ int windowoffs = lzss_offset_for_position(self, startpos); ++ int firstpart = lzss_size(self) - windowoffs; ++ if (length <= firstpart) { ++ /* Request fits inside window */ ++ memcpy(buffer, &self->window[windowoffs], length); ++ } ++ else { ++ /* Request wraps around window */ ++ memcpy(buffer, &self->window[windowoffs], firstpart); ++ memcpy(buffer + firstpart, &self->window[0], length - firstpart); ++ } ++} ++ ++static inline bool lzss_initialize(LZSS *self, int windowsize) { ++ self->window = malloc(windowsize); ++ if (!self->window) ++ return false; ++ ++ self->mask = windowsize - 1; /* Assume windows are power-of-two sized! */ ++ memset(self->window, 0, lzss_size(self)); ++ self->position = 0; ++ return true; ++} ++ ++static inline void lzss_cleanup(LZSS *self) { free(self->window); } ++ ++#endif +diff --git a/cut-n-paste/unarr/rar/parse-rar.c b/cut-n-paste/unarr/rar/parse-rar.c +new file mode 100644 +index 00000000..f41534c6 +--- /dev/null ++++ b/cut-n-paste/unarr/rar/parse-rar.c +@@ -0,0 +1,236 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADRARParser.m */ ++ ++#include "rar.h" ++ ++static inline uint8_t uint8le(unsigned char *data) { return data[0]; } ++static inline uint16_t uint16le(unsigned char *data) { return data[0] | data[1] << 8; } ++static inline uint32_t uint32le(unsigned char *data) { return data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24; } ++ ++bool rar_parse_header(ar_archive *ar, struct rar_header *header) ++{ ++ unsigned char header_data[7]; ++ size_t read = ar_read(ar->stream, header_data, sizeof(header_data)); ++ if (read == 0) { ++ ar->at_eof = true; ++ return false; ++ } ++ if (read < sizeof(header_data)) ++ return false; ++ ++ header->crc = uint16le(header_data + 0); ++ header->type = uint8le(header_data + 2); ++ header->flags = uint16le(header_data + 3); ++ header->size = uint16le(header_data + 5); ++ ++ header->datasize = 0; ++ if ((header->flags & LHD_LONG_BLOCK) || header->type == 0x74) { ++ unsigned char size_data[4]; ++ if (!(header->flags & LHD_LONG_BLOCK)) ++ log("File header without LHD_LONG_BLOCK set"); ++ read += ar_read(ar->stream, size_data, sizeof(size_data)); ++ if (read < sizeof(header_data) + sizeof(size_data)) ++ return false; ++ header->datasize = uint32le(size_data); ++ } ++ ++ if (header->size < read) { ++ warn("Invalid header size %d", header->size); ++ return false; ++ } ++ ++ return true; ++} ++ ++bool rar_check_header_crc(ar_archive *ar) ++{ ++ unsigned char buffer[256]; ++ uint16_t crc16, size; ++ uint32_t crc32; ++ ++ if (!ar_seek(ar->stream, ar->entry_offset, SEEK_SET)) ++ return false; ++ if (ar_read(ar->stream, buffer, 7) != 7) ++ return false; ++ ++ crc16 = uint16le(buffer + 0); ++ size = uint16le(buffer + 5); ++ if (size < 7) ++ return false; ++ size -= 7; ++ ++ crc32 = ar_crc32(0, buffer + 2, 5); ++ while (size > 0) { ++ if (ar_read(ar->stream, buffer, smin(size, sizeof(buffer))) != smin(size, sizeof(buffer))) ++ return false; ++ crc32 = ar_crc32(crc32, buffer, smin(size, sizeof(buffer))); ++ size -= (uint16_t)smin(size, sizeof(buffer)); ++ } ++ return (crc32 & 0xFFFF) == crc16; ++} ++ ++bool rar_parse_header_entry(ar_archive_rar *rar, struct rar_header *header, struct rar_entry *entry) ++{ ++ unsigned char data[21]; ++ if (ar_read(rar->super.stream, data, sizeof(data)) != sizeof(data)) ++ return false; ++ ++ entry->size = uint32le(data + 0); ++ entry->os = uint8le(data + 4); ++ entry->crc = uint32le(data + 5); ++ entry->dosdate = uint32le(data + 9); ++ entry->version = uint8le(data + 13); ++ entry->method = uint8le(data + 14); ++ entry->namelen = uint16le(data + 15); ++ entry->attrs = uint32le(data + 17); ++ if ((header->flags & LHD_LARGE)) { ++ unsigned char more_data[8]; ++ if (ar_read(rar->super.stream, more_data, sizeof(more_data)) != sizeof(more_data)) ++ return false; ++ header->datasize += (uint64_t)uint32le(more_data + 0); ++ entry->size += (uint64_t)uint32le(more_data + 4); ++ } ++ if (!ar_skip(rar->super.stream, entry->namelen)) ++ return false; ++ if ((header->flags & LHD_SALT)) { ++ log("Skipping LHD_SALT"); ++ ar_skip(rar->super.stream, 8); ++ } ++ ++ rar->entry.version = entry->version; ++ rar->entry.method = entry->method; ++ rar->entry.crc = entry->crc; ++ rar->entry.header_size = header->size; ++ rar->entry.solid = entry->version < 20 ? (rar->archive_flags & MHD_SOLID) : (header->flags & LHD_SOLID); ++ free(rar->entry.name); ++ rar->entry.name = NULL; ++ ++ return true; ++} ++ ++/* this seems to be what RAR considers "Unicode" */ ++static char *rar_conv_unicode_to_utf8(const char *data, uint16_t len) ++{ ++#define Check(cond) if (!(cond)) { free(str); return NULL; } else ((void)0) ++ ++ uint8_t highbyte, flagbyte, flagbits, size, length, i; ++ const uint8_t *in = (uint8_t *)data + strlen(data) + 1; ++ const uint8_t *end_in = (uint8_t *)data + len; ++ char *str = calloc(len + 1, 3); ++ char *out = str; ++ char *end_out = str + len * 3; ++ ++ if (!str) ++ return NULL; ++ if (end_in - in <= 1) { ++ memcpy(str, data, len); ++ return str; ++ } ++ ++ highbyte = *in++; ++ flagbyte = 0; ++ flagbits = 0; ++ size = 0; ++ ++ while (in < end_in && out < end_out) { ++ if (flagbits == 0) { ++ flagbyte = *in++; ++ flagbits = 8; ++ } ++ flagbits -= 2; ++ switch ((flagbyte >> flagbits) & 3) { ++ case 0: ++ Check(in + 1 <= end_in); ++ out += ar_conv_rune_to_utf8(*in++, out, end_out - out); ++ size++; ++ break; ++ case 1: ++ Check(in + 1 <= end_in); ++ out += ar_conv_rune_to_utf8(((uint16_t)highbyte << 8) | *in++, out, end_out - out); ++ size++; ++ break; ++ case 2: ++ Check(in + 2 <= end_in); ++ out += ar_conv_rune_to_utf8(((uint16_t)*(in + 1) << 8) | *in, out, end_out - out); ++ in += 2; ++ size++; ++ break; ++ case 3: ++ Check(in + 1 <= end_in); ++ length = *in++; ++ if ((length & 0x80)) { ++ uint8_t correction = *in++; ++ for (i = 0; i < (length & 0x7F) + 2; i++) { ++ Check(size < len); ++ out += ar_conv_rune_to_utf8(((uint16_t)highbyte << 8) | (data[size] + (correction & 0xFF)), out, end_out - out); ++ size++; ++ } ++ } ++ else { ++ for (i = 0; i < (length & 0x7F) + 2; i++) { ++ Check(size < len); ++ out += ar_conv_rune_to_utf8(data[size], out, end_out - out); ++ size++; ++ } ++ } ++ break; ++ } ++ } ++ ++ return str; ++ ++#undef Check ++} ++ ++const char *rar_get_name(ar_archive *ar) ++{ ++ ar_archive_rar *rar = (ar_archive_rar *)ar; ++ if (!rar->entry.name) { ++ unsigned char data[21]; ++ uint16_t namelen; ++ char *name; ++ ++ struct rar_header header; ++ if (!ar_seek(ar->stream, ar->entry_offset, SEEK_SET)) ++ return NULL; ++ if (!rar_parse_header(ar, &header)) ++ return NULL; ++ if (ar_read(ar->stream, data, sizeof(data)) != sizeof(data)) ++ return NULL; ++ if ((header.flags & LHD_LARGE) && !ar_skip(ar->stream, 8)) ++ return NULL; ++ ++ namelen = uint16le(data + 15); ++ name = malloc(namelen + 1); ++ if (!name || ar_read(ar->stream, name, namelen) != namelen) { ++ free(name); ++ return NULL; ++ } ++ name[namelen] = '\0'; ++ ++ if (!(header.flags & LHD_UNICODE)) { ++ rar->entry.name = ar_conv_dos_to_utf8(name); ++ free(name); ++ } ++ else if (namelen == strlen(name)) { ++ rar->entry.name = name; ++ } ++ else { ++ rar->entry.name = rar_conv_unicode_to_utf8(name, namelen); ++ free(name); ++ } ++ /* normalize path separators */ ++ if (rar->entry.name) { ++ char *p = rar->entry.name; ++ while ((p = strchr(p, '\\')) != NULL) { ++ *p = '/'; ++ } ++ } ++ ++ if (!ar_seek(ar->stream, ar->entry_offset + rar->entry.header_size, SEEK_SET)) ++ warn("Couldn't seek back to the end of the entry header"); ++ } ++ return rar->entry.name; ++} +diff --git a/cut-n-paste/unarr/rar/rar.c b/cut-n-paste/unarr/rar/rar.c +new file mode 100644 +index 00000000..40cafcfc +--- /dev/null ++++ b/cut-n-paste/unarr/rar/rar.c +@@ -0,0 +1,223 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++#include "rar.h" ++ ++static void rar_close(ar_archive *ar) ++{ ++ ar_archive_rar *rar = (ar_archive_rar *)ar; ++ free(rar->entry.name); ++ rar_clear_uncompress(&rar->uncomp); ++} ++ ++static bool rar_parse_entry(ar_archive *ar, off64_t offset) ++{ ++ ar_archive_rar *rar = (ar_archive_rar *)ar; ++ struct rar_header header; ++ struct rar_entry entry; ++ bool out_of_order = offset != ar->entry_offset_next; ++ ++ if (!ar_seek(ar->stream, offset, SEEK_SET)) { ++ warn("Couldn't seek to offset %" PRIi64, offset); ++ return false; ++ } ++ ++ for (;;) { ++ ar->entry_offset = ar_tell(ar->stream); ++ ar->entry_size_uncompressed = 0; ++ ++ if (!rar_parse_header(ar, &header)) ++ return false; ++ ++ ar->entry_offset_next = ar->entry_offset + header.size + header.datasize; ++ if (ar->entry_offset_next < ar->entry_offset + header.size) { ++ warn("Integer overflow due to overly large data size"); ++ return false; ++ } ++ ++ switch (header.type) { ++ case TYPE_MAIN_HEADER: ++ if ((header.flags & MHD_PASSWORD)) { ++ warn("Encrypted archives aren't supported"); ++ return false; ++ } ++ ar_skip(ar->stream, 6 /* reserved data */); ++ if ((header.flags & MHD_ENCRYPTVER)) { ++ log("MHD_ENCRYPTVER is set"); ++ ar_skip(ar->stream, 1); ++ } ++ if ((header.flags & MHD_COMMENT)) ++ log("MHD_COMMENT is set"); ++ if (ar_tell(ar->stream) - ar->entry_offset > header.size) { ++ warn("Invalid RAR header size: %d", header.size); ++ return false; ++ } ++ rar->archive_flags = header.flags; ++ break; ++ ++ case TYPE_FILE_ENTRY: ++ if (!rar_parse_header_entry(rar, &header, &entry)) ++ return false; ++ if ((header.flags & LHD_PASSWORD)) ++ warn("Encrypted entries will fail to uncompress"); ++ if ((header.flags & LHD_DIRECTORY) == LHD_DIRECTORY) { ++ if (header.datasize == 0) { ++ log("Skipping directory entry \"%s\"", rar_get_name(ar)); ++ break; ++ } ++ warn("Can't skip directory entries containing data"); ++ } ++ if ((header.flags & (LHD_SPLIT_BEFORE | LHD_SPLIT_AFTER))) ++ warn("Splitting files isn't really supported"); ++ ar->entry_size_uncompressed = (size_t)entry.size; ++ ar->entry_filetime = ar_conv_dosdate_to_filetime(entry.dosdate); ++ if (!rar->entry.solid || rar->entry.method == METHOD_STORE || out_of_order) { ++ rar_clear_uncompress(&rar->uncomp); ++ memset(&rar->solid, 0, sizeof(rar->solid)); ++ } ++ else { ++ br_clear_leftover_bits(&rar->uncomp); ++ } ++ ++ rar->solid.restart = rar->entry.solid && (out_of_order || !rar->solid.part_done); ++ rar->solid.part_done = !ar->entry_size_uncompressed; ++ rar->progress.data_left = (size_t)header.datasize; ++ rar->progress.bytes_done = 0; ++ rar->progress.crc = 0; ++ ++ /* TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader) */ ++ if (!rar_check_header_crc(ar)) ++ warn("Invalid header checksum @%" PRIi64, ar->entry_offset); ++ if (ar_tell(ar->stream) != ar->entry_offset + rar->entry.header_size) { ++ warn("Couldn't seek to offset %" PRIi64, ar->entry_offset + rar->entry.header_size); ++ return false; ++ } ++ return true; ++ ++ case TYPE_NEWSUB: ++ log("Skipping newsub header @%" PRIi64, ar->entry_offset); ++ break; ++ ++ case TYPE_END_OF_ARCHIVE: ++ ar->at_eof = true; ++ return false; ++ ++ default: ++ log("Unknown RAR header type %02x", header.type); ++ break; ++ } ++ ++ /* TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader) */ ++ if (!rar_check_header_crc(ar)) ++ warn("Invalid header checksum @%" PRIi64, ar->entry_offset); ++ if (!ar_seek(ar->stream, ar->entry_offset_next, SEEK_SET)) { ++ warn("Couldn't seek to offset %" PRIi64, ar->entry_offset_next); ++ return false; ++ } ++ } ++} ++ ++static bool rar_copy_stored(ar_archive_rar *rar, void *buffer, size_t count) ++{ ++ if (count > rar->progress.data_left) { ++ warn("Unexpected EOS in stored data"); ++ return false; ++ } ++ if (ar_read(rar->super.stream, buffer, count) != count) { ++ warn("Unexpected EOF in stored data"); ++ return false; ++ } ++ rar->progress.data_left -= count; ++ rar->progress.bytes_done += count; ++ return true; ++} ++ ++static bool rar_restart_solid(ar_archive *ar) ++{ ++ ar_archive_rar *rar = (ar_archive_rar *)ar; ++ off64_t current_offset = ar->entry_offset; ++ log("Restarting decompression for solid entry"); ++ if (!ar_parse_entry_at(ar, ar->entry_offset_first)) { ++ ar_parse_entry_at(ar, current_offset); ++ return false; ++ } ++ while (ar->entry_offset < current_offset) { ++ size_t size = ar->entry_size_uncompressed; ++ rar->solid.restart = false; ++ while (size > 0) { ++ unsigned char buffer[1024]; ++ size_t count = smin(size, sizeof(buffer)); ++ if (!ar_entry_uncompress(ar, buffer, count)) { ++ ar_parse_entry_at(ar, current_offset); ++ return false; ++ } ++ size -= count; ++ } ++ if (!ar_parse_entry(ar)) { ++ ar_parse_entry_at(ar, current_offset); ++ return false; ++ } ++ } ++ rar->solid.restart = false; ++ return true; ++} ++ ++static bool rar_uncompress(ar_archive *ar, void *buffer, size_t count) ++{ ++ ar_archive_rar *rar = (ar_archive_rar *)ar; ++ if (count > ar->entry_size_uncompressed - rar->progress.bytes_done) { ++ warn("Requesting too much data (%" PRIuPTR " < %" PRIuPTR ")", ar->entry_size_uncompressed - rar->progress.bytes_done, count); ++ return false; ++ } ++ if (rar->entry.method == METHOD_STORE) { ++ if (!rar_copy_stored(rar, buffer, count)) ++ return false; ++ } ++ else if (rar->entry.method == METHOD_FASTEST || rar->entry.method == METHOD_FAST || ++ rar->entry.method == METHOD_NORMAL || rar->entry.method == METHOD_GOOD || ++ rar->entry.method == METHOD_BEST) { ++ if (rar->solid.restart && !rar_restart_solid(ar)) { ++ warn("Failed to produce the required solid decompression state"); ++ return false; ++ } ++ if (!rar_uncompress_part(rar, buffer, count)) ++ return false; ++ } ++ else { ++ warn("Unknown compression method %#02x", rar->entry.method); ++ return false; ++ } ++ ++ rar->progress.crc = ar_crc32(rar->progress.crc, buffer, count); ++ if (rar->progress.bytes_done < ar->entry_size_uncompressed) ++ return true; ++ if (rar->progress.data_left) ++ log("Compressed block has more data than required"); ++ rar->solid.part_done = true; ++ rar->solid.size_total += rar->progress.bytes_done; ++ if (rar->progress.crc != rar->entry.crc) { ++ warn("Checksum of extracted data doesn't match"); ++ return false; ++ } ++ return true; ++} ++ ++ar_archive *ar_open_rar_archive(ar_stream *stream) ++{ ++ char signature[FILE_SIGNATURE_SIZE]; ++ if (!ar_seek(stream, 0, SEEK_SET)) ++ return NULL; ++ if (ar_read(stream, signature, sizeof(signature)) != sizeof(signature)) ++ return NULL; ++ if (memcmp(signature, "Rar!\x1A\x07\x00", sizeof(signature)) != 0) { ++ if (memcmp(signature, "Rar!\x1A\x07\x01", sizeof(signature)) == 0) ++ warn("RAR 5 format isn't supported"); ++ else if (memcmp(signature, "RE~^", 4) == 0) ++ warn("Ancient RAR format isn't supported"); ++ else if (memcmp(signature, "MZ", 2) == 0 || memcmp(signature, "\x7F\x45LF", 4) == 0) ++ warn("SFX archives aren't supported"); ++ return NULL; ++ } ++ ++ return ar_open_archive(stream, sizeof(ar_archive_rar), rar_close, rar_parse_entry, rar_get_name, rar_uncompress, NULL, FILE_SIGNATURE_SIZE); ++} +diff --git a/cut-n-paste/unarr/rar/rar.h b/cut-n-paste/unarr/rar/rar.h +new file mode 100644 +index 00000000..0e112224 +--- /dev/null ++++ b/cut-n-paste/unarr/rar/rar.h +@@ -0,0 +1,252 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++#ifndef rar_rar_h ++#define rar_rar_h ++ ++#include "../common/unarr-imp.h" ++ ++#include "lzss.h" ++#include "../lzmasdk/Ppmd7.h" ++#include ++ ++static inline size_t smin(size_t a, size_t b) { return a < b ? a : b; } ++ ++typedef struct ar_archive_rar_s ar_archive_rar; ++ ++/***** parse-rar *****/ ++ ++#define FILE_SIGNATURE_SIZE 7 ++ ++enum block_types { ++ TYPE_FILE_SIGNATURE = 0x72, TYPE_MAIN_HEADER = 0x73, TYPE_FILE_ENTRY = 0x74, ++ TYPE_NEWSUB = 0x7A, TYPE_END_OF_ARCHIVE = 0x7B, ++}; ++ ++enum archive_flags { ++ MHD_VOLUME = 1 << 0, MHD_COMMENT = 1 << 1, MHD_LOCK = 1 << 2, ++ MHD_SOLID = 1 << 3, MHD_PACK_COMMENT = 1 << 4, MHD_AV = 1 << 5, ++ MHD_PROTECT = 1 << 6, MHD_PASSWORD = 1 << 7, MHD_FIRSTVOLUME = 1 << 8, ++ MHD_ENCRYPTVER = 1 << 9, ++ MHD_LONG_BLOCK = 1 << 15, ++}; ++ ++enum entry_flags { ++ LHD_SPLIT_BEFORE = 1 << 0, LHD_SPLIT_AFTER = 1 << 1, LHD_PASSWORD = 1 << 2, ++ LHD_COMMENT = 1 << 3, LHD_SOLID = 1 << 4, ++ LHD_DIRECTORY = (1 << 5) | (1 << 6) | (1 << 7), ++ LHD_LARGE = 1 << 8, LHD_UNICODE = 1 << 9, LHD_SALT = 1 << 10, ++ LHD_VERSION = 1 << 11, LHD_EXTTIME = 1 << 12, LHD_EXTFLAGS = 1 << 13, ++ LHD_LONG_BLOCK = 1 << 15, ++}; ++ ++enum compression_method { ++ METHOD_STORE = 0x30, ++ METHOD_FASTEST = 0x31, METHOD_FAST = 0x32, METHOD_NORMAL = 0x33, ++ METHOD_GOOD = 0x34, METHOD_BEST = 0x35, ++}; ++ ++struct rar_header { ++ uint16_t crc; ++ uint8_t type; ++ uint16_t flags; ++ uint16_t size; ++ uint64_t datasize; ++}; ++ ++struct rar_entry { ++ uint64_t size; ++ uint8_t os; ++ uint32_t crc; ++ uint32_t dosdate; ++ uint8_t version; ++ uint8_t method; ++ uint16_t namelen; ++ uint32_t attrs; ++}; ++ ++struct ar_archive_rar_entry { ++ uint8_t version; ++ uint8_t method; ++ uint32_t crc; ++ uint16_t header_size; ++ bool solid; ++ char *name; ++}; ++ ++bool rar_parse_header(ar_archive *ar, struct rar_header *header); ++bool rar_check_header_crc(ar_archive *ar); ++bool rar_parse_header_entry(ar_archive_rar *rar, struct rar_header *header, struct rar_entry *entry); ++const char *rar_get_name(ar_archive *ar); ++ ++/***** filter-rar *****/ ++ ++struct RARVirtualMachine; ++struct RARProgramCode; ++struct RARFilter; ++ ++struct ar_archive_rar_filters { ++ struct RARVirtualMachine *vm; ++ struct RARProgramCode *progs; ++ struct RARFilter *stack; ++ size_t filterstart; ++ uint32_t lastfilternum; ++ size_t lastend; ++ uint8_t *bytes; ++ size_t bytes_ready; ++}; ++ ++bool rar_parse_filter(ar_archive_rar *rar, const uint8_t *bytes, uint16_t length, uint8_t flags); ++bool rar_run_filters(ar_archive_rar *rar); ++void rar_clear_filters(struct ar_archive_rar_filters *filters); ++ ++/***** huffman-rar *****/ ++ ++struct huffman_code { ++ struct { ++ int branches[2]; ++ } *tree; ++ int numentries; ++ int capacity; ++ int minlength; ++ int maxlength; ++ struct { ++ int length; ++ int value; ++ } *table; ++ int tablesize; ++}; ++ ++bool rar_new_node(struct huffman_code *code); ++bool rar_add_value(struct huffman_code *code, int value, int codebits, int length); ++bool rar_create_code(struct huffman_code *code, uint8_t *lengths, int numsymbols); ++bool rar_make_table(struct huffman_code *code); ++void rar_free_code(struct huffman_code *code); ++ ++static inline bool rar_is_leaf_node(struct huffman_code *code, int node) { return code->tree[node].branches[0] == code->tree[node].branches[1]; } ++ ++/***** uncompress-rar *****/ ++ ++#define LZSS_WINDOW_SIZE 0x400000 ++#define LZSS_OVERFLOW_SIZE 288 ++ ++#define MAINCODE_SIZE 299 ++#define OFFSETCODE_SIZE 60 ++#define LOWOFFSETCODE_SIZE 17 ++#define LENGTHCODE_SIZE 28 ++#define HUFFMAN_TABLE_SIZE MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE + LENGTHCODE_SIZE ++ ++struct ByteReader { ++ IByteIn super; ++ ar_archive_rar *rar; ++}; ++ ++struct CPpmdRAR_RangeDec { ++ IPpmd7_RangeDec super; ++ UInt32 Range; ++ UInt32 Code; ++ UInt32 Low; ++ IByteIn *Stream; ++}; ++ ++struct ar_archive_rar_uncomp_v3 { ++ struct huffman_code maincode; ++ struct huffman_code offsetcode; ++ struct huffman_code lowoffsetcode; ++ struct huffman_code lengthcode; ++ uint8_t lengthtable[HUFFMAN_TABLE_SIZE]; ++ uint32_t lastlength; ++ uint32_t lastoffset; ++ uint32_t oldoffset[4]; ++ uint32_t lastlowoffset; ++ uint32_t numlowoffsetrepeats; ++ ++ bool is_ppmd_block; ++ int ppmd_escape; ++ CPpmd7 ppmd7_context; ++ struct CPpmdRAR_RangeDec range_dec; ++ struct ByteReader bytein; ++ ++ struct ar_archive_rar_filters filters; ++}; ++ ++#define MAINCODE_SIZE_20 298 ++#define OFFSETCODE_SIZE_20 48 ++#define LENGTHCODE_SIZE_20 28 ++#define HUFFMAN_TABLE_SIZE_20 4 * 257 ++ ++struct AudioState { ++ int8_t weight[5]; ++ int16_t delta[4]; ++ int8_t lastdelta; ++ int error[11]; ++ int count; ++ uint8_t lastbyte; ++}; ++ ++struct ar_archive_rar_uncomp_v2 { ++ struct huffman_code maincode; ++ struct huffman_code offsetcode; ++ struct huffman_code lengthcode; ++ struct huffman_code audiocode[4]; ++ uint8_t lengthtable[HUFFMAN_TABLE_SIZE_20]; ++ uint32_t lastoffset; ++ uint32_t lastlength; ++ uint32_t oldoffset[4]; ++ uint32_t oldoffsetindex; ++ ++ bool audioblock; ++ uint8_t channel; ++ uint8_t numchannels; ++ struct AudioState audiostate[4]; ++ int8_t channeldelta; ++}; ++ ++struct ar_archive_rar_uncomp { ++ uint8_t version; ++ ++ LZSS lzss; ++ size_t bytes_ready; ++ bool start_new_table; ++ ++ union { ++ struct ar_archive_rar_uncomp_v3 v3; ++ struct ar_archive_rar_uncomp_v2 v2; ++ } state; ++ ++ struct StreamBitReader { ++ uint64_t bits; ++ int available; ++ bool at_eof; ++ } br; ++}; ++ ++bool rar_uncompress_part(ar_archive_rar *rar, void *buffer, size_t buffer_size); ++int64_t rar_expand(ar_archive_rar *rar, int64_t end); ++void rar_clear_uncompress(struct ar_archive_rar_uncomp *uncomp); ++static inline void br_clear_leftover_bits(struct ar_archive_rar_uncomp *uncomp) { uncomp->br.available &= ~0x07; } ++ ++/***** rar *****/ ++ ++struct ar_archive_rar_progress { ++ size_t data_left; ++ size_t bytes_done; ++ uint32_t crc; ++}; ++ ++struct ar_archive_rar_solid { ++ size_t size_total; ++ bool part_done; ++ bool restart; ++}; ++ ++struct ar_archive_rar_s { ++ ar_archive super; ++ uint16_t archive_flags; ++ struct ar_archive_rar_entry entry; ++ struct ar_archive_rar_uncomp uncomp; ++ struct ar_archive_rar_progress progress; ++ struct ar_archive_rar_solid solid; ++}; ++ ++#endif +diff --git a/cut-n-paste/unarr/rar/rarvm.c b/cut-n-paste/unarr/rar/rarvm.c +new file mode 100644 +index 00000000..6f738ec3 +--- /dev/null ++++ b/cut-n-paste/unarr/rar/rarvm.c +@@ -0,0 +1,616 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/RARVirtualMachine.c */ ++ ++#include "rarvm.h" ++#include "../common/allocator.h" ++ ++#include ++#include ++ ++typedef struct RAROpcode_s RAROpcode; ++ ++struct RAROpcode_s { ++ uint8_t instruction; ++ uint8_t bytemode; ++ uint8_t addressingmode1; ++ uint8_t addressingmode2; ++ uint32_t value1; ++ uint32_t value2; ++}; ++ ++struct RARProgram_s { ++ RAROpcode *opcodes; ++ uint32_t length; ++ uint32_t capacity; ++}; ++ ++/* Program building */ ++ ++RARProgram *RARCreateProgram() ++{ ++ return calloc(1, sizeof(RARProgram)); ++} ++ ++void RARDeleteProgram(RARProgram *prog) ++{ ++ if (prog) ++ free(prog->opcodes); ++ free(prog); ++} ++ ++bool RARProgramAddInstr(RARProgram *prog, uint8_t instruction, bool bytemode) ++{ ++ if (instruction >= RARNumberOfInstructions) ++ return false; ++ if (bytemode && !RARInstructionHasByteMode(instruction)) ++ return false; ++ if (prog->length + 1 >= prog->capacity) { ++ /* in my small file sample, 16 is the value needed most often */ ++ uint32_t newCapacity = prog->capacity ? prog->capacity * 4 : 32; ++ RAROpcode *newCodes = calloc(newCapacity, sizeof(*prog->opcodes)); ++ if (!newCodes) ++ return false; ++ memcpy(newCodes, prog->opcodes, prog->capacity * sizeof(*prog->opcodes)); ++ free(prog->opcodes); ++ prog->opcodes = newCodes; ++ prog->capacity = newCapacity; ++ } ++ memset(&prog->opcodes[prog->length], 0, sizeof(prog->opcodes[prog->length])); ++ prog->opcodes[prog->length].instruction = instruction; ++ if (instruction == RARMovzxInstruction || instruction == RARMovsxInstruction) ++ prog->opcodes[prog->length].bytemode = 2; /* second argument only */ ++ else if (bytemode) ++ prog->opcodes[prog->length].bytemode = (1 | 2); ++ else ++ prog->opcodes[prog->length].bytemode = 0; ++ prog->length++; ++ return true; ++} ++ ++bool RARSetLastInstrOperands(RARProgram *prog, uint8_t addressingmode1, uint32_t value1, uint8_t addressingmode2, uint32_t value2) ++{ ++ RAROpcode *opcode = &prog->opcodes[prog->length - 1]; ++ int numoperands; ++ ++ if (addressingmode1 >= RARNumberOfAddressingModes || addressingmode2 >= RARNumberOfAddressingModes) ++ return false; ++ if (!prog->length || opcode->addressingmode1 || opcode->value1 || opcode->addressingmode2 || opcode->value2) ++ return false; ++ ++ numoperands = NumberOfRARInstructionOperands(opcode->instruction); ++ if (numoperands == 0) ++ return true; ++ ++ if (addressingmode1 == RARImmediateAddressingMode && RARInstructionWritesFirstOperand(opcode->instruction)) ++ return false; ++ opcode->addressingmode1 = addressingmode1; ++ opcode->value1 = value1; ++ ++ if (numoperands == 2) { ++ if (addressingmode2 == RARImmediateAddressingMode && RARInstructionWritesSecondOperand(opcode->instruction)) ++ return false; ++ opcode->addressingmode2 = addressingmode2; ++ opcode->value2 = value2; ++ } ++ ++ return true; ++} ++ ++bool RARIsProgramTerminated(RARProgram *prog) ++{ ++ return prog->length > 0 && RARInstructionIsUnconditionalJump(prog->opcodes[prog->length - 1].instruction); ++} ++ ++/* Execution */ ++ ++#define EXTMACRO_BEGIN do { ++#ifdef _MSC_VER ++#define EXTMACRO_END } __pragma(warning(push)) __pragma(warning(disable:4127)) while (0) __pragma(warning(pop)) ++#else ++#define EXTMACRO_END } while (0) ++#endif ++ ++#define CarryFlag 1 ++#define ZeroFlag 2 ++#define SignFlag 0x80000000 ++ ++#define SignExtend(a) ((uint32_t)((int8_t)(a))) ++ ++static uint32_t _RARGetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode); ++static void _RARSetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode, uint32_t data); ++ ++#define GetOperand1() _RARGetOperand(vm, opcode->addressingmode1, opcode->value1, opcode->bytemode & 1) ++#define GetOperand2() _RARGetOperand(vm, opcode->addressingmode2, opcode->value2, opcode->bytemode & 2) ++#define SetOperand1(data) _RARSetOperand(vm, opcode->addressingmode1, opcode->value1, opcode->bytemode & 1, data) ++#define SetOperand2(data) _RARSetOperand(vm, opcode->addressingmode2, opcode->value2, opcode->bytemode & 2, data) ++ ++#define SetFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint32_t result = (res); flags = (result == 0 ? ZeroFlag : (result & SignFlag)) | ((carry) ? CarryFlag : 0); EXTMACRO_END ++#define SetByteFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint8_t result = (res); flags = (result == 0 ? ZeroFlag : (SignExtend(result) & SignFlag)) | ((carry) ? CarryFlag : 0); EXTMACRO_END ++#define SetFlags(res) SetFlagsWithCarry(res, 0) ++ ++#define SetOperand1AndFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint32_t r = (res); SetFlagsWithCarry(r, carry); SetOperand1(r); EXTMACRO_END ++#define SetOperand1AndByteFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint8_t r = (res); SetByteFlagsWithCarry(r, carry); SetOperand1(r); EXTMACRO_END ++#define SetOperand1AndFlags(res) EXTMACRO_BEGIN uint32_t r = (res); SetFlags(r); SetOperand1(r); EXTMACRO_END ++ ++#define NextInstruction() { opcode++; continue; } ++#define Jump(offs) { uint32_t o = (offs); if (o >= prog->length) return false; opcode = &prog->opcodes[o]; continue; } ++ ++bool RARExecuteProgram(RARVirtualMachine *vm, RARProgram *prog) ++{ ++ RAROpcode *opcode = prog->opcodes; ++ uint32_t flags = 0; ++ uint32_t op1, op2, carry, i; ++ uint32_t counter = 0; ++ ++ if (!RARIsProgramTerminated(prog)) ++ return false; ++ ++ while ((uint32_t)(opcode - prog->opcodes) < prog->length && counter++ < RARRuntimeMaxInstructions) { ++ switch (opcode->instruction) { ++ case RARMovInstruction: ++ SetOperand1(GetOperand2()); ++ NextInstruction(); ++ ++ case RARCmpInstruction: ++ op1 = GetOperand1(); ++ SetFlagsWithCarry(op1 - GetOperand2(), result > op1); ++ NextInstruction(); ++ ++ case RARAddInstruction: ++ op1 = GetOperand1(); ++ if (opcode->bytemode) ++ SetOperand1AndByteFlagsWithCarry((op1 + GetOperand2()) & 0xFF, result < op1); ++ else ++ SetOperand1AndFlagsWithCarry(op1 + GetOperand2(), result < op1); ++ NextInstruction(); ++ ++ case RARSubInstruction: ++ op1 = GetOperand1(); ++#if 0 /* apparently not correctly implemented in the RAR VM */ ++ if (opcode->bytemode) ++ SetOperand1AndByteFlagsWithCarry((op1 - GetOperand2()) & 0xFF, result > op1); ++ else ++#endif ++ SetOperand1AndFlagsWithCarry(op1 - GetOperand2(), result > op1); ++ NextInstruction(); ++ ++ case RARJzInstruction: ++ if ((flags & ZeroFlag)) ++ Jump(GetOperand1()); ++ NextInstruction(); ++ ++ case RARJnzInstruction: ++ if (!(flags & ZeroFlag)) ++ Jump(GetOperand1()); ++ NextInstruction(); ++ ++ case RARIncInstruction: ++ if (opcode->bytemode) ++ SetOperand1AndFlags((GetOperand1() + 1) & 0xFF); ++ else ++ SetOperand1AndFlags(GetOperand1() + 1); ++ NextInstruction(); ++ ++ case RARDecInstruction: ++ if (opcode->bytemode) ++ SetOperand1AndFlags((GetOperand1() - 1) & 0xFF); ++ else ++ SetOperand1AndFlags(GetOperand1() - 1); ++ NextInstruction(); ++ ++ case RARJmpInstruction: ++ Jump(GetOperand1()); ++ ++ case RARXorInstruction: ++ SetOperand1AndFlags(GetOperand1() ^ GetOperand2()); ++ NextInstruction(); ++ ++ case RARAndInstruction: ++ SetOperand1AndFlags(GetOperand1() & GetOperand2()); ++ NextInstruction(); ++ ++ case RAROrInstruction: ++ SetOperand1AndFlags(GetOperand1() | GetOperand2()); ++ NextInstruction(); ++ ++ case RARTestInstruction: ++ SetFlags(GetOperand1() & GetOperand2()); ++ NextInstruction(); ++ ++ case RARJsInstruction: ++ if ((flags & SignFlag)) ++ Jump(GetOperand1()); ++ NextInstruction(); ++ ++ case RARJnsInstruction: ++ if (!(flags & SignFlag)) ++ Jump(GetOperand1()); ++ NextInstruction(); ++ ++ case RARJbInstruction: ++ if ((flags & CarryFlag)) ++ Jump(GetOperand1()); ++ NextInstruction(); ++ ++ case RARJbeInstruction: ++ if ((flags & (CarryFlag | ZeroFlag))) ++ Jump(GetOperand1()); ++ NextInstruction(); ++ ++ case RARJaInstruction: ++ if (!(flags & (CarryFlag | ZeroFlag))) ++ Jump(GetOperand1()); ++ NextInstruction(); ++ ++ case RARJaeInstruction: ++ if (!(flags & CarryFlag)) ++ Jump(GetOperand1()); ++ NextInstruction(); ++ ++ case RARPushInstruction: ++ vm->registers[7] -= 4; ++ RARVirtualMachineWrite32(vm, vm->registers[7], GetOperand1()); ++ NextInstruction(); ++ ++ case RARPopInstruction: ++ SetOperand1(RARVirtualMachineRead32(vm, vm->registers[7])); ++ vm->registers[7] += 4; ++ NextInstruction(); ++ ++ case RARCallInstruction: ++ vm->registers[7] -= 4; ++ RARVirtualMachineWrite32(vm, vm->registers[7], (uint32_t)(opcode - prog->opcodes + 1)); ++ Jump(GetOperand1()); ++ ++ case RARRetInstruction: ++ if (vm->registers[7] >= RARProgramMemorySize) ++ return true; ++ i = RARVirtualMachineRead32(vm, vm->registers[7]); ++ vm->registers[7] += 4; ++ Jump(i); ++ ++ case RARNotInstruction: ++ SetOperand1(~GetOperand1()); ++ NextInstruction(); ++ ++ case RARShlInstruction: ++ op1 = GetOperand1(); ++ op2 = GetOperand2(); ++ SetOperand1AndFlagsWithCarry(op1 << op2, ((op1 << (op2 - 1)) & 0x80000000) != 0); ++ NextInstruction(); ++ ++ case RARShrInstruction: ++ op1 = GetOperand1(); ++ op2 = GetOperand2(); ++ SetOperand1AndFlagsWithCarry(op1 >> op2, ((op1 >> (op2 - 1)) & 1) != 0); ++ NextInstruction(); ++ ++ case RARSarInstruction: ++ op1 = GetOperand1(); ++ op2 = GetOperand2(); ++ SetOperand1AndFlagsWithCarry(((int32_t)op1) >> op2, ((op1 >> (op2 - 1)) & 1) != 0); ++ NextInstruction(); ++ ++ case RARNegInstruction: ++ SetOperand1AndFlagsWithCarry(-(int32_t)GetOperand1(), result != 0); ++ NextInstruction(); ++ ++ case RARPushaInstruction: ++ vm->registers[7] -= 32; ++ for (i = 0; i < 8; i++) ++ RARVirtualMachineWrite32(vm, vm->registers[7] + (7 - i) * 4, vm->registers[i]); ++ NextInstruction(); ++ ++ case RARPopaInstruction: ++ for (i = 0; i < 8; i++) ++ vm->registers[i] = RARVirtualMachineRead32(vm, vm->registers[7] + (7 - i) * 4); ++ vm->registers[7] += 32; ++ NextInstruction(); ++ ++ case RARPushfInstruction: ++ vm->registers[7] -= 4; ++ RARVirtualMachineWrite32(vm, vm->registers[7], flags); ++ NextInstruction(); ++ ++ case RARPopfInstruction: ++ flags = RARVirtualMachineRead32(vm, vm->registers[7]); ++ vm->registers[7] += 4; ++ NextInstruction(); ++ ++ case RARMovzxInstruction: ++ SetOperand1(GetOperand2()); ++ NextInstruction(); ++ ++ case RARMovsxInstruction: ++ SetOperand1(SignExtend(GetOperand2())); ++ NextInstruction(); ++ ++ case RARXchgInstruction: ++ op1 = GetOperand1(); ++ op2 = GetOperand2(); ++ SetOperand1(op2); ++ SetOperand2(op1); ++ NextInstruction(); ++ ++ case RARMulInstruction: ++ SetOperand1(GetOperand1() * GetOperand2()); ++ NextInstruction(); ++ ++ case RARDivInstruction: ++ op2 = GetOperand2(); ++ if (op2 != 0) ++ SetOperand1(GetOperand1() / op2); ++ NextInstruction(); ++ ++ case RARAdcInstruction: ++ op1 = GetOperand1(); ++ carry = (flags & CarryFlag); ++ if (opcode->bytemode) ++ SetOperand1AndFlagsWithCarry((op1 + GetOperand2() + carry) & 0xFF, result < op1 || (result == op1 && carry)); /* does not correctly set sign bit */ ++ else ++ SetOperand1AndFlagsWithCarry(op1 + GetOperand2() + carry, result < op1 || (result == op1 && carry)); ++ NextInstruction(); ++ ++ case RARSbbInstruction: ++ op1 = GetOperand1(); ++ carry = (flags & CarryFlag); ++ if (opcode->bytemode) ++ SetOperand1AndFlagsWithCarry((op1 - GetOperand2() - carry) & 0xFF, result > op1 || (result == op1 && carry)); /* does not correctly set sign bit */ ++ else ++ SetOperand1AndFlagsWithCarry(op1 - GetOperand2() - carry, result > op1 || (result == op1 && carry)); ++ NextInstruction(); ++ ++ case RARPrintInstruction: ++ /* TODO: ??? */ ++ NextInstruction(); ++ } ++ } ++ ++ return false; ++} ++ ++/* Memory and register access */ ++ ++static uint32_t _RARRead32(const uint8_t *b) ++{ ++ return ((uint32_t)b[3] << 24) | ((uint32_t)b[2] << 16) | ((uint32_t)b[1] << 8) | (uint32_t)b[0]; ++} ++ ++static void _RARWrite32(uint8_t *b, uint32_t n) ++{ ++ b[3] = (n >> 24) & 0xFF; ++ b[2] = (n >> 16) & 0xFF; ++ b[1] = (n >> 8) & 0xFF; ++ b[0] = n & 0xFF; ++} ++ ++void RARSetVirtualMachineRegisters(RARVirtualMachine *vm, uint32_t registers[8]) ++{ ++ if (registers) ++ memcpy(vm->registers, registers, sizeof(vm->registers)); ++ else ++ memset(vm->registers, 0, sizeof(vm->registers)); ++} ++ ++uint32_t RARVirtualMachineRead32(RARVirtualMachine *vm, uint32_t address) ++{ ++ return _RARRead32(&vm->memory[address & RARProgramMemoryMask]); ++} ++ ++void RARVirtualMachineWrite32(RARVirtualMachine *vm, uint32_t address, uint32_t val) ++{ ++ _RARWrite32(&vm->memory[address & RARProgramMemoryMask], val); ++} ++ ++uint8_t RARVirtualMachineRead8(RARVirtualMachine *vm, uint32_t address) ++{ ++ return vm->memory[address & RARProgramMemoryMask]; ++} ++ ++void RARVirtualMachineWrite8(RARVirtualMachine *vm, uint32_t address, uint8_t val) ++{ ++ vm->memory[address & RARProgramMemoryMask] = val; ++} ++ ++static uint32_t _RARGetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode) ++{ ++ if (RARRegisterAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterAddressingMode(7)) { ++ uint32_t result = vm->registers[addressingmode % 8]; ++ if (bytemode) ++ result = result & 0xFF; ++ return result; ++ } ++ if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7)) { ++ if (bytemode) ++ return RARVirtualMachineRead8(vm, vm->registers[addressingmode % 8]); ++ return RARVirtualMachineRead32(vm, vm->registers[addressingmode % 8]); ++ } ++ if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7)) { ++ if (bytemode) ++ return RARVirtualMachineRead8(vm, value + vm->registers[addressingmode % 8]); ++ return RARVirtualMachineRead32(vm, value + vm->registers[addressingmode % 8]); ++ } ++ if (addressingmode == RARAbsoluteAddressingMode) { ++ if (bytemode) ++ return RARVirtualMachineRead8(vm, value); ++ return RARVirtualMachineRead32(vm, value); ++ } ++ /* if (addressingmode == RARImmediateAddressingMode) */ ++ return value; ++} ++ ++static void _RARSetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode, uint32_t data) ++{ ++ if (RARRegisterAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterAddressingMode(7)) { ++ if (bytemode) ++ data = data & 0xFF; ++ vm->registers[addressingmode % 8] = data; ++ } ++ else if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7)) { ++ if (bytemode) ++ RARVirtualMachineWrite8(vm, vm->registers[addressingmode % 8], (uint8_t)data); ++ else ++ RARVirtualMachineWrite32(vm, vm->registers[addressingmode % 8], data); ++ } ++ else if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7)) { ++ if (bytemode) ++ RARVirtualMachineWrite8(vm, value + vm->registers[addressingmode % 8], (uint8_t)data); ++ else ++ RARVirtualMachineWrite32(vm, value + vm->registers[addressingmode % 8], data); ++ } ++ else if (addressingmode == RARAbsoluteAddressingMode) { ++ if (bytemode) ++ RARVirtualMachineWrite8(vm, value, (uint8_t)data); ++ else ++ RARVirtualMachineWrite32(vm, value, data); ++ } ++} ++ ++/* Instruction properties */ ++ ++#define RAR0OperandsFlag 0 ++#define RAR1OperandFlag 1 ++#define RAR2OperandsFlag 2 ++#define RAROperandsFlag 3 ++#define RARHasByteModeFlag 4 ++#define RARIsUnconditionalJumpFlag 8 ++#define RARIsRelativeJumpFlag 16 ++#define RARWritesFirstOperandFlag 32 ++#define RARWritesSecondOperandFlag 64 ++#define RARReadsStatusFlag 128 ++#define RARWritesStatusFlag 256 ++ ++static const int InstructionFlags[RARNumberOfInstructions] = { ++ /*RARMovInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, ++ /*RARCmpInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesStatusFlag, ++ /*RARAddInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, ++ /*RARSubInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, ++ /*RARJzInstruction*/ RAR1OperandFlag | RARIsUnconditionalJumpFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, ++ /*RARJnzInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, ++ /*RARIncInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, ++ /*RARDecInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, ++ /*RARJmpInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag, ++ /*RARXorInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, ++ /*RARAndInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, ++ /*RAROrInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, ++ /*RARTestInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesStatusFlag, ++ /*RARJsInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, ++ /*RARJnsInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, ++ /*RARJbInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, ++ /*RARJbeInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, ++ /*RARJaInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, ++ /*RARJaeInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, ++ /*RARPushInstruction*/ RAR1OperandFlag, ++ /*RARPopInstruction*/ RAR1OperandFlag, ++ /*RARCallInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag, ++ /*RARRetInstruction*/ RAR0OperandsFlag | RARIsUnconditionalJumpFlag, ++ /*RARNotInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, ++ /*RARShlInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, ++ /*RARShrInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, ++ /*RARSarInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, ++ /*RARNegInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, ++ /*RARPushaInstruction*/ RAR0OperandsFlag, ++ /*RARPopaInstruction*/ RAR0OperandsFlag, ++ /*RARPushfInstruction*/ RAR0OperandsFlag | RARReadsStatusFlag, ++ /*RARPopfInstruction*/ RAR0OperandsFlag | RARWritesStatusFlag, ++ /*RARMovzxInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag, ++ /*RARMovsxInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag, ++ /*RARXchgInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag | RARWritesSecondOperandFlag | RARHasByteModeFlag, ++ /*RARMulInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, ++ /*RARDivInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, ++ /*RARAdcInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARReadsStatusFlag | RARWritesStatusFlag, ++ /*RARSbbInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARReadsStatusFlag | RARWritesStatusFlag, ++ /*RARPrintInstruction*/ RAR0OperandsFlag ++}; ++ ++int NumberOfRARInstructionOperands(uint8_t instruction) ++{ ++ if (instruction >= RARNumberOfInstructions) ++ return 0; ++ return InstructionFlags[instruction] & RAROperandsFlag; ++} ++ ++bool RARInstructionHasByteMode(uint8_t instruction) ++{ ++ if (instruction >= RARNumberOfInstructions) ++ return false; ++ return (InstructionFlags[instruction] & RARHasByteModeFlag)!=0; ++} ++ ++bool RARInstructionIsUnconditionalJump(uint8_t instruction) ++{ ++ if (instruction >= RARNumberOfInstructions) ++ return false; ++ return (InstructionFlags[instruction] & RARIsUnconditionalJumpFlag) != 0; ++} ++ ++bool RARInstructionIsRelativeJump(uint8_t instruction) ++{ ++ if (instruction >= RARNumberOfInstructions) ++ return false; ++ return (InstructionFlags[instruction] & RARIsRelativeJumpFlag) != 0; ++} ++ ++bool RARInstructionWritesFirstOperand(uint8_t instruction) ++{ ++ if (instruction >= RARNumberOfInstructions) ++ return false; ++ return (InstructionFlags[instruction] & RARWritesFirstOperandFlag) != 0; ++} ++ ++bool RARInstructionWritesSecondOperand(uint8_t instruction) ++{ ++ if (instruction >= RARNumberOfInstructions) ++ return false; ++ return (InstructionFlags[instruction] & RARWritesSecondOperandFlag) != 0; ++} ++ ++/* Program debugging */ ++ ++#ifndef NDEBUG ++#include ++ ++static void RARPrintOperand(uint8_t addressingmode, uint32_t value) ++{ ++ if (RARRegisterAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterAddressingMode(7)) ++ printf("r%d", addressingmode % 8); ++ else if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7)) ++ printf("@(r%d)", addressingmode % 8); ++ else if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7)) ++ printf("@(r%d+$%02x)", addressingmode % 8, value); ++ else if (addressingmode == RARAbsoluteAddressingMode) ++ printf("@($%02x)", value); ++ else if (addressingmode == RARImmediateAddressingMode) ++ printf("$%02x", value); ++} ++ ++void RARPrintProgram(RARProgram *prog) ++{ ++ static const char *instructionNames[RARNumberOfInstructions] = { ++ "Mov", "Cmp", "Add", "Sub", "Jz", "Jnz", "Inc", "Dec", "Jmp", "Xor", ++ "And", "Or", "Test", "Js", "Jns", "Jb", "Jbe", "Ja", "Jae", "Push", ++ "Pop", "Call", "Ret", "Not", "Shl", "Shr", "Sar", "Neg", "Pusha", "Popa", ++ "Pushf", "Popf", "Movzx", "Movsx", "Xchg", "Mul", "Div", "Adc", "Sbb", "Print", ++ }; ++ ++ uint32_t i; ++ for (i = 0; i < prog->length; i++) { ++ RAROpcode *opcode = &prog->opcodes[i]; ++ int numoperands = NumberOfRARInstructionOperands(opcode->instruction); ++ printf(" %02x: %s", i, instructionNames[opcode->instruction]); ++ if (opcode->bytemode) ++ printf("B"); ++ if (numoperands >= 1) { ++ printf(" "); ++ RARPrintOperand(opcode->addressingmode1, opcode->value1); ++ } ++ if (numoperands == 2) { ++ printf(", "); ++ RARPrintOperand(opcode->addressingmode2, opcode->value2); ++ } ++ printf("\n"); ++ } ++} ++#endif +diff --git a/cut-n-paste/unarr/rar/rarvm.h b/cut-n-paste/unarr/rar/rarvm.h +new file mode 100644 +index 00000000..4fb0b47f +--- /dev/null ++++ b/cut-n-paste/unarr/rar/rarvm.h +@@ -0,0 +1,117 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/RARVirtualMachine.h */ ++ ++#ifndef rar_vm_h ++#define rar_vm_h ++ ++#include ++#include ++ ++#define RARProgramMemorySize 0x40000 ++#define RARProgramMemoryMask (RARProgramMemorySize - 1) ++#define RARProgramWorkSize 0x3c000 ++#define RARProgramGlobalSize 0x2000 ++#define RARProgramSystemGlobalAddress RARProgramWorkSize ++#define RARProgramSystemGlobalSize 64 ++#define RARProgramUserGlobalAddress (RARProgramSystemGlobalAddress + RARProgramSystemGlobalSize) ++#define RARProgramUserGlobalSize (RARProgramGlobalSize - RARProgramSystemGlobalSize) ++#define RARRuntimeMaxInstructions 250000000 ++ ++#define RARRegisterAddressingMode(n) (0 + (n)) ++#define RARRegisterIndirectAddressingMode(n) (8 + (n)) ++#define RARIndexedAbsoluteAddressingMode(n) (16 + (n)) ++#define RARAbsoluteAddressingMode 24 ++#define RARImmediateAddressingMode 25 ++#define RARNumberOfAddressingModes 26 ++ ++typedef struct RARVirtualMachine RARVirtualMachine; ++ ++struct RARVirtualMachine { ++ uint32_t registers[8]; ++ uint8_t memory[RARProgramMemorySize + sizeof(uint32_t) /* overflow sentinel */]; ++}; ++ ++typedef struct RARProgram_s RARProgram; ++ ++/* Program building */ ++ ++enum { ++ RARMovInstruction = 0, ++ RARCmpInstruction = 1, ++ RARAddInstruction = 2, ++ RARSubInstruction = 3, ++ RARJzInstruction = 4, ++ RARJnzInstruction = 5, ++ RARIncInstruction = 6, ++ RARDecInstruction = 7, ++ RARJmpInstruction = 8, ++ RARXorInstruction = 9, ++ RARAndInstruction = 10, ++ RAROrInstruction = 11, ++ RARTestInstruction = 12, ++ RARJsInstruction = 13, ++ RARJnsInstruction = 14, ++ RARJbInstruction = 15, ++ RARJbeInstruction = 16, ++ RARJaInstruction = 17, ++ RARJaeInstruction = 18, ++ RARPushInstruction = 19, ++ RARPopInstruction = 20, ++ RARCallInstruction = 21, ++ RARRetInstruction = 22, ++ RARNotInstruction = 23, ++ RARShlInstruction = 24, ++ RARShrInstruction = 25, ++ RARSarInstruction = 26, ++ RARNegInstruction = 27, ++ RARPushaInstruction = 28, ++ RARPopaInstruction = 29, ++ RARPushfInstruction = 30, ++ RARPopfInstruction = 31, ++ RARMovzxInstruction = 32, ++ RARMovsxInstruction = 33, ++ RARXchgInstruction = 34, ++ RARMulInstruction = 35, ++ RARDivInstruction = 36, ++ RARAdcInstruction = 37, ++ RARSbbInstruction = 38, ++ RARPrintInstruction = 39, ++ RARNumberOfInstructions = 40, ++}; ++ ++RARProgram *RARCreateProgram(); ++void RARDeleteProgram(RARProgram *prog); ++bool RARProgramAddInstr(RARProgram *prog, uint8_t instruction, bool bytemode); ++bool RARSetLastInstrOperands(RARProgram *prog, uint8_t addressingmode1, uint32_t value1, uint8_t addressingmode2, uint32_t value2); ++bool RARIsProgramTerminated(RARProgram *prog); ++ ++/* Execution */ ++ ++bool RARExecuteProgram(RARVirtualMachine *vm, RARProgram *prog); ++ ++/* Memory and register access (convenience) */ ++ ++void RARSetVirtualMachineRegisters(RARVirtualMachine *vm, uint32_t registers[8]); ++uint32_t RARVirtualMachineRead32(RARVirtualMachine *vm, uint32_t address); ++void RARVirtualMachineWrite32(RARVirtualMachine *vm, uint32_t address, uint32_t val); ++uint8_t RARVirtualMachineRead8(RARVirtualMachine *vm, uint32_t address); ++void RARVirtualMachineWrite8(RARVirtualMachine *vm, uint32_t address, uint8_t val); ++ ++/* Instruction properties */ ++ ++int NumberOfRARInstructionOperands(uint8_t instruction); ++bool RARInstructionHasByteMode(uint8_t instruction); ++bool RARInstructionIsUnconditionalJump(uint8_t instruction); ++bool RARInstructionIsRelativeJump(uint8_t instruction); ++bool RARInstructionWritesFirstOperand(uint8_t instruction); ++bool RARInstructionWritesSecondOperand(uint8_t instruction); ++ ++/* Program debugging */ ++ ++#ifndef NDEBUG ++void RARPrintProgram(RARProgram *prog); ++#endif ++ ++#endif +diff --git a/cut-n-paste/unarr/rar/uncompress-rar.c b/cut-n-paste/unarr/rar/uncompress-rar.c +new file mode 100644 +index 00000000..74c2ea61 +--- /dev/null ++++ b/cut-n-paste/unarr/rar/uncompress-rar.c +@@ -0,0 +1,1038 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADRAR30Handle.m */ ++/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADRAR20Handle.m */ ++ ++#include "rar.h" ++ ++static void *gSzAlloc_Alloc(void *self, size_t size) { (void)self; return malloc(size); } ++static void gSzAlloc_Free(void *self, void *ptr) { (void)self; free(ptr); } ++static ISzAlloc gSzAlloc = { gSzAlloc_Alloc, gSzAlloc_Free }; ++ ++static bool br_fill(ar_archive_rar *rar, int bits) ++{ ++ uint8_t bytes[8]; ++ int count, i; ++ /* read as many bits as possible */ ++ count = (64 - rar->uncomp.br.available) / 8; ++ if (rar->progress.data_left < (size_t)count) ++ count = (int)rar->progress.data_left; ++ ++ if (bits > rar->uncomp.br.available + 8 * count || ar_read(rar->super.stream, bytes, count) != (size_t)count) { ++ if (!rar->uncomp.br.at_eof) { ++ warn("Unexpected EOF during decompression (truncated file?)"); ++ rar->uncomp.br.at_eof = true; ++ } ++ return false; ++ } ++ rar->progress.data_left -= count; ++ for (i = 0; i < count; i++) { ++ rar->uncomp.br.bits = (rar->uncomp.br.bits << 8) | bytes[i]; ++ } ++ rar->uncomp.br.available += 8 * count; ++ return true; ++} ++ ++static inline bool br_check(ar_archive_rar *rar, int bits) ++{ ++ return bits <= rar->uncomp.br.available || br_fill(rar, bits); ++} ++ ++static inline uint64_t br_bits(ar_archive_rar *rar, int bits) ++{ ++ return (rar->uncomp.br.bits >> (rar->uncomp.br.available -= bits)) & (((uint64_t)1 << bits) - 1); ++} ++ ++static Byte ByteIn_Read(void *p) ++{ ++ struct ByteReader *self = p; ++ return br_check(self->rar, 8) ? (Byte)br_bits(self->rar, 8) : 0xFF; ++} ++ ++static void ByteIn_CreateVTable(struct ByteReader *br, ar_archive_rar *rar) ++{ ++ br->super.Read = ByteIn_Read; ++ br->rar = rar; ++} ++ ++/* Ppmd7 range decoder differs between 7z and RAR */ ++static void PpmdRAR_RangeDec_Init(struct CPpmdRAR_RangeDec *p) ++{ ++ int i; ++ p->Code = 0; ++ p->Low = 0; ++ p->Range = 0xFFFFFFFF; ++ for (i = 0; i < 4; i++) { ++ p->Code = (p->Code << 8) | p->Stream->Read(p->Stream); ++ } ++} ++ ++static UInt32 Range_GetThreshold(void *p, UInt32 total) ++{ ++ struct CPpmdRAR_RangeDec *self = p; ++ return self->Code / (self->Range /= total); ++} ++ ++static void Range_Decode_RAR(void *p, UInt32 start, UInt32 size) ++{ ++ struct CPpmdRAR_RangeDec *self = p; ++ self->Low += start * self->Range; ++ self->Code -= start * self->Range; ++ self->Range *= size; ++ for (;;) { ++ if ((self->Low ^ (self->Low + self->Range)) >= (1 << 24)) { ++ if (self->Range >= (1 << 15)) ++ break; ++ self->Range = ((uint32_t)(-(int32_t)self->Low)) & ((1 << 15) - 1); ++ } ++ self->Code = (self->Code << 8) | self->Stream->Read(self->Stream); ++ self->Range <<= 8; ++ self->Low <<= 8; ++ } ++} ++ ++static UInt32 Range_DecodeBit_RAR(void *p, UInt32 size0) ++{ ++ UInt32 value = Range_GetThreshold(p, PPMD_BIN_SCALE); ++ UInt32 bit = value < size0 ? 0 : 1; ++ if (!bit) ++ Range_Decode_RAR(p, 0, size0); ++ else ++ Range_Decode_RAR(p, size0, PPMD_BIN_SCALE - size0); ++ return bit; ++} ++ ++static void PpmdRAR_RangeDec_CreateVTable(struct CPpmdRAR_RangeDec *p, IByteIn *stream) ++{ ++ p->super.GetThreshold = Range_GetThreshold; ++ p->super.Decode = Range_Decode_RAR; ++ p->super.DecodeBit = Range_DecodeBit_RAR; ++ p->Stream = stream; ++} ++ ++static bool rar_init_uncompress(struct ar_archive_rar_uncomp *uncomp, uint8_t version) ++{ ++ /* per XADRARParser.m @handleForSolidStreamWithObject these versions are identical */ ++ if (version == 29 || version == 36) ++ version = 3; ++ else if (version == 20 || version == 26) ++ version = 2; ++ else { ++ warn("Unsupported compression version: %d", version); ++ return false; ++ } ++ if (uncomp->version) { ++ if (uncomp->version != version) { ++ warn("Compression version mismatch: %d != %d", version, uncomp->version); ++ return false; ++ } ++ return true; ++ } ++ memset(uncomp, 0, sizeof(*uncomp)); ++ uncomp->start_new_table = true; ++ if (!lzss_initialize(&uncomp->lzss, LZSS_WINDOW_SIZE)) { ++ warn("OOM during decompression"); ++ return false; ++ } ++ if (version == 3) { ++ uncomp->state.v3.ppmd_escape = 2; ++ uncomp->state.v3.filters.filterstart = SIZE_MAX; ++ } ++ uncomp->version = version; ++ return true; ++} ++ ++static void rar_free_codes(struct ar_archive_rar_uncomp *uncomp); ++ ++void rar_clear_uncompress(struct ar_archive_rar_uncomp *uncomp) ++{ ++ if (!uncomp->version) ++ return; ++ rar_free_codes(uncomp); ++ lzss_cleanup(&uncomp->lzss); ++ if (uncomp->version == 3) { ++ Ppmd7_Free(&uncomp->state.v3.ppmd7_context, &gSzAlloc); ++ rar_clear_filters(&uncomp->state.v3.filters); ++ } ++ uncomp->version = 0; ++} ++ ++static int rar_read_next_symbol(ar_archive_rar *rar, struct huffman_code *code) ++{ ++ int node = 0; ++ ++ if (!code->table && !rar_make_table(code)) ++ return -1; ++ ++ /* performance optimization */ ++ if (code->tablesize <= rar->uncomp.br.available) { ++ uint16_t bits = (uint16_t)br_bits(rar, code->tablesize); ++ int length = code->table[bits].length; ++ int value = code->table[bits].value; ++ ++ if (length < 0) { ++ warn("Invalid data in bitstream"); /* invalid prefix code in bitstream */ ++ return -1; ++ } ++ if (length <= code->tablesize) { ++ /* Skip only length bits */ ++ rar->uncomp.br.available += code->tablesize - length; ++ return value; ++ } ++ ++ node = value; ++ } ++ ++ while (!rar_is_leaf_node(code, node)) { ++ uint8_t bit; ++ if (!br_check(rar, 1)) ++ return -1; ++ bit = (uint8_t)br_bits(rar, 1); ++ if (code->tree[node].branches[bit] < 0) { ++ warn("Invalid data in bitstream"); /* invalid prefix code in bitstream */ ++ return -1; ++ } ++ node = code->tree[node].branches[bit]; ++ } ++ ++ return code->tree[node].branches[0]; ++} ++ ++/***** RAR version 2 decompression *****/ ++ ++static void rar_free_codes_v2(struct ar_archive_rar_uncomp_v2 *uncomp_v2) ++{ ++ int i; ++ rar_free_code(&uncomp_v2->maincode); ++ rar_free_code(&uncomp_v2->offsetcode); ++ rar_free_code(&uncomp_v2->lengthcode); ++ for (i = 0; i < 4; i++) ++ rar_free_code(&uncomp_v2->audiocode[i]); ++} ++ ++static bool rar_parse_codes_v2(ar_archive_rar *rar) ++{ ++ struct ar_archive_rar_uncomp_v2 *uncomp_v2 = &rar->uncomp.state.v2; ++ struct huffman_code precode; ++ uint8_t prelengths[19]; ++ uint16_t i, count; ++ int j, val, n; ++ bool ok = false; ++ ++ rar_free_codes_v2(uncomp_v2); ++ ++ if (!br_check(rar, 2)) ++ return false; ++ uncomp_v2->audioblock = br_bits(rar, 1) != 0; ++ if (!br_bits(rar, 1)) ++ memset(uncomp_v2->lengthtable, 0, sizeof(uncomp_v2->lengthtable)); ++ ++ if (uncomp_v2->audioblock) { ++ if (!br_check(rar, 2)) ++ return false; ++ uncomp_v2->numchannels = (uint8_t)br_bits(rar, 2) + 1; ++ count = uncomp_v2->numchannels * 257; ++ if (uncomp_v2->channel > uncomp_v2->numchannels) ++ uncomp_v2->channel = 0; ++ } ++ else ++ count = MAINCODE_SIZE_20 + OFFSETCODE_SIZE_20 + LENGTHCODE_SIZE_20; ++ ++ for (i = 0; i < 19; i++) { ++ if (!br_check(rar, 4)) ++ return false; ++ prelengths[i] = (uint8_t)br_bits(rar, 4); ++ } ++ ++ memset(&precode, 0, sizeof(precode)); ++ if (!rar_create_code(&precode, prelengths, 19)) ++ goto PrecodeError; ++ for (i = 0; i < count; ) { ++ val = rar_read_next_symbol(rar, &precode); ++ if (val < 0) ++ goto PrecodeError; ++ if (val < 16) { ++ uncomp_v2->lengthtable[i] = (uncomp_v2->lengthtable[i] + val) & 0x0F; ++ i++; ++ } ++ else if (val == 16) { ++ if (i == 0) { ++ warn("Invalid data in bitstream"); ++ goto PrecodeError; ++ } ++ if (!br_check(rar, 2)) ++ goto PrecodeError; ++ n = (uint8_t)br_bits(rar, 2) + 3; ++ for (j = 0; j < n && i < count; i++, j++) { ++ uncomp_v2->lengthtable[i] = uncomp_v2->lengthtable[i - 1]; ++ } ++ } ++ else { ++ if (val == 17) { ++ if (!br_check(rar, 3)) ++ goto PrecodeError; ++ n = (uint8_t)br_bits(rar, 3) + 3; ++ } ++ else { ++ if (!br_check(rar, 7)) ++ goto PrecodeError; ++ n = (uint8_t)br_bits(rar, 7) + 11; ++ } ++ for (j = 0; j < n && i < count; i++, j++) { ++ uncomp_v2->lengthtable[i] = 0; ++ } ++ } ++ } ++ ok = true; ++PrecodeError: ++ rar_free_code(&precode); ++ if (!ok) ++ return false; ++ ++ if (uncomp_v2->audioblock) { ++ for (i = 0; i < uncomp_v2->numchannels; i++) { ++ if (!rar_create_code(&uncomp_v2->audiocode[i], uncomp_v2->lengthtable + i * 257, 257)) ++ return false; ++ } ++ } ++ else { ++ if (!rar_create_code(&uncomp_v2->maincode, uncomp_v2->lengthtable, MAINCODE_SIZE_20)) ++ return false; ++ if (!rar_create_code(&uncomp_v2->offsetcode, uncomp_v2->lengthtable + MAINCODE_SIZE_20, OFFSETCODE_SIZE_20)) ++ return false; ++ if (!rar_create_code(&uncomp_v2->lengthcode, uncomp_v2->lengthtable + MAINCODE_SIZE_20 + OFFSETCODE_SIZE_20, LENGTHCODE_SIZE_20)) ++ return false; ++ } ++ ++ rar->uncomp.start_new_table = false; ++ return true; ++} ++ ++static uint8_t rar_decode_audio(struct AudioState *state, int8_t *channeldelta, int8_t delta) ++{ ++ uint8_t predbyte, byte; ++ int prederror; ++ ++ state->delta[3] = state->delta[2]; ++ state->delta[2] = state->delta[1]; ++ state->delta[1] = state->lastdelta - state->delta[0]; ++ state->delta[0] = state->lastdelta; ++ ++ predbyte = ((8 * state->lastbyte + state->weight[0] * state->delta[0] + state->weight[1] * state->delta[1] + state->weight[2] * state->delta[2] + state->weight[3] * state->delta[3] + state->weight[4] * *channeldelta) >> 3) & 0xFF; ++ byte = (predbyte - delta) & 0xFF; ++ ++ prederror = delta << 3; ++ state->error[0] += abs(prederror); ++ state->error[1] += abs(prederror - state->delta[0]); state->error[2] += abs(prederror + state->delta[0]); ++ state->error[3] += abs(prederror - state->delta[1]); state->error[4] += abs(prederror + state->delta[1]); ++ state->error[5] += abs(prederror - state->delta[2]); state->error[6] += abs(prederror + state->delta[2]); ++ state->error[7] += abs(prederror - state->delta[3]); state->error[8] += abs(prederror + state->delta[3]); ++ state->error[9] += abs(prederror - *channeldelta); state->error[10] += abs(prederror + *channeldelta); ++ ++ *channeldelta = state->lastdelta = (int8_t)(byte - state->lastbyte); ++ state->lastbyte = byte; ++ ++ if (!(++state->count & 0x1F)) { ++ uint8_t i, idx = 0; ++ for (i = 1; i < 11; i++) { ++ if (state->error[i] < state->error[idx]) ++ idx = i; ++ } ++ memset(state->error, 0, sizeof(state->error)); ++ ++ switch (idx) { ++ case 1: if (state->weight[0] >= -16) state->weight[0]--; break; ++ case 2: if (state->weight[0] < 16) state->weight[0]++; break; ++ case 3: if (state->weight[1] >= -16) state->weight[1]--; break; ++ case 4: if (state->weight[1] < 16) state->weight[1]++; break; ++ case 5: if (state->weight[2] >= -16) state->weight[2]--; break; ++ case 6: if (state->weight[2] < 16) state->weight[2]++; break; ++ case 7: if (state->weight[3] >= -16) state->weight[3]--; break; ++ case 8: if (state->weight[3] < 16) state->weight[3]++; break; ++ case 9: if (state->weight[4] >= -16) state->weight[4]--; break; ++ case 10: if (state->weight[4] < 16) state->weight[4]++; break; ++ } ++ } ++ ++ return byte; ++} ++ ++int64_t rar_expand_v2(ar_archive_rar *rar, int64_t end) ++{ ++ static const uint8_t lengthbases[] = ++ { 0, 1, 2, 3, 4, 5, 6, ++ 7, 8, 10, 12, 14, 16, 20, ++ 24, 28, 32, 40, 48, 56, 64, ++ 80, 96, 112, 128, 160, 192, 224 }; ++ static const uint8_t lengthbits[] = ++ { 0, 0, 0, 0, 0, 0, 0, ++ 0, 1, 1, 1, 1, 2, 2, ++ 2, 2, 3, 3, 3, 3, 4, ++ 4, 4, 4, 5, 5, 5, 5 }; ++ static const int32_t offsetbases[] = ++ { 0, 1, 2, 3, 4, 6, ++ 8, 12, 16, 24, 32, 48, ++ 64, 96, 128, 192, 256, 384, ++ 512, 768, 1024, 1536, 2048, 3072, ++ 4096, 6144, 8192, 12288, 16384, 24576, ++ 32768, 49152, 65536, 98304, 131072, 196608, ++ 262144, 327680, 393216, 458752, 524288, 589824, ++ 655360, 720896, 786432, 851968, 917504, 983040 }; ++ static const uint8_t offsetbits[] = ++ { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, ++ 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, ++ 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, ++ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 }; ++ static const uint8_t shortbases[] = ++ { 0, 4, 8, 16, 32, 64, 128, 192 }; ++ static const uint8_t shortbits[] = ++ { 2, 2, 3, 4, 5, 6, 6, 6 }; ++ ++ struct ar_archive_rar_uncomp_v2 *uncomp_v2 = &rar->uncomp.state.v2; ++ LZSS *lzss = &rar->uncomp.lzss; ++ int symbol, offs, len; ++ ++ if ((uint64_t)end > rar->super.entry_size_uncompressed + rar->solid.size_total) ++ end = rar->super.entry_size_uncompressed + rar->solid.size_total; ++ ++ for (;;) { ++ if (lzss_position(lzss) >= end) ++ return end; ++ ++ if (uncomp_v2->audioblock) { ++ uint8_t byte; ++ symbol = rar_read_next_symbol(rar, &uncomp_v2->audiocode[uncomp_v2->channel]); ++ if (symbol < 0) ++ return -1; ++ if (symbol == 256) { ++ rar->uncomp.start_new_table = true; ++ return lzss_position(lzss); ++ } ++ byte = rar_decode_audio(&uncomp_v2->audiostate[uncomp_v2->channel], &uncomp_v2->channeldelta, (int8_t)(uint8_t)symbol); ++ uncomp_v2->channel++; ++ if (uncomp_v2->channel == uncomp_v2->numchannels) ++ uncomp_v2->channel = 0; ++ lzss_emit_literal(lzss, byte); ++ continue; ++ } ++ ++ symbol = rar_read_next_symbol(rar, &uncomp_v2->maincode); ++ if (symbol < 0) ++ return -1; ++ if (symbol < 256) { ++ lzss_emit_literal(lzss, (uint8_t)symbol); ++ continue; ++ } ++ if (symbol == 256) { ++ offs = uncomp_v2->lastoffset; ++ len = uncomp_v2->lastlength; ++ } ++ else if (symbol <= 260) { ++ int idx = symbol - 256; ++ int lensymbol = rar_read_next_symbol(rar, &uncomp_v2->lengthcode); ++ offs = uncomp_v2->oldoffset[(uncomp_v2->oldoffsetindex - idx) & 0x03]; ++ if (lensymbol < 0 || lensymbol > (int)(sizeof(lengthbases) / sizeof(lengthbases[0])) || lensymbol > (int)(sizeof(lengthbits) / sizeof(lengthbits[0]))) { ++ warn("Invalid data in bitstream"); ++ return -1; ++ } ++ len = lengthbases[lensymbol] + 2; ++ if (lengthbits[lensymbol] > 0) { ++ if (!br_check(rar, lengthbits[lensymbol])) ++ return -1; ++ len += (uint8_t)br_bits(rar, lengthbits[lensymbol]); ++ } ++ if (offs >= 0x40000) ++ len++; ++ if (offs >= 0x2000) ++ len++; ++ if (offs >= 0x101) ++ len++; ++ } ++ else if (symbol <= 268) { ++ int idx = symbol - 261; ++ offs = shortbases[idx] + 1; ++ if (shortbits[idx] > 0) { ++ if (!br_check(rar, shortbits[idx])) ++ return -1; ++ offs += (uint8_t)br_bits(rar, shortbits[idx]); ++ } ++ len = 2; ++ } ++ else if (symbol == 269) { ++ rar->uncomp.start_new_table = true; ++ return lzss_position(lzss); ++ } ++ else { ++ int idx = symbol - 270; ++ int offssymbol; ++ if (idx > (int)(sizeof(lengthbases) / sizeof(lengthbases[0])) || idx > (int)(sizeof(lengthbits) / sizeof(lengthbits[0]))) { ++ warn("Invalid data in bitstream"); ++ return -1; ++ } ++ len = lengthbases[idx] + 3; ++ if (lengthbits[idx] > 0) { ++ if (!br_check(rar, lengthbits[idx])) ++ return -1; ++ len += (uint8_t)br_bits(rar, lengthbits[idx]); ++ } ++ offssymbol = rar_read_next_symbol(rar, &uncomp_v2->offsetcode); ++ if (offssymbol < 0 || offssymbol > (int)(sizeof(offsetbases) / sizeof(offsetbases[0])) || offssymbol > (int)(sizeof(offsetbits) / sizeof(offsetbits[0]))) { ++ warn("Invalid data in bitstream"); ++ return -1; ++ } ++ offs = offsetbases[offssymbol] + 1; ++ if (offsetbits[offssymbol] > 0) { ++ if (!br_check(rar, offsetbits[offssymbol])) ++ return -1; ++ offs += (int)br_bits(rar, offsetbits[offssymbol]); ++ } ++ if (offs >= 0x40000) ++ len++; ++ if (offs >= 0x2000) ++ len++; ++ } ++ ++ uncomp_v2->lastoffset = uncomp_v2->oldoffset[uncomp_v2->oldoffsetindex++ & 0x03] = offs; ++ uncomp_v2->lastlength = len; ++ ++ lzss_emit_match(lzss, offs, len); ++ } ++} ++ ++/***** RAR version 3 decompression *****/ ++ ++static void rar_free_codes(struct ar_archive_rar_uncomp *uncomp) ++{ ++ struct ar_archive_rar_uncomp_v3 *uncomp_v3 = &uncomp->state.v3; ++ ++ if (uncomp->version == 2) { ++ rar_free_codes_v2(&uncomp->state.v2); ++ return; ++ } ++ ++ rar_free_code(&uncomp_v3->maincode); ++ rar_free_code(&uncomp_v3->offsetcode); ++ rar_free_code(&uncomp_v3->lowoffsetcode); ++ rar_free_code(&uncomp_v3->lengthcode); ++} ++ ++static bool rar_parse_codes(ar_archive_rar *rar) ++{ ++ struct ar_archive_rar_uncomp_v3 *uncomp_v3 = &rar->uncomp.state.v3; ++ ++ if (rar->uncomp.version == 2) ++ return rar_parse_codes_v2(rar); ++ ++ rar_free_codes(&rar->uncomp); ++ ++ br_clear_leftover_bits(&rar->uncomp); ++ ++ if (!br_check(rar, 1)) ++ return false; ++ uncomp_v3->is_ppmd_block = br_bits(rar, 1) != 0; ++ if (uncomp_v3->is_ppmd_block) { ++ uint8_t ppmd_flags; ++ uint32_t max_alloc = 0; ++ ++ if (!br_check(rar, 7)) ++ return false; ++ ppmd_flags = (uint8_t)br_bits(rar, 7); ++ if ((ppmd_flags & 0x20)) { ++ if (!br_check(rar, 8)) ++ return false; ++ max_alloc = ((uint8_t)br_bits(rar, 8) + 1) << 20; ++ } ++ if ((ppmd_flags & 0x40)) { ++ if (!br_check(rar, 8)) ++ return false; ++ uncomp_v3->ppmd_escape = (uint8_t)br_bits(rar, 8); ++ } ++ if ((ppmd_flags & 0x20)) { ++ uint32_t maxorder = (ppmd_flags & 0x1F) + 1; ++ if (maxorder == 1) ++ return false; ++ if (maxorder > 16) ++ maxorder = 16 + (maxorder - 16) * 3; ++ ++ Ppmd7_Free(&uncomp_v3->ppmd7_context, &gSzAlloc); ++ Ppmd7_Construct(&uncomp_v3->ppmd7_context); ++ if (!Ppmd7_Alloc(&uncomp_v3->ppmd7_context, max_alloc, &gSzAlloc)) { ++ warn("OOM during decompression"); ++ return false; ++ } ++ ByteIn_CreateVTable(&uncomp_v3->bytein, rar); ++ PpmdRAR_RangeDec_CreateVTable(&uncomp_v3->range_dec, &uncomp_v3->bytein.super); ++ PpmdRAR_RangeDec_Init(&uncomp_v3->range_dec); ++ Ppmd7_Init(&uncomp_v3->ppmd7_context, maxorder); ++ } ++ else { ++ if (!Ppmd7_WasAllocated(&uncomp_v3->ppmd7_context)) { ++ warn("Invalid data in bitstream"); /* invalid PPMd sequence */ ++ return false; ++ } ++ PpmdRAR_RangeDec_Init(&uncomp_v3->range_dec); ++ } ++ } ++ else { ++ struct huffman_code precode; ++ uint8_t bitlengths[20]; ++ uint8_t zerocount; ++ int i, j, val, n; ++ bool ok = false; ++ ++ if (!br_check(rar, 1)) ++ return false; ++ if (!br_bits(rar, 1)) ++ memset(uncomp_v3->lengthtable, 0, sizeof(uncomp_v3->lengthtable)); ++ memset(&bitlengths, 0, sizeof(bitlengths)); ++ for (i = 0; i < sizeof(bitlengths); i++) { ++ if (!br_check(rar, 4)) ++ return false; ++ bitlengths[i] = (uint8_t)br_bits(rar, 4); ++ if (bitlengths[i] == 0x0F) { ++ if (!br_check(rar, 4)) ++ return false; ++ zerocount = (uint8_t)br_bits(rar, 4); ++ if (zerocount) { ++ for (j = 0; j < zerocount + 2 && i < sizeof(bitlengths); j++) { ++ bitlengths[i++] = 0; ++ } ++ i--; ++ } ++ } ++ } ++ ++ memset(&precode, 0, sizeof(precode)); ++ if (!rar_create_code(&precode, bitlengths, sizeof(bitlengths))) ++ goto PrecodeError; ++ for (i = 0; i < HUFFMAN_TABLE_SIZE; ) { ++ val = rar_read_next_symbol(rar, &precode); ++ if (val < 0) ++ goto PrecodeError; ++ if (val < 16) { ++ uncomp_v3->lengthtable[i] = (uncomp_v3->lengthtable[i] + val) & 0x0F; ++ i++; ++ } ++ else if (val < 18) { ++ if (i == 0) { ++ warn("Invalid data in bitstream"); ++ goto PrecodeError; ++ } ++ if (val == 16) { ++ if (!br_check(rar, 3)) ++ goto PrecodeError; ++ n = (uint8_t)br_bits(rar, 3) + 3; ++ } ++ else { ++ if (!br_check(rar, 7)) ++ goto PrecodeError; ++ n = (uint8_t)br_bits(rar, 7) + 11; ++ } ++ for (j = 0; j < n && i < HUFFMAN_TABLE_SIZE; i++, j++) { ++ uncomp_v3->lengthtable[i] = uncomp_v3->lengthtable[i - 1]; ++ } ++ } ++ else { ++ if (val == 18) { ++ if (!br_check(rar, 3)) ++ goto PrecodeError; ++ n = (uint8_t)br_bits(rar, 3) + 3; ++ } ++ else { ++ if (!br_check(rar, 7)) ++ goto PrecodeError; ++ n = (uint8_t)br_bits(rar, 7) + 11; ++ } ++ for (j = 0; j < n && i < HUFFMAN_TABLE_SIZE; i++, j++) { ++ uncomp_v3->lengthtable[i] = 0; ++ } ++ } ++ } ++ ok = true; ++PrecodeError: ++ rar_free_code(&precode); ++ if (!ok) ++ return false; ++ ++ if (!rar_create_code(&uncomp_v3->maincode, uncomp_v3->lengthtable, MAINCODE_SIZE)) ++ return false; ++ if (!rar_create_code(&uncomp_v3->offsetcode, uncomp_v3->lengthtable + MAINCODE_SIZE, OFFSETCODE_SIZE)) ++ return false; ++ if (!rar_create_code(&uncomp_v3->lowoffsetcode, uncomp_v3->lengthtable + MAINCODE_SIZE + OFFSETCODE_SIZE, LOWOFFSETCODE_SIZE)) ++ return false; ++ if (!rar_create_code(&uncomp_v3->lengthcode, uncomp_v3->lengthtable + MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE, LENGTHCODE_SIZE)) ++ return false; ++ } ++ ++ rar->uncomp.start_new_table = false; ++ return true; ++} ++ ++static bool rar_read_filter(ar_archive_rar *rar, bool (* decode_byte)(ar_archive_rar *rar, uint8_t *byte), int64_t *end) ++{ ++ uint8_t flags, val, *code; ++ uint16_t length, i; ++ ++ if (!decode_byte(rar, &flags)) ++ return false; ++ length = (flags & 0x07) + 1; ++ if (length == 7) { ++ if (!decode_byte(rar, &val)) ++ return false; ++ length = val + 7; ++ } ++ else if (length == 8) { ++ if (!decode_byte(rar, &val)) ++ return false; ++ length = val << 8; ++ if (!decode_byte(rar, &val)) ++ return false; ++ length |= val; ++ } ++ ++ code = malloc(length); ++ if (!code) { ++ warn("OOM during decompression"); ++ return false; ++ } ++ for (i = 0; i < length; i++) { ++ if (!decode_byte(rar, &code[i])) { ++ free(code); ++ return false; ++ } ++ } ++ if (!rar_parse_filter(rar, code, length, flags)) { ++ free(code); ++ return false; ++ } ++ free(code); ++ ++ if (rar->uncomp.state.v3.filters.filterstart < (size_t)*end) ++ *end = rar->uncomp.state.v3.filters.filterstart; ++ ++ return true; ++} ++ ++static inline bool rar_decode_ppmd7_symbol(struct ar_archive_rar_uncomp_v3 *uncomp_v3, Byte *symbol) ++{ ++ int value = Ppmd7_DecodeSymbol(&uncomp_v3->ppmd7_context, &uncomp_v3->range_dec.super); ++ if (value < 0) { ++ warn("Invalid data in bitstream"); /* invalid PPMd symbol */ ++ return false; ++ } ++ *symbol = (Byte)value; ++ return true; ++} ++ ++static bool rar_decode_byte(ar_archive_rar *rar, uint8_t *byte) ++{ ++ if (!br_check(rar, 8)) ++ return false; ++ *byte = (uint8_t)br_bits(rar, 8); ++ return true; ++} ++ ++static bool rar_decode_ppmd7_byte(ar_archive_rar *rar, uint8_t *byte) ++{ ++ return rar_decode_ppmd7_symbol(&rar->uncomp.state.v3, byte); ++} ++ ++static bool rar_handle_ppmd_sequence(ar_archive_rar *rar, int64_t *end) ++{ ++ struct ar_archive_rar_uncomp_v3 *uncomp_v3 = &rar->uncomp.state.v3; ++ LZSS *lzss = &rar->uncomp.lzss; ++ Byte sym, code, length; ++ int lzss_offset; ++ ++ if (!rar_decode_ppmd7_symbol(uncomp_v3, &sym)) ++ return false; ++ if (sym != uncomp_v3->ppmd_escape) { ++ lzss_emit_literal(lzss, sym); ++ return true; ++ } ++ ++ if (!rar_decode_ppmd7_symbol(uncomp_v3, &code)) ++ return false; ++ switch (code) { ++ case 0: ++ return rar_parse_codes(rar); ++ ++ case 2: ++ rar->uncomp.start_new_table = true; ++ return true; ++ ++ case 3: ++ return rar_read_filter(rar, rar_decode_ppmd7_byte, end); ++ ++ case 4: ++ if (!rar_decode_ppmd7_symbol(uncomp_v3, &code)) ++ return false; ++ lzss_offset = code << 16; ++ if (!rar_decode_ppmd7_symbol(uncomp_v3, &code)) ++ return false; ++ lzss_offset |= code << 8; ++ if (!rar_decode_ppmd7_symbol(uncomp_v3, &code)) ++ return false; ++ lzss_offset |= code; ++ if (!rar_decode_ppmd7_symbol(uncomp_v3, &length)) ++ return false; ++ lzss_emit_match(lzss, lzss_offset + 2, length + 32); ++ return true; ++ ++ case 5: ++ if (!rar_decode_ppmd7_symbol(uncomp_v3, &length)) ++ return false; ++ lzss_emit_match(lzss, 1, length + 4); ++ return true; ++ ++ default: ++ lzss_emit_literal(lzss, sym); ++ return true; ++ } ++} ++ ++int64_t rar_expand(ar_archive_rar *rar, int64_t end) ++{ ++ static const uint8_t lengthbases[] = ++ { 0, 1, 2, 3, 4, 5, 6, ++ 7, 8, 10, 12, 14, 16, 20, ++ 24, 28, 32, 40, 48, 56, 64, ++ 80, 96, 112, 128, 160, 192, 224 }; ++ static const uint8_t lengthbits[] = ++ { 0, 0, 0, 0, 0, 0, 0, ++ 0, 1, 1, 1, 1, 2, 2, ++ 2, 2, 3, 3, 3, 3, 4, ++ 4, 4, 4, 5, 5, 5, 5 }; ++ static const int32_t offsetbases[] = ++ { 0, 1, 2, 3, 4, 6, ++ 8, 12, 16, 24, 32, 48, ++ 64, 96, 128, 192, 256, 384, ++ 512, 768, 1024, 1536, 2048, 3072, ++ 4096, 6144, 8192, 12288, 16384, 24576, ++ 32768, 49152, 65536, 98304, 131072, 196608, ++ 262144, 327680, 393216, 458752, 524288, 589824, ++ 655360, 720896, 786432, 851968, 917504, 983040, ++ 1048576, 1310720, 1572864, 1835008, 2097152, 2359296, ++ 2621440, 2883584, 3145728, 3407872, 3670016, 3932160 }; ++ static const uint8_t offsetbits[] = ++ { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, ++ 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, ++ 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, ++ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, ++ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 }; ++ static const uint8_t shortbases[] = ++ { 0, 4, 8, 16, 32, 64, 128, 192 }; ++ static const uint8_t shortbits[] = ++ { 2, 2, 3, 4, 5, 6, 6, 6 }; ++ ++ struct ar_archive_rar_uncomp_v3 *uncomp_v3 = &rar->uncomp.state.v3; ++ LZSS *lzss = &rar->uncomp.lzss; ++ int symbol, offs, len, i; ++ ++ if (rar->uncomp.version == 2) ++ return rar_expand_v2(rar, end); ++ ++ for (;;) { ++ if (lzss_position(lzss) >= end) ++ return end; ++ ++ if (uncomp_v3->is_ppmd_block) { ++ if (!rar_handle_ppmd_sequence(rar, &end)) ++ return -1; ++ if (rar->uncomp.start_new_table) ++ return lzss_position(lzss); ++ continue; ++ } ++ ++ symbol = rar_read_next_symbol(rar, &uncomp_v3->maincode); ++ if (symbol < 0) ++ return -1; ++ if (symbol < 256) { ++ lzss_emit_literal(lzss, (uint8_t)symbol); ++ continue; ++ } ++ if (symbol == 256) { ++ if (!br_check(rar, 1)) ++ return -1; ++ if (!br_bits(rar, 1)) { ++ if (!br_check(rar, 1)) ++ return -1; ++ rar->uncomp.start_new_table = br_bits(rar, 1) != 0; ++ return lzss_position(lzss); ++ } ++ if (!rar_parse_codes(rar)) ++ return -1; ++ continue; ++ } ++ if (symbol == 257) { ++ if (!rar_read_filter(rar, rar_decode_byte, &end)) ++ return -1; ++ continue; ++ } ++ if (symbol == 258) { ++ if (uncomp_v3->lastlength == 0) ++ continue; ++ offs = uncomp_v3->lastoffset; ++ len = uncomp_v3->lastlength; ++ } ++ else if (symbol <= 262) { ++ int idx = symbol - 259; ++ int lensymbol = rar_read_next_symbol(rar, &uncomp_v3->lengthcode); ++ offs = uncomp_v3->oldoffset[idx]; ++ if (lensymbol < 0 || lensymbol > (int)(sizeof(lengthbases) / sizeof(lengthbases[0])) || lensymbol > (int)(sizeof(lengthbits) / sizeof(lengthbits[0]))) { ++ warn("Invalid data in bitstream"); ++ return -1; ++ } ++ len = lengthbases[lensymbol] + 2; ++ if (lengthbits[lensymbol] > 0) { ++ if (!br_check(rar, lengthbits[lensymbol])) ++ return -1; ++ len += (uint8_t)br_bits(rar, lengthbits[lensymbol]); ++ } ++ for (i = idx; i > 0; i--) ++ uncomp_v3->oldoffset[i] = uncomp_v3->oldoffset[i - 1]; ++ uncomp_v3->oldoffset[0] = offs; ++ } ++ else if (symbol <= 270) { ++ int idx = symbol - 263; ++ offs = shortbases[idx] + 1; ++ if (shortbits[idx] > 0) { ++ if (!br_check(rar, shortbits[idx])) ++ return -1; ++ offs += (uint8_t)br_bits(rar, shortbits[idx]); ++ } ++ len = 2; ++ for (i = 3; i > 0; i--) ++ uncomp_v3->oldoffset[i] = uncomp_v3->oldoffset[i - 1]; ++ uncomp_v3->oldoffset[0] = offs; ++ } ++ else { ++ int idx = symbol - 271; ++ int offssymbol; ++ if (idx > (int)(sizeof(lengthbases) / sizeof(lengthbases[0])) || idx > (int)(sizeof(lengthbits) / sizeof(lengthbits[0]))) { ++ warn("Invalid data in bitstream"); ++ return -1; ++ } ++ len = lengthbases[idx] + 3; ++ if (lengthbits[idx] > 0) { ++ if (!br_check(rar, lengthbits[idx])) ++ return -1; ++ len += (uint8_t)br_bits(rar, lengthbits[idx]); ++ } ++ offssymbol = rar_read_next_symbol(rar, &uncomp_v3->offsetcode); ++ if (offssymbol < 0 || offssymbol > (int)(sizeof(offsetbases) / sizeof(offsetbases[0])) || offssymbol > (int)(sizeof(offsetbits) / sizeof(offsetbits[0]))) { ++ warn("Invalid data in bitstream"); ++ return -1; ++ } ++ offs = offsetbases[offssymbol] + 1; ++ if (offsetbits[offssymbol] > 0) { ++ if (offssymbol > 9) { ++ if (offsetbits[offssymbol] > 4) { ++ if (!br_check(rar, offsetbits[offssymbol] - 4)) ++ return -1; ++ offs += (int)br_bits(rar, offsetbits[offssymbol] - 4) << 4; ++ } ++ if (uncomp_v3->numlowoffsetrepeats > 0) { ++ uncomp_v3->numlowoffsetrepeats--; ++ offs += uncomp_v3->lastlowoffset; ++ } ++ else { ++ int lowoffsetsymbol = rar_read_next_symbol(rar, &uncomp_v3->lowoffsetcode); ++ if (lowoffsetsymbol < 0) ++ return -1; ++ if (lowoffsetsymbol == 16) { ++ uncomp_v3->numlowoffsetrepeats = 15; ++ offs += uncomp_v3->lastlowoffset; ++ } ++ else { ++ offs += lowoffsetsymbol; ++ uncomp_v3->lastlowoffset = lowoffsetsymbol; ++ } ++ } ++ } ++ else { ++ if (!br_check(rar, offsetbits[offssymbol])) ++ return -1; ++ offs += (int)br_bits(rar, offsetbits[offssymbol]); ++ } ++ } ++ ++ if (offs >= 0x40000) ++ len++; ++ if (offs >= 0x2000) ++ len++; ++ ++ for (i = 3; i > 0; i--) ++ uncomp_v3->oldoffset[i] = uncomp_v3->oldoffset[i - 1]; ++ uncomp_v3->oldoffset[0] = offs; ++ } ++ ++ uncomp_v3->lastoffset = offs; ++ uncomp_v3->lastlength = len; ++ ++ lzss_emit_match(lzss, offs, len); ++ } ++} ++ ++bool rar_uncompress_part(ar_archive_rar *rar, void *buffer, size_t buffer_size) ++{ ++ struct ar_archive_rar_uncomp *uncomp = &rar->uncomp; ++ struct ar_archive_rar_uncomp_v3 *uncomp_v3 = NULL; ++ size_t end; ++ ++ if (!rar_init_uncompress(uncomp, rar->entry.version)) ++ return false; ++ if (uncomp->version == 3) ++ uncomp_v3 = &uncomp->state.v3; ++ ++ for (;;) { ++ if (uncomp_v3 && uncomp_v3->filters.bytes_ready > 0) { ++ size_t count = smin(uncomp_v3->filters.bytes_ready, buffer_size); ++ memcpy(buffer, uncomp_v3->filters.bytes, count); ++ uncomp_v3->filters.bytes_ready -= count; ++ uncomp_v3->filters.bytes += count; ++ rar->progress.bytes_done += count; ++ buffer_size -= count; ++ buffer = (uint8_t *)buffer + count; ++ if (rar->progress.bytes_done == rar->super.entry_size_uncompressed) ++ goto FinishBlock; ++ } ++ else if (uncomp->bytes_ready > 0) { ++ int count = (int)smin(uncomp->bytes_ready, buffer_size); ++ lzss_copy_bytes_from_window(&uncomp->lzss, buffer, rar->progress.bytes_done + rar->solid.size_total, count); ++ uncomp->bytes_ready -= count; ++ rar->progress.bytes_done += count; ++ buffer_size -= count; ++ buffer = (uint8_t *)buffer + count; ++ } ++ if (buffer_size == 0) ++ return true; ++ ++ if (uncomp->br.at_eof) ++ return false; ++ ++ if (uncomp_v3 && uncomp_v3->filters.lastend == uncomp_v3->filters.filterstart) { ++ if (!rar_run_filters(rar)) ++ return false; ++ continue; ++ } ++ ++FinishBlock: ++ if (uncomp->start_new_table && !rar_parse_codes(rar)) ++ return false; ++ ++ end = rar->progress.bytes_done + rar->solid.size_total + LZSS_WINDOW_SIZE - LZSS_OVERFLOW_SIZE; ++ if (uncomp_v3 && uncomp_v3->filters.filterstart < end) ++ end = uncomp_v3->filters.filterstart; ++ end = (size_t)rar_expand(rar, end); ++ if (end == (size_t)-1 || end < rar->progress.bytes_done + rar->solid.size_total) ++ return false; ++ uncomp->bytes_ready = end - rar->progress.bytes_done - rar->solid.size_total; ++ if (uncomp_v3) ++ uncomp_v3->filters.lastend = end; ++ ++ if (uncomp_v3 && uncomp_v3->is_ppmd_block && uncomp->start_new_table) ++ goto FinishBlock; ++ } ++} +diff --git a/cut-n-paste/unarr/unarr.h b/cut-n-paste/unarr/unarr.h +new file mode 100644 +index 00000000..5ef7447c +--- /dev/null ++++ b/cut-n-paste/unarr/unarr.h +@@ -0,0 +1,94 @@ ++/* Copyright 2015 the unarr project authors (see AUTHORS file). ++ License: LGPLv3 */ ++ ++#ifndef unarr_h ++#define unarr_h ++ ++#include ++#include ++#include ++typedef int64_t off64_t; ++typedef int64_t time64_t; ++ ++#define UNARR_API_VERSION 100 ++ ++/***** common/stream *****/ ++ ++typedef struct ar_stream_s ar_stream; ++ ++/* opens a read-only stream for the given file path; returns NULL on error */ ++ar_stream *ar_open_file(const char *path); ++#ifdef _WIN32 ++ar_stream *ar_open_file_w(const wchar_t *path); ++#endif ++/* opens a read-only stream for the given chunk of memory; the pointer must be valid until ar_close is called */ ++ar_stream *ar_open_memory(const void *data, size_t datalen); ++#ifdef _WIN32 ++typedef struct IStream IStream; ++/* opens a read-only stream based on the given IStream */ ++ar_stream *ar_open_istream(IStream *stream); ++#endif ++ ++/* closes the stream and releases underlying resources */ ++void ar_close(ar_stream *stream); ++/* tries to read 'count' bytes into buffer, advancing the read offset pointer; returns the actual number of bytes read */ ++size_t ar_read(ar_stream *stream, void *buffer, size_t count); ++/* moves the read offset pointer (same as fseek); returns false on failure */ ++bool ar_seek(ar_stream *stream, off64_t offset, int origin); ++/* shortcut for ar_seek(stream, count, SEEK_CUR); returns false on failure */ ++bool ar_skip(ar_stream *stream, off64_t count); ++/* returns the current read offset (or 0 on error) */ ++off64_t ar_tell(ar_stream *stream); ++ ++/***** common/unarr *****/ ++ ++typedef struct ar_archive_s ar_archive; ++ ++/* frees all data stored for the given archive; does not close the underlying stream */ ++void ar_close_archive(ar_archive *ar); ++/* reads the next archive entry; returns false on error or at the end of the file (use ar_at_eof to distinguish the two cases) */ ++bool ar_parse_entry(ar_archive *ar); ++/* reads the archive entry at the given offset as returned by ar_entry_get_offset (offset 0 always restarts at the first entry); should always succeed */ ++bool ar_parse_entry_at(ar_archive *ar, off64_t offset); ++/* reads the (first) archive entry associated with the given name; returns false if the entry couldn't be found */ ++bool ar_parse_entry_for(ar_archive *ar, const char *entry_name); ++/* returns whether the last ar_parse_entry call has reached the file's expected end */ ++bool ar_at_eof(ar_archive *ar); ++ ++/* returns the name of the current entry as UTF-8 string; this pointer is only valid until the next call to ar_parse_entry; returns NULL on failure */ ++const char *ar_entry_get_name(ar_archive *ar); ++/* returns the stream offset of the current entry for use with ar_parse_entry_at */ ++off64_t ar_entry_get_offset(ar_archive *ar); ++/* returns the total size of uncompressed data of the current entry; read exactly that many bytes using ar_entry_uncompress */ ++size_t ar_entry_get_size(ar_archive *ar); ++/* returns the stored modification date of the current entry in 100ns since 1601/01/01 */ ++time64_t ar_entry_get_filetime(ar_archive *ar); ++/* WARNING: don't manually seek in the stream between ar_parse_entry and the last corresponding ar_entry_uncompress call! */ ++/* uncompresses the next 'count' bytes of the current entry into buffer; returns false on error */ ++bool ar_entry_uncompress(ar_archive *ar, void *buffer, size_t count); ++ ++/* copies at most 'count' bytes of the archive's global comment (if any) into buffer; returns the actual amout of bytes copied (or, if 'buffer' is NULL, the required buffer size) */ ++size_t ar_get_global_comment(ar_archive *ar, void *buffer, size_t count); ++ ++/***** rar/rar *****/ ++ ++/* checks whether 'stream' could contain RAR data and prepares for archive listing/extraction; returns NULL on failure */ ++ar_archive *ar_open_rar_archive(ar_stream *stream); ++ ++/***** tar/tar *****/ ++ ++/* checks whether 'stream' could contain TAR data and prepares for archive listing/extraction; returns NULL on failure */ ++ar_archive *ar_open_tar_archive(ar_stream *stream); ++ ++/***** zip/zip *****/ ++ ++/* checks whether 'stream' could contain ZIP data and prepares for archive listing/extraction; returns NULL on failure */ ++/* set deflatedonly for extracting XPS, EPUB, etc. documents where non-Deflate compression methods are not supported by specification */ ++ar_archive *ar_open_zip_archive(ar_stream *stream, bool deflatedonly); ++ ++/***** _7z/_7z *****/ ++ ++/* checks whether 'stream' could contain 7Z data and prepares for archive listing/extraction; returns NULL on failure */ ++ar_archive *ar_open_7z_archive(ar_stream *stream); ++ ++#endif +-- +2.13.0 + + +From 8606199554b482b792ab009c49dad59f6c61547c Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 15 Mar 2017 18:28:42 +0100 +Subject: [PATCH 4/8] comics: Fix "no previous prototype for..." errors + +Functions that aren't used outside the C file should be marked as +static. + +https://bugzilla.gnome.org/show_bug.cgi?id=720742 +--- + cut-n-paste/unarr/lzmasdk/LzmaDec.c | 2 +- + cut-n-paste/unarr/rar/uncompress-rar.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/cut-n-paste/unarr/lzmasdk/LzmaDec.c b/cut-n-paste/unarr/lzmasdk/LzmaDec.c +index bbf650de..038b000a 100644 +--- a/cut-n-paste/unarr/lzmasdk/LzmaDec.c ++++ b/cut-n-paste/unarr/lzmasdk/LzmaDec.c +@@ -715,7 +715,7 @@ static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) + p->needFlush = 0; + } + +-void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) ++static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) + { + p->needFlush = 1; + p->remainLen = 0; +diff --git a/cut-n-paste/unarr/rar/uncompress-rar.c b/cut-n-paste/unarr/rar/uncompress-rar.c +index 74c2ea61..3df490d4 100644 +--- a/cut-n-paste/unarr/rar/uncompress-rar.c ++++ b/cut-n-paste/unarr/rar/uncompress-rar.c +@@ -358,7 +358,7 @@ static uint8_t rar_decode_audio(struct AudioState *state, int8_t *channeldelta, + return byte; + } + +-int64_t rar_expand_v2(ar_archive_rar *rar, int64_t end) ++static int64_t rar_expand_v2(ar_archive_rar *rar, int64_t end) + { + static const uint8_t lengthbases[] = + { 0, 1, 2, 3, 4, 5, 6, +-- +2.13.0 + + +From e8d80996295e2463943918bdadb628f368df0988 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 15 Mar 2017 18:28:34 +0100 +Subject: [PATCH 5/8] =?UTF-8?q?comics:=20Fix=20"function=20declaration=20i?= + =?UTF-8?q?sn=E2=80=99t=20a=20prototype"=20errors?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Need to use "void" as the arguments when declaring. + +FIXME: Should be upstreamed! + +https://bugzilla.gnome.org/show_bug.cgi?id=720742 +--- + cut-n-paste/unarr/lzmasdk/CpuArch.h | 4 ++-- + cut-n-paste/unarr/rar/rarvm.h | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/cut-n-paste/unarr/lzmasdk/CpuArch.h b/cut-n-paste/unarr/lzmasdk/CpuArch.h +index 4fee0093..e3c95257 100644 +--- a/cut-n-paste/unarr/lzmasdk/CpuArch.h ++++ b/cut-n-paste/unarr/lzmasdk/CpuArch.h +@@ -147,8 +147,8 @@ int x86cpuid_GetFirm(const Cx86cpuid *p); + #define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F) + #define x86cpuid_GetStepping(p) ((p)->ver & 0xF) + +-Bool CPU_Is_InOrder(); +-Bool CPU_Is_Aes_Supported(); ++Bool CPU_Is_InOrder(void); ++Bool CPU_Is_Aes_Supported(void); + + #endif + +diff --git a/cut-n-paste/unarr/rar/rarvm.h b/cut-n-paste/unarr/rar/rarvm.h +index 4fb0b47f..51567a9a 100644 +--- a/cut-n-paste/unarr/rar/rarvm.h ++++ b/cut-n-paste/unarr/rar/rarvm.h +@@ -81,7 +81,7 @@ enum { + RARNumberOfInstructions = 40, + }; + +-RARProgram *RARCreateProgram(); ++RARProgram *RARCreateProgram(void); + void RARDeleteProgram(RARProgram *prog); + bool RARProgramAddInstr(RARProgram *prog, uint8_t instruction, bool bytemode); + bool RARSetLastInstrOperands(RARProgram *prog, uint8_t addressingmode1, uint32_t value1, uint8_t addressingmode2, uint32_t value2); +-- +2.13.0 + + +From 16711b99c713b5ff2ff1f3d2e91a4927dfc34314 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 15 Mar 2017 19:00:50 +0100 +Subject: [PATCH 6/8] comics: Add unarr support + +We support all the type of RAR archives now! + +https://bugzilla.gnome.org/show_bug.cgi?id=720742 +--- + backend/comics/Makefile.am | 2 ++ + backend/comics/ev-archive.c | 50 ++++++++++++++++++++++++++++++++++----------- + 2 files changed, 40 insertions(+), 12 deletions(-) + +diff --git a/backend/comics/Makefile.am b/backend/comics/Makefile.am +index 856f469c..1e87d049 100644 +--- a/backend/comics/Makefile.am ++++ b/backend/comics/Makefile.am +@@ -9,6 +9,7 @@ libcomicsdocument_la_SOURCES = \ + libcomicsdocument_la_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libdocument \ ++ -I$(top_srcdir)/cut-n-paste \ + -DGNOMELOCALEDIR=\"$(datadir)/locale\" \ + -DEVINCE_COMPILATION \ + $(AM_CPPFLAGS) +@@ -22,6 +23,7 @@ libcomicsdocument_la_CFLAGS = \ + libcomicsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) + libcomicsdocument_la_LIBADD = \ + $(top_builddir)/libdocument/libevdocument3.la \ ++ $(top_builddir)/cut-n-paste/unarr/libunarr.la \ + $(LIBARCHIVE_LIBS) \ + $(BACKEND_LIBS) \ + $(LIB_LIBS) +diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c +index 333db470..4c423ef2 100644 +--- a/backend/comics/ev-archive.c ++++ b/backend/comics/ev-archive.c +@@ -22,6 +22,7 @@ + + #include + #include ++#include + #include + + #define BUFFER_SIZE (64 * 1024) +@@ -33,6 +34,10 @@ struct _EvArchive { + /* libarchive */ + struct archive *libar; + struct archive_entry *libar_entry; ++ ++ /* unarr */ ++ ar_stream *unarr_s; ++ ar_archive *unarr; + }; + + G_DEFINE_TYPE(EvArchive, ev_archive, G_TYPE_OBJECT); +@@ -44,6 +49,8 @@ ev_archive_finalize (GObject *object) + + switch (archive->type) { + case EV_ARCHIVE_TYPE_RAR: ++ g_clear_pointer (&archive->unarr, ar_close_archive); ++ g_clear_pointer (&archive->unarr_s, ar_close); + break; + case EV_ARCHIVE_TYPE_ZIP: + case EV_ARCHIVE_TYPE_7Z: +@@ -103,9 +110,8 @@ ev_archive_set_archive_type (EvArchive *archive, + + switch (archive_type) { + case EV_ARCHIVE_TYPE_RAR: +- /* Disabled until this is fixed: +- * https://github.com/libarchive/libarchive/issues/373 */ +- return FALSE; ++ archive->type = archive_type; ++ break; + case EV_ARCHIVE_TYPE_ZIP: + case EV_ARCHIVE_TYPE_7Z: + case EV_ARCHIVE_TYPE_TAR: +@@ -133,9 +139,19 @@ ev_archive_open_filename (EvArchive *archive, + case EV_ARCHIVE_TYPE_NONE: + g_assert_not_reached (); + case EV_ARCHIVE_TYPE_RAR: +- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, +- "Archive type 'RAR' not supported"); +- return FALSE; ++ archive->unarr_s = ar_open_file (path); ++ if (archive->unarr_s == NULL) { ++ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Error opening archive"); ++ return FALSE; ++ } ++ archive->unarr = ar_open_rar_archive (archive->unarr_s); ++ if (archive->unarr == NULL) { ++ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Error opening RAR archive"); ++ return FALSE; ++ } ++ return TRUE; + case EV_ARCHIVE_TYPE_ZIP: + case EV_ARCHIVE_TYPE_7Z: + case EV_ARCHIVE_TYPE_TAR: +@@ -190,7 +206,7 @@ ev_archive_read_next_header (EvArchive *archive, + switch (archive->type) { + case EV_ARCHIVE_TYPE_NONE: + case EV_ARCHIVE_TYPE_RAR: +- g_assert_not_reached (); ++ return ar_parse_entry (archive->unarr); + case EV_ARCHIVE_TYPE_ZIP: + case EV_ARCHIVE_TYPE_7Z: + case EV_ARCHIVE_TYPE_TAR: +@@ -205,16 +221,17 @@ ev_archive_get_entry_pathname (EvArchive *archive) + { + g_return_val_if_fail (EV_IS_ARCHIVE (archive), NULL); + g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, NULL); +- g_return_val_if_fail (archive->libar_entry != NULL, NULL); + + switch (archive->type) { + case EV_ARCHIVE_TYPE_NONE: + g_assert_not_reached (); + case EV_ARCHIVE_TYPE_RAR: +- return NULL; ++ g_return_val_if_fail (archive->unarr != NULL, NULL); ++ return ar_entry_get_name (archive->unarr); + case EV_ARCHIVE_TYPE_ZIP: + case EV_ARCHIVE_TYPE_7Z: + case EV_ARCHIVE_TYPE_TAR: ++ g_return_val_if_fail (archive->libar_entry != NULL, NULL); + return archive_entry_pathname (archive->libar_entry); + } + +@@ -226,15 +243,17 @@ ev_archive_get_entry_size (EvArchive *archive) + { + g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1); + g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1); +- g_return_val_if_fail (archive->libar_entry != NULL, -1); + + switch (archive->type) { + case EV_ARCHIVE_TYPE_RAR: ++ g_return_val_if_fail (archive->unarr != NULL, -1); ++ return ar_entry_get_size (archive->unarr); + case EV_ARCHIVE_TYPE_NONE: + g_assert_not_reached (); + case EV_ARCHIVE_TYPE_ZIP: + case EV_ARCHIVE_TYPE_7Z: + case EV_ARCHIVE_TYPE_TAR: ++ g_return_val_if_fail (archive->libar_entry != NULL, -1); + return archive_entry_size (archive->libar_entry); + } + +@@ -251,15 +270,22 @@ ev_archive_read_data (EvArchive *archive, + + g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1); + g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1); +- g_return_val_if_fail (archive->libar_entry != NULL, -1); + + switch (archive->type) { + case EV_ARCHIVE_TYPE_RAR: + case EV_ARCHIVE_TYPE_NONE: +- g_assert_not_reached (); ++ g_return_val_if_fail (archive->unarr != NULL, -1); ++ if (!ar_entry_uncompress (archive->unarr, buf, count)) { ++ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to decompress RAR data"); ++ return -1; ++ } ++ r = count; ++ break; + case EV_ARCHIVE_TYPE_ZIP: + case EV_ARCHIVE_TYPE_7Z: + case EV_ARCHIVE_TYPE_TAR: ++ g_return_val_if_fail (archive->libar_entry != NULL, -1); + r = archive_read_data (archive->libar, buf, count); + if (r < 0) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, +-- +2.13.0 + + +From fb4333feb64ba3facee8907195a56470d6b3ea36 Mon Sep 17 00:00:00 2001 +From: Carlos Garcia Campos +Date: Sat, 25 Mar 2017 12:34:38 +0100 +Subject: [PATCH 7/8] comics: Add ev_archive_reset() + +And use it instead of destroying and re-creating the EvArchive. This way +we don't need to keep the archive type in the document either. +--- + backend/comics/comics-document.c | 17 +++-------------- + backend/comics/ev-archive.c | 32 +++++++++++++++++++++++++++----- + backend/comics/ev-archive.h | 1 + + 3 files changed, 31 insertions(+), 19 deletions(-) + +diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c +index 87c25270..5e07df4d 100644 +--- a/backend/comics/comics-document.c ++++ b/backend/comics/comics-document.c +@@ -49,7 +49,6 @@ struct _ComicsDocument + { + EvDocument parent_instance; + EvArchive *archive; +- EvArchiveType archive_type; + gchar *archive_path; + gchar *archive_uri; + GPtrArray *page_names; +@@ -59,14 +58,6 @@ static GSList* get_supported_image_extensions (void); + + EV_BACKEND_REGISTER (ComicsDocument, comics_document) + +-static void +-comics_document_reset_archive (ComicsDocument *comics_document) +-{ +- g_clear_object (&comics_document->archive); +- comics_document->archive = ev_archive_new (); +- ev_archive_set_archive_type (comics_document->archive, comics_document->archive_type); +-} +- + static char ** + comics_document_list (ComicsDocument *comics_document) + { +@@ -104,7 +95,7 @@ comics_document_list (ComicsDocument *comics_document) + } + + out: +- comics_document_reset_archive (comics_document); ++ ev_archive_reset (comics_document->archive); + return ret; + } + +@@ -202,8 +193,6 @@ comics_document_load (EvDocument *document, + } + g_free (mime_type); + +- comics_document->archive_type = ev_archive_get_archive_type (comics_document->archive); +- + /* Get list of files in archive */ + cb_files = comics_document_list (comics_document); + if (!cb_files) { +@@ -354,7 +343,7 @@ comics_document_get_page_size (EvDocument *document, + } + + out: +- comics_document_reset_archive (comics_document); ++ ev_archive_reset (comics_document->archive); + } + + static void +@@ -439,7 +428,7 @@ comics_document_render_pixbuf (EvDocument *document, + g_object_unref (loader); + + out: +- comics_document_reset_archive (comics_document); ++ ev_archive_reset (comics_document->archive); + return rotated_pixbuf; + } + +diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c +index 4c423ef2..e8a93682 100644 +--- a/backend/comics/ev-archive.c ++++ b/backend/comics/ev-archive.c +@@ -36,7 +36,7 @@ struct _EvArchive { + struct archive_entry *libar_entry; + + /* unarr */ +- ar_stream *unarr_s; ++ ar_stream *unarr_stream; + ar_archive *unarr; + }; + +@@ -50,7 +50,7 @@ ev_archive_finalize (GObject *object) + switch (archive->type) { + case EV_ARCHIVE_TYPE_RAR: + g_clear_pointer (&archive->unarr, ar_close_archive); +- g_clear_pointer (&archive->unarr_s, ar_close); ++ g_clear_pointer (&archive->unarr_stream, ar_close); + break; + case EV_ARCHIVE_TYPE_ZIP: + case EV_ARCHIVE_TYPE_7Z: +@@ -139,13 +139,13 @@ ev_archive_open_filename (EvArchive *archive, + case EV_ARCHIVE_TYPE_NONE: + g_assert_not_reached (); + case EV_ARCHIVE_TYPE_RAR: +- archive->unarr_s = ar_open_file (path); +- if (archive->unarr_s == NULL) { ++ archive->unarr_stream = ar_open_file (path); ++ if (archive->unarr_stream == NULL) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Error opening archive"); + return FALSE; + } +- archive->unarr = ar_open_rar_archive (archive->unarr_s); ++ archive->unarr = ar_open_rar_archive (archive->unarr_stream); + if (archive->unarr == NULL) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Error opening RAR archive"); +@@ -297,6 +297,28 @@ ev_archive_read_data (EvArchive *archive, + return r; + } + ++void ++ev_archive_reset (EvArchive *archive) ++{ ++ g_return_if_fail (EV_IS_ARCHIVE (archive)); ++ g_return_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_RAR: ++ g_clear_pointer (&archive->unarr, ar_close_archive); ++ g_clear_pointer (&archive->unarr_stream, ar_close); ++ break; ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ g_clear_pointer (&archive->libar, archive_free); ++ libarchive_set_archive_type (archive, archive->type); ++ break; ++ default: ++ g_assert_not_reached (); ++ } ++} ++ + static void + ev_archive_init (EvArchive *archive) + { +diff --git a/backend/comics/ev-archive.h b/backend/comics/ev-archive.h +index 3e206939..cb526698 100644 +--- a/backend/comics/ev-archive.h ++++ b/backend/comics/ev-archive.h +@@ -50,6 +50,7 @@ gssize ev_archive_read_data (EvArchive *archive, + void *buf, + gsize count, + GError **error); ++void ev_archive_reset (EvArchive *archive); + + G_END_DECLS + +-- +2.13.0 + + +From db4f4ec44bc32682c9f648f697e83c7e13c5a9eb Mon Sep 17 00:00:00 2001 +From: Carlos Garcia Campos +Date: Sat, 25 Mar 2017 12:38:07 +0100 +Subject: [PATCH 8/8] comic: correctly handle NONE archive type in several + methods + +--- + backend/comics/ev-archive.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c +index e8a93682..7556a49c 100644 +--- a/backend/comics/ev-archive.c ++++ b/backend/comics/ev-archive.c +@@ -205,6 +205,7 @@ ev_archive_read_next_header (EvArchive *archive, + + switch (archive->type) { + case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); + case EV_ARCHIVE_TYPE_RAR: + return ar_parse_entry (archive->unarr); + case EV_ARCHIVE_TYPE_ZIP: +@@ -273,7 +274,6 @@ ev_archive_read_data (EvArchive *archive, + + switch (archive->type) { + case EV_ARCHIVE_TYPE_RAR: +- case EV_ARCHIVE_TYPE_NONE: + g_return_val_if_fail (archive->unarr != NULL, -1); + if (!ar_entry_uncompress (archive->unarr, buf, count)) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, +@@ -282,6 +282,8 @@ ev_archive_read_data (EvArchive *archive, + } + r = count; + break; ++ case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); + case EV_ARCHIVE_TYPE_ZIP: + case EV_ARCHIVE_TYPE_7Z: + case EV_ARCHIVE_TYPE_TAR: +-- +2.13.0 + diff --git a/evince.spec b/evince.spec index 207a73b..3e7272f 100644 --- a/evince.spec +++ b/evince.spec @@ -5,7 +5,7 @@ Name: evince Version: 3.24.0 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Document viewer License: GPLv2+ and GPLv3+ and LGPLv2+ and MIT and Afmparse @@ -19,6 +19,9 @@ Patch2: 0001-Resolves-rhbz-1404656-crash-on-opening-second-evince.patch Patch3: 0001-Resolves-deb-762530-rhbz-1061177-add-man-pages.patch Patch4: 0001-Resolves-rhbz-1358249-page-up-down.patch Patch5: 0001-Revert-Bump-poppler-requirements-to-0.33.0.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1468488 +Patch6: evince-libarchive-gnome-3-24.patch +Patch7: 0001-comics-Fix-decoding-some-files-in-RAR-archives.patch BuildRequires: pkgconfig(adwaita-icon-theme) BuildRequires: pkgconfig(gio-unix-2.0) >= %{glib2_version} @@ -29,6 +32,7 @@ BuildRequires: pkgconfig(libsecret-1) BuildRequires: pkgconfig(libspectre) BuildRequires: pkgconfig(libxml-2.0) BuildRequires: pkgconfig(poppler-glib) >= %{poppler_version} +BuildRequires: pkgconfig(libarchive) BuildRequires: libtiff-devel BuildRequires: gettext BuildRequires: libtool @@ -265,6 +269,11 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas >&/dev/null ||: %{_libdir}/mozilla/plugins/libevbrowserplugin.so %changelog +* Fri Jul 07 2017 Bastien Nocera - 3.24.0-2 ++ evince-3.24.0-2 +- Use libarchive to handle compressed comics documents +Resolves: #1468488 + * Tue Mar 21 2017 Kalev Lember - 3.24.0-1 - Update to 3.24.0