evince/evince-libarchive-gnome-3-24.patch
2017-07-13 14:26:46 +02:00

11480 lines
347 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 275560fc30cf3fc3bf630941dd70b04e771c05f5 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Thu, 29 Jan 2015 16:54:21 +0100
Subject: [PATCH 01/10] comics: Port to using libarchive for unarchiving
v8:
- Fix double-free in error path when decompressing images
v7:
- Bump buffer size in ev-archive, good performance increase for
local files
v6:
- Fix 2 pretty big memory leaks
- Remove unneeded archive_read_data_skip() calls
- Optimise the "no rotation" case (could also be done for gnome-3-24)
- Use render_pixbuf_size_prepared_cb()
- Add debug to "next header" archive calls
v5:
- Remove unused members of ComicsDocument struct
- Split archive handling into an EvArchive object
- Fix copy/paste error in configure.ac
v4:
- Fix crash caused by a bug in comics_document_list()
(the array was not NULL terminated)
- Remove duplicate "!cb_files" check
- Use "size-prepared" instead of "area-prepared" to get the doc size
- Fix link to libarchive bug in code, not working yet :/
v3:
- Rebase against latest evince, making sure to bring back:
- Use Unicode in translatable strings
- Sort pages in natural order
- Fix mime-type comparisons
- https://github.com/libarchive/libarchive/issues/373 looks
like it's fixed!
v2:
- Rebase against latest evince
- Update libarchive bug URL
https://bugzilla.gnome.org/show_bug.cgi?id=720742
---
backend/comics/Makefile.am | 6 +-
backend/comics/comics-document.c | 841 ++++++++++-----------------------------
backend/comics/ev-archive.c | 277 +++++++++++++
backend/comics/ev-archive.h | 47 +++
configure.ac | 12 +-
5 files changed, 560 insertions(+), 623 deletions(-)
create mode 100644 backend/comics/ev-archive.c
create mode 100644 backend/comics/ev-archive.h
diff --git a/backend/comics/Makefile.am b/backend/comics/Makefile.am
index b047ad36..856f469c 100644
--- a/backend/comics/Makefile.am
+++ b/backend/comics/Makefile.am
@@ -2,7 +2,9 @@ backend_LTLIBRARIES = libcomicsdocument.la
libcomicsdocument_la_SOURCES = \
comics-document.c \
- comics-document.h
+ comics-document.h \
+ ev-archive.c \
+ ev-archive.h
libcomicsdocument_la_CPPFLAGS = \
-I$(top_srcdir) \
@@ -13,12 +15,14 @@ libcomicsdocument_la_CPPFLAGS = \
libcomicsdocument_la_CFLAGS = \
$(BACKEND_CFLAGS) \
+ $(LIBARCHIVE_CFLAGS) \
$(LIB_CFLAGS) \
$(AM_CFLAGS)
libcomicsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS)
libcomicsdocument_la_LIBADD = \
$(top_builddir)/libdocument/libevdocument3.la \
+ $(LIBARCHIVE_LIBS) \
$(BACKEND_LIBS) \
$(LIB_LIBS)
diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c
index 4c747310..b55c25b3 100644
--- a/backend/comics/comics-document.c
+++ b/backend/comics/comics-document.c
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2009-2010 Juanjo Marín <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,
- _("Cant find an appropriate command to "
- "decompress this type of comic book"));
+ _("libarchive lacks support for this comic books "
+ "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
From dda98ee300df709c10c7025ad527a7969796c527 Mon Sep 17 00:00:00 2001
From: Carlos Garcia Campos <carlosgc@gnome.org>
Date: Sat, 25 Mar 2017 11:33:42 +0100
Subject: [PATCH 02/10] comics: cosmetic changes
---
backend/comics/comics-document.c | 109 ++++++++++++++++++---------------------
backend/comics/ev-archive.c | 30 +++++------
backend/comics/ev-archive.h | 33 +++++++-----
3 files changed, 85 insertions(+), 87 deletions(-)
diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c
index b55c25b3..87c25270 100644
--- a/backend/comics/comics-document.c
+++ b/backend/comics/comics-document.c
@@ -48,31 +48,23 @@ struct _ComicsDocumentClass
struct _ComicsDocument
{
EvDocument parent_instance;
- EvArchive *a;
- EvArchiveType a_type;
+ EvArchive *archive;
+ EvArchiveType archive_type;
gchar *archive_path;
gchar *archive_uri;
GPtrArray *page_names;
};
-static GSList* get_supported_image_extensions (void);
-static void get_page_size_prepared_cb (GdkPixbufLoader *loader,
- int width,
- int height,
- gpointer data);
-static void render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
- gint width,
- gint height,
- EvRenderContext *rc);
+static GSList* get_supported_image_extensions (void);
EV_BACKEND_REGISTER (ComicsDocument, comics_document)
static void
comics_document_reset_archive (ComicsDocument *comics_document)
{
- g_clear_object (&comics_document->a);
- comics_document->a = ev_archive_new ();
- ev_archive_set_archive_type (comics_document->a, comics_document->a_type);
+ g_clear_object (&comics_document->archive);
+ comics_document->archive = ev_archive_new ();
+ ev_archive_set_archive_type (comics_document->archive, comics_document->archive_type);
}
static char **
@@ -81,7 +73,7 @@ comics_document_list (ComicsDocument *comics_document)
char **ret = NULL;
GPtrArray *array;
- if (!ev_archive_open_filename (comics_document->a, comics_document->archive_path, NULL))
+ if (!ev_archive_open_filename (comics_document->archive, comics_document->archive_path, NULL))
goto out;
array = g_ptr_array_new ();
@@ -90,7 +82,7 @@ comics_document_list (ComicsDocument *comics_document)
const char *name;
GError *error = NULL;
- if (!ev_archive_read_next_header (comics_document->a, &error)) {
+ if (!ev_archive_read_next_header (comics_document->archive, &error)) {
if (error != NULL) {
g_warning ("Fatal error handling archive: %s", error->message);
g_error_free (error);
@@ -98,7 +90,7 @@ comics_document_list (ComicsDocument *comics_document)
break;
}
- name = ev_archive_get_entry_pathname (comics_document->a);
+ name = ev_archive_get_entry_pathname (comics_document->archive);
g_debug ("Adding '%s' to the list of files in the comics", name);
g_ptr_array_add (array, g_strdup (name));
@@ -125,19 +117,19 @@ comics_check_decompress_support (gchar *mime_type,
{
if (g_content_type_is_a (mime_type, "application/x-cbr") ||
g_content_type_is_a (mime_type, "application/x-rar")) {
- if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_RAR))
+ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_RAR))
return TRUE;
} else if (g_content_type_is_a (mime_type, "application/x-cbz") ||
g_content_type_is_a (mime_type, "application/zip")) {
- if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_ZIP))
+ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_ZIP))
return TRUE;
} else if (g_content_type_is_a (mime_type, "application/x-cb7") ||
g_content_type_is_a (mime_type, "application/x-7z-compressed")) {
- if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_7Z))
+ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_7Z))
return TRUE;
} else if (g_content_type_is_a (mime_type, "application/x-cbt") ||
g_content_type_is_a (mime_type, "application/x-tar")) {
- if (ev_archive_set_archive_type (comics_document->a, EV_ARCHIVE_TYPE_TAR))
+ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_TAR))
return TRUE;
} else {
g_set_error (error,
@@ -208,10 +200,10 @@ comics_document_load (EvDocument *document,
g_free (mime_type);
return FALSE;
}
- comics_document->a_type = ev_archive_get_archive_type (comics_document->a);
-
g_free (mime_type);
+ comics_document->archive_type = ev_archive_get_archive_type (comics_document->archive);
+
/* Get list of files in archive */
cb_files = comics_document_list (comics_document);
if (!cb_files) {
@@ -282,7 +274,18 @@ typedef struct {
gboolean got_info;
int height;
int width;
-} pixbuf_info;
+} PixbufInfo;
+
+static void
+get_page_size_prepared_cb (GdkPixbufLoader *loader,
+ int width,
+ int height,
+ PixbufInfo *info)
+{
+ info->got_info = TRUE;
+ info->height = height;
+ info->width = width;
+}
static void
comics_document_get_page_size (EvDocument *document,
@@ -293,10 +296,10 @@ comics_document_get_page_size (EvDocument *document,
GdkPixbufLoader *loader;
ComicsDocument *comics_document = COMICS_DOCUMENT (document);
const char *page_path;
- pixbuf_info info;
+ PixbufInfo info;
GError *error = NULL;
- if (!ev_archive_open_filename (comics_document->a, comics_document->archive_path, &error)) {
+ if (!ev_archive_open_filename (comics_document->archive, comics_document->archive_path, &error)) {
g_warning ("Fatal error opening archive: %s", error->message);
g_error_free (error);
goto out;
@@ -314,7 +317,7 @@ comics_document_get_page_size (EvDocument *document,
const char *name;
GError *error = NULL;
- if (!ev_archive_read_next_header (comics_document->a, &error)) {
+ if (!ev_archive_read_next_header (comics_document->archive, &error)) {
if (error != NULL) {
g_warning ("Fatal error handling archive: %s", error->message);
g_error_free (error);
@@ -322,15 +325,15 @@ comics_document_get_page_size (EvDocument *document,
break;
}
- name = ev_archive_get_entry_pathname (comics_document->a);
+ name = ev_archive_get_entry_pathname (comics_document->archive);
if (g_strcmp0 (name, page_path) == 0) {
char buf[BLOCK_SIZE];
gssize read;
- read = ev_archive_read_data (comics_document->a, buf, sizeof(buf), &error);
+ read = ev_archive_read_data (comics_document->archive, buf, sizeof(buf), &error);
while (read > 0 && !info.got_info) {
gdk_pixbuf_loader_write (loader, (guchar *) buf, read, NULL);
- read = ev_archive_read_data (comics_document->a, buf, BLOCK_SIZE, &error);
+ read = ev_archive_read_data (comics_document->archive, buf, BLOCK_SIZE, &error);
}
if (read < 0) {
g_warning ("Fatal error reading '%s' in archive: %s", name, error->message);
@@ -355,15 +358,15 @@ out:
}
static void
-get_page_size_prepared_cb (GdkPixbufLoader *loader,
- int width,
- int height,
- gpointer data)
+render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
+ gint width,
+ gint height,
+ EvRenderContext *rc)
{
- pixbuf_info *info = data;
- info->got_info = TRUE;
- info->height = height;
- info->width = width;
+ int scaled_width, scaled_height;
+
+ ev_render_context_compute_scaled_size (rc, width, height, &scaled_width, &scaled_height);
+ gdk_pixbuf_loader_set_size (loader, scaled_width, scaled_height);
}
static GdkPixbuf *
@@ -372,12 +375,12 @@ comics_document_render_pixbuf (EvDocument *document,
{
GdkPixbufLoader *loader;
GdkPixbuf *tmp_pixbuf;
- GdkPixbuf *rotated_pixbuf;
+ GdkPixbuf *rotated_pixbuf = NULL;
ComicsDocument *comics_document = COMICS_DOCUMENT (document);
const char *page_path;
GError *error = NULL;
- if (!ev_archive_open_filename (comics_document->a, comics_document->archive_path, &error)) {
+ if (!ev_archive_open_filename (comics_document->archive, comics_document->archive_path, &error)) {
g_warning ("Fatal error opening archive: %s", error->message);
g_error_free (error);
goto out;
@@ -393,7 +396,7 @@ comics_document_render_pixbuf (EvDocument *document,
while (1) {
const char *name;
- if (!ev_archive_read_next_header (comics_document->a, &error)) {
+ if (!ev_archive_read_next_header (comics_document->archive, &error)) {
if (error != NULL) {
g_warning ("Fatal error handling archive: %s", error->message);
g_error_free (error);
@@ -401,14 +404,14 @@ comics_document_render_pixbuf (EvDocument *document,
break;
}
- name = ev_archive_get_entry_pathname (comics_document->a);
+ name = ev_archive_get_entry_pathname (comics_document->archive);
if (g_strcmp0 (name, page_path) == 0) {
- size_t size = ev_archive_get_entry_size (comics_document->a);
+ size_t size = ev_archive_get_entry_size (comics_document->archive);
char *buf;
ssize_t read;
buf = g_malloc (size);
- read = ev_archive_read_data (comics_document->a, buf, size, &error);
+ read = ev_archive_read_data (comics_document->archive, buf, size, &error);
if (read <= 0) {
if (read < 0) {
g_warning ("Fatal error reading '%s' in archive: %s", name, error->message);
@@ -426,7 +429,6 @@ comics_document_render_pixbuf (EvDocument *document,
}
tmp_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
- rotated_pixbuf = NULL;
if (tmp_pixbuf) {
if ((rc->rotation % 360) == 0)
rotated_pixbuf = g_object_ref (tmp_pixbuf);
@@ -456,18 +458,6 @@ comics_document_render (EvDocument *document,
}
static void
-render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
- gint width,
- gint height,
- EvRenderContext *rc)
-{
- int scaled_width, scaled_height;
-
- ev_render_context_compute_scaled_size (rc, width, height, &scaled_width, &scaled_height);
- gdk_pixbuf_loader_set_size (loader, scaled_width, scaled_height);
-}
-
-static void
comics_document_finalize (GObject *object)
{
ComicsDocument *comics_document = COMICS_DOCUMENT (object);
@@ -477,7 +467,7 @@ comics_document_finalize (GObject *object)
g_ptr_array_free (comics_document->page_names, TRUE);
}
- g_clear_object (&comics_document->a);
+ g_clear_object (&comics_document->archive);
g_free (comics_document->archive_path);
g_free (comics_document->archive_uri);
@@ -502,8 +492,7 @@ comics_document_class_init (ComicsDocumentClass *klass)
static void
comics_document_init (ComicsDocument *comics_document)
{
- comics_document->a = ev_archive_new ();
- comics_document->page_names = NULL;
+ comics_document->archive = ev_archive_new ();
}
/* Returns a list of file extensions supported by gdk-pixbuf */
diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c
index 9efa69f8..333db470 100644
--- a/backend/comics/ev-archive.c
+++ b/backend/comics/ev-archive.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "config.h"
#include "ev-archive.h"
#include <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
From 1497b8f6bd30a7a560d190399dd150aa15a96301 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Wed, 15 Mar 2017 18:10:17 +0100
Subject: [PATCH 03/10] comics: Add unarr copy/paste
To allow us to decompress CBR comics compressed with recent versions of
RAR, from https://github.com/sumatrapdfreader/sumatrapdf/tree/master/ext/unarr
https://bugzilla.gnome.org/show_bug.cgi?id=720742
---
configure.ac | 1 +
cut-n-paste/Makefile.am | 2 +-
cut-n-paste/unarr/AUTHORS | 12 +
cut-n-paste/unarr/COPYING | 165 +++++
cut-n-paste/unarr/Makefile.am | 36 +
cut-n-paste/unarr/common/allocator.h | 29 +
cut-n-paste/unarr/common/conv.c | 96 +++
cut-n-paste/unarr/common/crc32.c | 51 ++
cut-n-paste/unarr/common/stream.c | 217 ++++++
cut-n-paste/unarr/common/unarr-imp.h | 81 +++
cut-n-paste/unarr/common/unarr.c | 109 ++++
cut-n-paste/unarr/lzmasdk/7zTypes.h | 256 ++++++++
cut-n-paste/unarr/lzmasdk/CpuArch.c | 190 ++++++
cut-n-paste/unarr/lzmasdk/CpuArch.h | 157 +++++
cut-n-paste/unarr/lzmasdk/LzmaDec.c | 1025 +++++++++++++++++++++++++++++
cut-n-paste/unarr/lzmasdk/LzmaDec.h | 227 +++++++
cut-n-paste/unarr/lzmasdk/Ppmd.h | 85 +++
cut-n-paste/unarr/lzmasdk/Ppmd7.c | 710 ++++++++++++++++++++
cut-n-paste/unarr/lzmasdk/Ppmd7.h | 140 ++++
cut-n-paste/unarr/lzmasdk/Ppmd7Dec.c | 189 ++++++
cut-n-paste/unarr/lzmasdk/Ppmd8.c | 1122 ++++++++++++++++++++++++++++++++
cut-n-paste/unarr/lzmasdk/Ppmd8.h | 137 ++++
cut-n-paste/unarr/lzmasdk/Ppmd8Dec.c | 157 +++++
cut-n-paste/unarr/lzmasdk/Precomp.h | 15 +
cut-n-paste/unarr/rar/filter-rar.c | 704 ++++++++++++++++++++
cut-n-paste/unarr/rar/huffman-rar.c | 142 ++++
cut-n-paste/unarr/rar/lzss.h | 88 +++
cut-n-paste/unarr/rar/parse-rar.c | 236 +++++++
cut-n-paste/unarr/rar/rar.c | 223 +++++++
cut-n-paste/unarr/rar/rar.h | 252 +++++++
cut-n-paste/unarr/rar/rarvm.c | 616 ++++++++++++++++++
cut-n-paste/unarr/rar/rarvm.h | 117 ++++
cut-n-paste/unarr/rar/uncompress-rar.c | 1038 +++++++++++++++++++++++++++++
cut-n-paste/unarr/unarr.h | 94 +++
34 files changed, 8718 insertions(+), 1 deletion(-)
create mode 100644 cut-n-paste/unarr/AUTHORS
create mode 100644 cut-n-paste/unarr/COPYING
create mode 100644 cut-n-paste/unarr/Makefile.am
create mode 100644 cut-n-paste/unarr/common/allocator.h
create mode 100644 cut-n-paste/unarr/common/conv.c
create mode 100644 cut-n-paste/unarr/common/crc32.c
create mode 100644 cut-n-paste/unarr/common/stream.c
create mode 100644 cut-n-paste/unarr/common/unarr-imp.h
create mode 100644 cut-n-paste/unarr/common/unarr.c
create mode 100644 cut-n-paste/unarr/lzmasdk/7zTypes.h
create mode 100644 cut-n-paste/unarr/lzmasdk/CpuArch.c
create mode 100644 cut-n-paste/unarr/lzmasdk/CpuArch.h
create mode 100644 cut-n-paste/unarr/lzmasdk/LzmaDec.c
create mode 100644 cut-n-paste/unarr/lzmasdk/LzmaDec.h
create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd.h
create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd7.c
create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd7.h
create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd7Dec.c
create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd8.c
create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd8.h
create mode 100644 cut-n-paste/unarr/lzmasdk/Ppmd8Dec.c
create mode 100644 cut-n-paste/unarr/lzmasdk/Precomp.h
create mode 100644 cut-n-paste/unarr/rar/filter-rar.c
create mode 100644 cut-n-paste/unarr/rar/huffman-rar.c
create mode 100644 cut-n-paste/unarr/rar/lzss.h
create mode 100644 cut-n-paste/unarr/rar/parse-rar.c
create mode 100644 cut-n-paste/unarr/rar/rar.c
create mode 100644 cut-n-paste/unarr/rar/rar.h
create mode 100644 cut-n-paste/unarr/rar/rarvm.c
create mode 100644 cut-n-paste/unarr/rar/rarvm.h
create mode 100644 cut-n-paste/unarr/rar/uncompress-rar.c
create mode 100644 cut-n-paste/unarr/unarr.h
diff --git a/configure.ac b/configure.ac
index a5ae8ccd..e7ee360f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -907,6 +907,7 @@ cut-n-paste/Makefile
cut-n-paste/gimpcellrenderertoggle/Makefile
cut-n-paste/synctex/Makefile
cut-n-paste/libgd/Makefile
+cut-n-paste/unarr/Makefile
data/evince.desktop.in
data/evince-previewer.desktop.in
data/Makefile
diff --git a/cut-n-paste/Makefile.am b/cut-n-paste/Makefile.am
index 2d5684d8..a049b7fd 100644
--- a/cut-n-paste/Makefile.am
+++ b/cut-n-paste/Makefile.am
@@ -1,3 +1,3 @@
-SUBDIRS = gimpcellrenderertoggle synctex libgd
+SUBDIRS = gimpcellrenderertoggle synctex libgd unarr
-include $(top_srcdir)/git.mk
diff --git a/cut-n-paste/unarr/AUTHORS b/cut-n-paste/unarr/AUTHORS
new file mode 100644
index 00000000..4af1be7c
--- /dev/null
+++ b/cut-n-paste/unarr/AUTHORS
@@ -0,0 +1,12 @@
+unarr contains code by:
+
+* The Unarchiver project (https://code.google.com/p/theunarchiver/)
+* Simon Bünzli (zeniko at gmail.com, http://www.zeniko.ch/#SumatraPDF)
+
+Most code is licensed under LGPLv3 (see COPYING). Exceptions are in code
+included from other projects:
+
+Files License URL
+----------------------------------------------------------------------------------
+common/crc32.c Public Domain https://gnunet.org/svn/gnunet/src/util/crypto_crc.c
+lzmasdk/*.* Public Domain http://www.7-zip.org/sdk.html
diff --git a/cut-n-paste/unarr/COPYING b/cut-n-paste/unarr/COPYING
new file mode 100644
index 00000000..65c5ca88
--- /dev/null
+++ b/cut-n-paste/unarr/COPYING
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <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
From 9ec025b3d17edebc0bbdd466f789c4740bbb1268 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Wed, 15 Mar 2017 18:28:42 +0100
Subject: [PATCH 04/10] comics: Fix "no previous prototype for..." errors
Functions that aren't used outside the C file should be marked as
static.
https://bugzilla.gnome.org/show_bug.cgi?id=720742
---
cut-n-paste/unarr/lzmasdk/LzmaDec.c | 2 +-
cut-n-paste/unarr/rar/uncompress-rar.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/cut-n-paste/unarr/lzmasdk/LzmaDec.c b/cut-n-paste/unarr/lzmasdk/LzmaDec.c
index bbf650de..038b000a 100644
--- a/cut-n-paste/unarr/lzmasdk/LzmaDec.c
+++ b/cut-n-paste/unarr/lzmasdk/LzmaDec.c
@@ -715,7 +715,7 @@ static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data)
p->needFlush = 0;
}
-void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
+static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
{
p->needFlush = 1;
p->remainLen = 0;
diff --git a/cut-n-paste/unarr/rar/uncompress-rar.c b/cut-n-paste/unarr/rar/uncompress-rar.c
index 74c2ea61..3df490d4 100644
--- a/cut-n-paste/unarr/rar/uncompress-rar.c
+++ b/cut-n-paste/unarr/rar/uncompress-rar.c
@@ -358,7 +358,7 @@ static uint8_t rar_decode_audio(struct AudioState *state, int8_t *channeldelta,
return byte;
}
-int64_t rar_expand_v2(ar_archive_rar *rar, int64_t end)
+static int64_t rar_expand_v2(ar_archive_rar *rar, int64_t end)
{
static const uint8_t lengthbases[] =
{ 0, 1, 2, 3, 4, 5, 6,
--
2.13.0
From 5ab715e60cad52ed1a71e3269e6120851bd8d221 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Wed, 15 Mar 2017 18:28:34 +0100
Subject: [PATCH 05/10] =?UTF-8?q?comics:=20Fix=20"function=20declaration?=
=?UTF-8?q?=20isn=E2=80=99t=20a=20prototype"=20errors?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Need to use "void" as the arguments when declaring.
FIXME: Should be upstreamed!
https://bugzilla.gnome.org/show_bug.cgi?id=720742
---
cut-n-paste/unarr/lzmasdk/CpuArch.h | 4 ++--
cut-n-paste/unarr/rar/rarvm.h | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/cut-n-paste/unarr/lzmasdk/CpuArch.h b/cut-n-paste/unarr/lzmasdk/CpuArch.h
index 4fee0093..e3c95257 100644
--- a/cut-n-paste/unarr/lzmasdk/CpuArch.h
+++ b/cut-n-paste/unarr/lzmasdk/CpuArch.h
@@ -147,8 +147,8 @@ int x86cpuid_GetFirm(const Cx86cpuid *p);
#define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F)
#define x86cpuid_GetStepping(p) ((p)->ver & 0xF)
-Bool CPU_Is_InOrder();
-Bool CPU_Is_Aes_Supported();
+Bool CPU_Is_InOrder(void);
+Bool CPU_Is_Aes_Supported(void);
#endif
diff --git a/cut-n-paste/unarr/rar/rarvm.h b/cut-n-paste/unarr/rar/rarvm.h
index 4fb0b47f..51567a9a 100644
--- a/cut-n-paste/unarr/rar/rarvm.h
+++ b/cut-n-paste/unarr/rar/rarvm.h
@@ -81,7 +81,7 @@ enum {
RARNumberOfInstructions = 40,
};
-RARProgram *RARCreateProgram();
+RARProgram *RARCreateProgram(void);
void RARDeleteProgram(RARProgram *prog);
bool RARProgramAddInstr(RARProgram *prog, uint8_t instruction, bool bytemode);
bool RARSetLastInstrOperands(RARProgram *prog, uint8_t addressingmode1, uint32_t value1, uint8_t addressingmode2, uint32_t value2);
--
2.13.0
From 283c897b5e4bb935014ab6ebd3d424bdead49c8c Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Wed, 15 Mar 2017 19:00:50 +0100
Subject: [PATCH 06/10] comics: Add unarr support
We support all the type of RAR archives now!
https://bugzilla.gnome.org/show_bug.cgi?id=720742
---
backend/comics/Makefile.am | 2 ++
backend/comics/ev-archive.c | 50 ++++++++++++++++++++++++++++++++++-----------
2 files changed, 40 insertions(+), 12 deletions(-)
diff --git a/backend/comics/Makefile.am b/backend/comics/Makefile.am
index 856f469c..1e87d049 100644
--- a/backend/comics/Makefile.am
+++ b/backend/comics/Makefile.am
@@ -9,6 +9,7 @@ libcomicsdocument_la_SOURCES = \
libcomicsdocument_la_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_srcdir)/libdocument \
+ -I$(top_srcdir)/cut-n-paste \
-DGNOMELOCALEDIR=\"$(datadir)/locale\" \
-DEVINCE_COMPILATION \
$(AM_CPPFLAGS)
@@ -22,6 +23,7 @@ libcomicsdocument_la_CFLAGS = \
libcomicsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS)
libcomicsdocument_la_LIBADD = \
$(top_builddir)/libdocument/libevdocument3.la \
+ $(top_builddir)/cut-n-paste/unarr/libunarr.la \
$(LIBARCHIVE_LIBS) \
$(BACKEND_LIBS) \
$(LIB_LIBS)
diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c
index 333db470..4c423ef2 100644
--- a/backend/comics/ev-archive.c
+++ b/backend/comics/ev-archive.c
@@ -22,6 +22,7 @@
#include <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
From adcd3dff47d20e657c5b825ed5192a3e8cdda444 Mon Sep 17 00:00:00 2001
From: Carlos Garcia Campos <carlosgc@gnome.org>
Date: Sat, 25 Mar 2017 12:34:38 +0100
Subject: [PATCH 07/10] comics: Add ev_archive_reset()
And use it instead of destroying and re-creating the EvArchive. This way
we don't need to keep the archive type in the document either.
---
backend/comics/comics-document.c | 17 +++--------------
backend/comics/ev-archive.c | 32 +++++++++++++++++++++++++++-----
backend/comics/ev-archive.h | 1 +
3 files changed, 31 insertions(+), 19 deletions(-)
diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c
index 87c25270..5e07df4d 100644
--- a/backend/comics/comics-document.c
+++ b/backend/comics/comics-document.c
@@ -49,7 +49,6 @@ struct _ComicsDocument
{
EvDocument parent_instance;
EvArchive *archive;
- EvArchiveType archive_type;
gchar *archive_path;
gchar *archive_uri;
GPtrArray *page_names;
@@ -59,14 +58,6 @@ static GSList* get_supported_image_extensions (void);
EV_BACKEND_REGISTER (ComicsDocument, comics_document)
-static void
-comics_document_reset_archive (ComicsDocument *comics_document)
-{
- g_clear_object (&comics_document->archive);
- comics_document->archive = ev_archive_new ();
- ev_archive_set_archive_type (comics_document->archive, comics_document->archive_type);
-}
-
static char **
comics_document_list (ComicsDocument *comics_document)
{
@@ -104,7 +95,7 @@ comics_document_list (ComicsDocument *comics_document)
}
out:
- comics_document_reset_archive (comics_document);
+ ev_archive_reset (comics_document->archive);
return ret;
}
@@ -202,8 +193,6 @@ comics_document_load (EvDocument *document,
}
g_free (mime_type);
- comics_document->archive_type = ev_archive_get_archive_type (comics_document->archive);
-
/* Get list of files in archive */
cb_files = comics_document_list (comics_document);
if (!cb_files) {
@@ -354,7 +343,7 @@ comics_document_get_page_size (EvDocument *document,
}
out:
- comics_document_reset_archive (comics_document);
+ ev_archive_reset (comics_document->archive);
}
static void
@@ -439,7 +428,7 @@ comics_document_render_pixbuf (EvDocument *document,
g_object_unref (loader);
out:
- comics_document_reset_archive (comics_document);
+ ev_archive_reset (comics_document->archive);
return rotated_pixbuf;
}
diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c
index 4c423ef2..e8a93682 100644
--- a/backend/comics/ev-archive.c
+++ b/backend/comics/ev-archive.c
@@ -36,7 +36,7 @@ struct _EvArchive {
struct archive_entry *libar_entry;
/* unarr */
- ar_stream *unarr_s;
+ ar_stream *unarr_stream;
ar_archive *unarr;
};
@@ -50,7 +50,7 @@ ev_archive_finalize (GObject *object)
switch (archive->type) {
case EV_ARCHIVE_TYPE_RAR:
g_clear_pointer (&archive->unarr, ar_close_archive);
- g_clear_pointer (&archive->unarr_s, ar_close);
+ g_clear_pointer (&archive->unarr_stream, ar_close);
break;
case EV_ARCHIVE_TYPE_ZIP:
case EV_ARCHIVE_TYPE_7Z:
@@ -139,13 +139,13 @@ ev_archive_open_filename (EvArchive *archive,
case EV_ARCHIVE_TYPE_NONE:
g_assert_not_reached ();
case EV_ARCHIVE_TYPE_RAR:
- archive->unarr_s = ar_open_file (path);
- if (archive->unarr_s == NULL) {
+ archive->unarr_stream = ar_open_file (path);
+ if (archive->unarr_stream == NULL) {
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Error opening archive");
return FALSE;
}
- archive->unarr = ar_open_rar_archive (archive->unarr_s);
+ archive->unarr = ar_open_rar_archive (archive->unarr_stream);
if (archive->unarr == NULL) {
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Error opening RAR archive");
@@ -297,6 +297,28 @@ ev_archive_read_data (EvArchive *archive,
return r;
}
+void
+ev_archive_reset (EvArchive *archive)
+{
+ g_return_if_fail (EV_IS_ARCHIVE (archive));
+ g_return_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE);
+
+ switch (archive->type) {
+ case EV_ARCHIVE_TYPE_RAR:
+ g_clear_pointer (&archive->unarr, ar_close_archive);
+ g_clear_pointer (&archive->unarr_stream, ar_close);
+ break;
+ case EV_ARCHIVE_TYPE_ZIP:
+ case EV_ARCHIVE_TYPE_7Z:
+ case EV_ARCHIVE_TYPE_TAR:
+ g_clear_pointer (&archive->libar, archive_free);
+ libarchive_set_archive_type (archive, archive->type);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
static void
ev_archive_init (EvArchive *archive)
{
diff --git a/backend/comics/ev-archive.h b/backend/comics/ev-archive.h
index 3e206939..cb526698 100644
--- a/backend/comics/ev-archive.h
+++ b/backend/comics/ev-archive.h
@@ -50,6 +50,7 @@ gssize ev_archive_read_data (EvArchive *archive,
void *buf,
gsize count,
GError **error);
+void ev_archive_reset (EvArchive *archive);
G_END_DECLS
--
2.13.0
From 5a81953bb79d3398bf143e8499cde3c6b527ba05 Mon Sep 17 00:00:00 2001
From: Carlos Garcia Campos <carlosgc@gnome.org>
Date: Sat, 25 Mar 2017 12:38:07 +0100
Subject: [PATCH 08/10] comic: correctly handle NONE archive type in several
methods
---
backend/comics/ev-archive.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c
index e8a93682..7556a49c 100644
--- a/backend/comics/ev-archive.c
+++ b/backend/comics/ev-archive.c
@@ -205,6 +205,7 @@ ev_archive_read_next_header (EvArchive *archive,
switch (archive->type) {
case EV_ARCHIVE_TYPE_NONE:
+ g_assert_not_reached ();
case EV_ARCHIVE_TYPE_RAR:
return ar_parse_entry (archive->unarr);
case EV_ARCHIVE_TYPE_ZIP:
@@ -273,7 +274,6 @@ ev_archive_read_data (EvArchive *archive,
switch (archive->type) {
case EV_ARCHIVE_TYPE_RAR:
- case EV_ARCHIVE_TYPE_NONE:
g_return_val_if_fail (archive->unarr != NULL, -1);
if (!ar_entry_uncompress (archive->unarr, buf, count)) {
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -282,6 +282,8 @@ ev_archive_read_data (EvArchive *archive,
}
r = count;
break;
+ case EV_ARCHIVE_TYPE_NONE:
+ g_assert_not_reached ();
case EV_ARCHIVE_TYPE_ZIP:
case EV_ARCHIVE_TYPE_7Z:
case EV_ARCHIVE_TYPE_TAR:
--
2.13.0
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