2017-07-13 12:26:12 +00:00
From 275560fc30cf3fc3bf630941dd70b04e771c05f5 Mon Sep 17 00:00:00 2001
2017-07-07 09:30:31 +00:00
From: Bastien Nocera <hadess@hadess.net>
Date: Thu, 29 Jan 2015 16:54:21 +0100
2017-07-13 12:26:12 +00:00
Subject: [PATCH 01/10] comics: Port to using libarchive for unarchiving
2017-07-07 09:30:31 +00:00
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 <juanj.marin@juntadeandalucia.es>
* Copyright (C) 2005, Teemu Tervo <teemu.tervo@gmx.net>
+ * Copyright (C) 2016-2017, Bastien Nocera <hadess@hadess.net>
*
* 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 <glib/gstdio.h>
#include <gio/gio.h>
-#ifdef G_OS_WIN32
-# define WIFEXITED(x) ((x) != 3)
-# define WEXITSTATUS(x) (x)
-#else
-# include <sys/wait.h>
-#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 <hadess@hadess.net>
+ *
+ * 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 <archive.h>
+#include <archive_entry.h>
+#include <gio/gio.h>
+
+#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 <hadess@hadess.net>
+ *
+ * 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 <glib-object.h>
+
+#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
2017-07-13 12:26:12 +00:00
From dda98ee300df709c10c7025ad527a7969796c527 Mon Sep 17 00:00:00 2001
2017-07-07 09:30:31 +00:00
From: Carlos Garcia Campos <carlosgc@gnome.org>
Date: Sat, 25 Mar 2017 11:33:42 +0100
2017-07-13 12:26:12 +00:00
Subject: [PATCH 02/10] comics: cosmetic changes
2017-07-07 09:30:31 +00:00
---
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 <archive.h>
@@ -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 <glib-object.h>
+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
2017-07-13 12:26:12 +00:00
From 1497b8f6bd30a7a560d190399dd150aa15a96301 Mon Sep 17 00:00:00 2001
2017-07-07 09:30:31 +00:00
From: Bastien Nocera <hadess@hadess.net>
Date: Wed, 15 Mar 2017 18:10:17 +0100
2017-07-13 12:26:12 +00:00
Subject: [PATCH 03/10] comics: Add unarr copy/paste
2017-07-07 09:30:31 +00:00
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. <http://fsf.org/>
+ 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 <stddef.h>
+
+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 <crtdbg.h>
+
+#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 <time.h>
+
+/* 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 <zlib.h>
+
+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 <windows.h>
+
+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 <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+
+/***** 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 <windows.h> */
+#endif
+
+#include <stddef.h>
+
+#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 <intrin.h>
+#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 <windows.h>
+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 <stdlib.h>
+
+#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 <string.h>
+
+#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 <memory.h>
+
+#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 <memory.h>
+
+#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 <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#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 <limits.h>
+
+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 <stdlib.h>
+#include <string.h>
+
+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 <stdio.h>
+
+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 <stdint.h>
+#include <stdbool.h>
+
+#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 <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+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
2017-07-13 12:26:12 +00:00
From 9ec025b3d17edebc0bbdd466f789c4740bbb1268 Mon Sep 17 00:00:00 2001
2017-07-07 09:30:31 +00:00
From: Bastien Nocera <hadess@hadess.net>
Date: Wed, 15 Mar 2017 18:28:42 +0100
2017-07-13 12:26:12 +00:00
Subject: [PATCH 04/10] comics: Fix "no previous prototype for..." errors
2017-07-07 09:30:31 +00:00
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
2017-07-13 12:26:12 +00:00
From 5ab715e60cad52ed1a71e3269e6120851bd8d221 Mon Sep 17 00:00:00 2001
2017-07-07 09:30:31 +00:00
From: Bastien Nocera <hadess@hadess.net>
Date: Wed, 15 Mar 2017 18:28:34 +0100
2017-07-13 12:26:12 +00:00
Subject: [PATCH 05/10] =?UTF-8?q?comics:=20Fix=20"function=20declaration?=
=?UTF-8?q?=20isn=E2=80=99t=20a=20prototype"=20errors?=
2017-07-07 09:30:31 +00:00
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
2017-07-13 12:26:12 +00:00
From 283c897b5e4bb935014ab6ebd3d424bdead49c8c Mon Sep 17 00:00:00 2001
2017-07-07 09:30:31 +00:00
From: Bastien Nocera <hadess@hadess.net>
Date: Wed, 15 Mar 2017 19:00:50 +0100
2017-07-13 12:26:12 +00:00
Subject: [PATCH 06/10] comics: Add unarr support
2017-07-07 09:30:31 +00:00
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 <archive.h>
#include <archive_entry.h>
+#include <unarr/unarr.h>
#include <gio/gio.h>
#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
2017-07-13 12:26:12 +00:00
From adcd3dff47d20e657c5b825ed5192a3e8cdda444 Mon Sep 17 00:00:00 2001
2017-07-07 09:30:31 +00:00
From: Carlos Garcia Campos <carlosgc@gnome.org>
Date: Sat, 25 Mar 2017 12:34:38 +0100
2017-07-13 12:26:12 +00:00
Subject: [PATCH 07/10] comics: Add ev_archive_reset()
2017-07-07 09:30:31 +00:00
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
2017-07-13 12:26:12 +00:00
From 5a81953bb79d3398bf143e8499cde3c6b527ba05 Mon Sep 17 00:00:00 2001
2017-07-07 09:30:31 +00:00
From: Carlos Garcia Campos <carlosgc@gnome.org>
Date: Sat, 25 Mar 2017 12:38:07 +0100
2017-07-13 12:26:12 +00:00
Subject: [PATCH 08/10] comic: correctly handle NONE archive type in several
2017-07-07 09:30:31 +00:00
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
2017-07-13 12:26:12 +00:00
From f919d85005d0573c92eb2df231855704c90a8602 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Tue, 11 Jul 2017 13:41:09 +0200
Subject: [PATCH 09/10] comics: Don't throw errors in a loop if image is not
readable
If the file inside the comics archive isn't a readable image file, then
the loader would get closed but we'd keep trying to feed it data,
leading to tons of warnings.
Exit early with the gdk-pixbuf loader error instead.
https://bugzilla.gnome.org/show_bug.cgi?id=784790
---
backend/comics/comics-document.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c
index 5e07df4d..ee060091 100644
--- a/backend/comics/comics-document.c
+++ b/backend/comics/comics-document.c
@@ -321,7 +321,10 @@ comics_document_get_page_size (EvDocument *document,
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);
+ 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);
}
if (read < 0) {
--
2.13.0
From 42dbbf4f8236c83c9c7101b34b97ea3face872d4 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Wed, 12 Jul 2017 12:53:19 +0200
Subject: [PATCH 10/10] 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