pax_global_header00006660000000000000000000000064131772703350014522gustar00rootroot0000000000000052 comment=b57ee2a07f33865eb0c06c32c2ccffec3eaf4667 unarr-1.0.1/000077500000000000000000000000001317727033500126505ustar00rootroot00000000000000unarr-1.0.1/AUTHORS000066400000000000000000000012171317727033500137210ustar00rootroot00000000000000unarr contains code by: * The Unarchiver project (https://bitbucket.org/kosovan/theunarchiver/) * Simon Bünzli (zeniko at gmail.com, http://www.zeniko.ch/#SumatraPDF) * Felix Kauselmann (licorn at gmail.com) * Bastien Nocera (hadess@hadess.net, http://www.hadess.net/) 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 unarr-1.0.1/CHANGELOG.md000066400000000000000000000014421317727033500144620ustar00rootroot00000000000000# (lib)unarr changelog ## 1.0.1 - 2017-11-04 This is a bugfix release. ### Fixed * Fixed typo in pkg-config.pc.cmake template ## 1.0.0 - 2017-09-22 ### Added * Cmake based build system for library builds * Support for pkg-config (libunarr.pc) * Windows compatible export header for DLL builds * xz utils / libLZMA can be used as decoder for LZMA1 and XZ (LZMA2) compressed ZIP archives. * The internal LZMA1 decoder can be replaced with xz utils / libLZMA if present ### Changed * LZMA SDK code was updated to version 17.01 beta * 7z extraction support is currently broken due to LZMA SDK api changes. * Unarr sample application (unarr-test) and its makefile (legacy unarr build system) have been moved to the [test](test) folder ### Fixed * Various small bugfixes related to compiler warnings unarr-1.0.1/CMakeLists.txt000066400000000000000000000110311317727033500154040ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.3 FATAL_ERROR) project(unarr VERSION 1.0.0 LANGUAGES C) set(PROJECT_DESCRIPTION "A decompression library for rar, tar and zip files.") include(GNUInstallDirs) # Set build type to default if unset. if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() # Build options option(BUILD_SHARED_LIBS "Build ${PROJECT_NAME} as a shared library" ON) # Build target add_library(unarr _7z/_7z.h _7z/_7z.c common/allocator.h common/unarr-imp.h common/conv.c common/crc32.c #common/custalloc.c common/stream.c common/unarr.c lzmasdk/7zTypes.h lzmasdk/CpuArch.h lzmasdk/Ppmd.h lzmasdk/Ppmd7.h lzmasdk/Ppmd8.h lzmasdk/Precomp.h lzmasdk/CpuArch.c lzmasdk/Ppmd7.c lzmasdk/Ppmd8.c lzmasdk/Ppmd7Dec.c lzmasdk/Ppmd8Dec.c rar/lzss.h rar/rar.h rar/rarvm.h rar/filter-rar.c rar/uncompress-rar.c rar/huffman-rar.c rar/rar.c rar/rarvm.c rar/parse-rar.c tar/tar.h tar/parse-tar.c tar/tar.c zip/inflate.h zip/zip.h zip/inflate.c zip/parse-zip.c zip/uncompress-zip.c zip/zip.c) set_target_properties(unarr PROPERTIES PUBLIC_HEADER unarr.h C_VISIBILITY_PRESET hidden C_STANDARD 99 C_STANDARD_REQUIRED ON DEFINE_SYMBOL UNARR_EXPORT_SYMBOLS VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) if(BUILD_SHARED_LIBS) target_compile_definitions(unarr PUBLIC UNARR_IS_SHARED_LIBRARY) endif() find_package(BZip2) if(BZIP2_FOUND) target_include_directories(unarr PRIVATE ${BZIP_INCLUDE_DIRS}) target_link_libraries(unarr ${BZIP2_LIBRARIES}) target_compile_definitions(unarr PRIVATE -DHAVE_BZIP2) # Bzip2 upstream does not supply a .pc file. Add it to Libs.private. set(PROJECT_LIBS_PRIVATE "-I${BZIP_INCLUDE_DIRS} -l${BZIP2_LIBRARIES}") endif() find_package(LibLZMA) if(LIBLZMA_FOUND) target_include_directories(unarr PRIVATE ${LIBLZMA_INCLUDE_DIRS}) target_link_libraries(unarr ${LIBLZMA_LIBRARIES}) target_compile_definitions(unarr PRIVATE -DHAVE_LIBLZMA) set(PROJECT_REQUIRES_PRIVATE "${UNARR_REQUIRES_PRIVATE} liblzma") else() target_sources(unarr PRIVATE lzmasdk/LzmaDec.h lzmasdk/LzmaDec.c) endif() find_package(ZLIB) if(ZLIB_FOUND) target_include_directories(unarr PRIVATE ${ZLIB_INCLUDE_DIRS}) target_link_libraries(unarr ${ZLIB_LIBRARIES}) target_compile_definitions(unarr PRIVATE -DHAVE_ZLIB) # Add zlib to libunarr.pc Requires.private set(PROJECT_REQUIRES_PRIVATE "${PROJECT_REQUIRES_PRIVATE} zlib") endif() # Compiler specific settings if(UNIX OR MINGW OR MSYS) target_compile_options(unarr PRIVATE -Wall -Wextra -pedantic -Wstrict-prototypes -Wmissing-prototypes -Werror-implicit-function-declaration $<$:-fomit-frame-pointer> $<$,$>: -Wno-missing-field-initializers> -flto) target_compile_definitions(unarr PRIVATE -D_FILE_OFFSET_BITS=64) # Linker flags # Clang linker needs -flto too when doing lto if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") set_target_properties(unarr PROPERTIES LINK_FLAGS "-Wl,--no-undefined -Wl,--as-needed -flto") # Apple ld uses different syntax for undefined symbol check elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") set_target_properties(unarr PROPERTIES LINK_FLAGS "-Wl,-undefined,error -flto") else() set_target_properties(unarr PROPERTIES LINK_FLAGS "-Wl,--no-undefined -Wl,--as-needed") endif() endif() if(MSVC) target_compile_options(unarr PRIVATE /W3 $<$:/Ox>) target_compile_definitions(unarr PRIVATE _CRT_SECURE_NO_WARNINGS) endif() # Write pkg-config file configure_file("pkg-config.pc.cmake" "lib${PROJECT_NAME}.pc" @ONLY) # Install library and header install(TARGETS unarr RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) # Install pkg-config file install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${PROJECT_NAME}.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) unarr-1.0.1/COPYING000066400000000000000000000172101317727033500137040ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. unarr-1.0.1/README.md000066400000000000000000000054161317727033500141350ustar00rootroot00000000000000# (lib)unarr **(lib)unarr** is a decompression library for RAR, TAR, ZIP and 7z* archives. It was forked from **unarr**, which originated as a port of the RAR extraction features from The Unarchiver project required for extracting images from comic book archives. [Zeniko](https://github.com/zeniko/) wrote unarr as an alternative to libarchive which didn't have support for parsing filters or solid compression at the time. While (lib)unarr was started with the intent of providing unarr with a proper cmake based build system suitable for packaging and cross-platform development, it's focus has now been extended to provide code maintenance and to continue the development of unarr, which no longer is maintained. ## Getting started ### Building from source #### Dependencies (lib)unarr can take advantage of the following libraries if they are present: * bzip2 * xz / libLZMA * zlib More information on what library is used for which purpose can be found in the description for embedded builds. #### Cmake >mkdir build >cd build >cmake .. >make ... as a static library >cmake .. -DBUILD_SHARED_LIBS=OFF Install >make install #### Embedded build Make sure your compiler is C99 compatible, grab the source code, copy it into your project and adjust your build system accordingly. You can define the following symbols to take advantage of third party libraries: | Symbol | Required header | Required for (format/method)| |-------------------|:---------------:|:----------------------------| |HAVE_ZLIB | zlib.h | faster CRC-32 and Deflate | |HAVE_BZIP2 | bzlib.h | ZIP / Bzip2 | |HAVE_LIBLZMA | lzma.h | ZIP / LZMA, XZ(LZMA2) | |HAVE_7Z | 7z.h | 7Z* / LZMA, LZMA2, BCJ | |_7ZIP_PPMD_SUPPORT | | 7Z* / PPMd | Make sure the required headers are present in the include path. ## Usage ### Examples Check [unarr.h](unarr.h) and [unarr-test](test/main.c) to get a general feel for the api and usage. ## Limitations Unarr was written for comic book archives, so it currently doesn't support: * password protected archives * self extracting archives * split archives ### 7z support 7z support is currently broken. This is due to two problems: 1. The version of the LZMA SDK used in the 7z extraction code is outdated and no longer compatible with the LZMA code used in the other formats 2. The ANSI-C based 7z extraction code provided by the LZMA SDK has a known performance issue that limits it's usefullness for large files with solid compression (see https://github.com/zeniko/unarr/issues/4). Fixing these problems requires a partial rewrite of the code involved. unarr-1.0.1/_7z/000077500000000000000000000000001317727033500133475ustar00rootroot00000000000000unarr-1.0.1/_7z/_7z.c000066400000000000000000000135251317727033500142200ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #include "_7z.h" #ifdef HAVE_7Z 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 SRes CSeekStream_Read(void *p, void *data, size_t *size) { struct CSeekStream *stm = p; *size = ar_read(stm->stream, data, *size); return SZ_OK; } static SRes CSeekStream_Seek(void *p, Int64 *pos, ESzSeek origin) { struct CSeekStream *stm = p; if (!ar_seek(stm->stream, *pos, (int)origin)) return SZ_ERROR_FAIL; *pos = ar_tell(stm->stream); return SZ_OK; } static void CSeekStream_CreateVTable(struct CSeekStream *in_stream, ar_stream *stream) { in_stream->super.Read = CSeekStream_Read; in_stream->super.Seek = CSeekStream_Seek; in_stream->stream = stream; } #ifndef USE_7Z_CRC32 UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) { return ar_crc32(0, data, size); } #endif static void _7z_close(ar_archive *ar) { ar_archive_7z *_7z = (ar_archive_7z *)ar; free(_7z->entry_name); SzArEx_Free(&_7z->data, &gSzAlloc); IAlloc_Free(&gSzAlloc, _7z->uncomp.buffer); } static const char *_7z_get_name(ar_archive *ar); static bool _7z_parse_entry(ar_archive *ar, off64_t offset) { ar_archive_7z *_7z = (ar_archive_7z *)ar; const CSzFileItem *item = _7z->data.db.Files + offset; if (offset < 0 || offset > _7z->data.db.NumFiles) { warn("Offsets must be between 0 and %u", _7z->data.db.NumFiles); return false; } if (offset == _7z->data.db.NumFiles) { ar->at_eof = true; return false; } ar->entry_offset = offset; ar->entry_offset_next = offset + 1; ar->entry_size_uncompressed = (size_t)item->Size; ar->entry_filetime = item->MTimeDefined ? (time64_t)(item->MTime.Low | ((time64_t)item->MTime.High << 32)) : 0; free(_7z->entry_name); _7z->entry_name = NULL; _7z->uncomp.initialized = false; if (item->IsDir) { log("Skipping directory entry \"%s\"", _7z_get_name(ar)); return _7z_parse_entry(ar, offset + 1); } return true; } static char *SzArEx_GetFileNameUtf8(const CSzArEx *p, UInt32 fileIndex) { size_t len = p->FileNameOffsets[fileIndex + 1] - p->FileNameOffsets[fileIndex]; const Byte *src = p->FileNames.data + p->FileNameOffsets[fileIndex] * 2; const Byte *srcEnd = src + len * 2; size_t size = len * 3; char *str, *out; if (size == (size_t)-1) return NULL; str = malloc(size + 1); if (!str) return NULL; for (out = str; src < srcEnd - 1; src += 2) { out += ar_conv_rune_to_utf8(src[0] | src[1] << 8, out, str + size - out); } *out = '\0'; return str; } static const char *_7z_get_name(ar_archive *ar) { ar_archive_7z *_7z = (ar_archive_7z *)ar; if (!_7z->entry_name && ar->entry_offset_next && !ar->at_eof) { _7z->entry_name = SzArEx_GetFileNameUtf8(&_7z->data, (UInt32)ar->entry_offset); /* normalize path separators */ if (_7z->entry_name) { char *p = _7z->entry_name; while ((p = strchr(p, '\\')) != NULL) { *p = '/'; } } } return _7z->entry_name; } static bool _7z_uncompress(ar_archive *ar, void *buffer, size_t buffer_size) { ar_archive_7z *_7z = (ar_archive_7z *)ar; struct ar_archive_7z_uncomp *uncomp = &_7z->uncomp; if (!uncomp->initialized) { /* TODO: this uncompresses all data for solid compressions */ SRes res = SzArEx_Extract(&_7z->data, &_7z->look_stream.s, (UInt32)ar->entry_offset, &uncomp->folder_index, &uncomp->buffer, &uncomp->buffer_size, &uncomp->offset, &uncomp->bytes_left, &gSzAlloc, &gSzAlloc); if (res != SZ_OK) { warn("Failed to extract file at index %" PRIi64 " (failed with error %d)", ar->entry_offset, res); return false; } if (uncomp->bytes_left != ar->entry_size_uncompressed) { warn("Uncompressed sizes don't match (%" PRIuPTR " != %" PRIuPTR ")", uncomp->bytes_left, ar->entry_size_uncompressed); return false; } uncomp->initialized = true; } if (buffer_size > uncomp->bytes_left) { warn("Requesting too much data (%" PRIuPTR " < %" PRIuPTR ")", uncomp->bytes_left, buffer_size); return false; } memcpy(buffer, uncomp->buffer + uncomp->offset + ar->entry_size_uncompressed - uncomp->bytes_left, buffer_size); uncomp->bytes_left -= buffer_size; return true; } ar_archive *ar_open_7z_archive(ar_stream *stream) { ar_archive *ar; ar_archive_7z *_7z; SRes res; if (!ar_seek(stream, 0, SEEK_SET)) return NULL; ar = ar_open_archive(stream, sizeof(ar_archive_7z), _7z_close, _7z_parse_entry, _7z_get_name, _7z_uncompress, NULL, 0); if (!ar) return NULL; _7z = (ar_archive_7z *)ar; CSeekStream_CreateVTable(&_7z->in_stream, stream); LookToRead_CreateVTable(&_7z->look_stream, False); _7z->look_stream.realStream = &_7z->in_stream.super; LookToRead_Init(&_7z->look_stream); #ifdef USE_7Z_CRC32 CrcGenerateTable(); #endif SzArEx_Init(&_7z->data); res = SzArEx_Open(&_7z->data, &_7z->look_stream.s, &gSzAlloc, &gSzAlloc); if (res != SZ_OK) { if (res != SZ_ERROR_NO_ARCHIVE) warn("Invalid 7z archive (failed with error %d)", res); free(ar); return NULL; } return ar; } #else ar_archive *ar_open_7z_archive(ar_stream *stream) { (void)stream; warn("7z support requires 7z SDK (define HAVE_7Z)"); return NULL; } #endif unarr-1.0.1/_7z/_7z.h000066400000000000000000000014141317727033500142170ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #ifndef _7z_7z_h #define _7z_7z_h #include "../common/unarr-imp.h" #include "../lzmasdk/7zTypes.h" #ifdef HAVE_7Z #include <7z.h> #endif typedef struct ar_archive_7z_s ar_archive_7z; struct CSeekStream { ISeekInStream super; ar_stream *stream; }; struct ar_archive_7z_uncomp { bool initialized; UInt32 folder_index; Byte *buffer; size_t buffer_size; size_t offset; size_t bytes_left; }; struct ar_archive_7z_s { ar_archive super; struct CSeekStream in_stream; #ifdef HAVE_7Z CLookToRead look_stream; CSzArEx data; #endif char *entry_name; struct ar_archive_7z_uncomp uncomp; }; #endif unarr-1.0.1/common/000077500000000000000000000000001317727033500141405ustar00rootroot00000000000000unarr-1.0.1/common/allocator.h000066400000000000000000000014041317727033500162700ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #ifndef common_allocator_h #define common_allocator_h #ifdef USE_CUSTOM_ALLOCATOR #include typedef void *(* custom_malloc_fn)(void *opaque, size_t size); typedef void (* custom_free_fn)(void *opaque, void *ptr); void ar_set_custom_allocator(custom_malloc_fn custom_malloc, custom_free_fn custom_free, void *opaque); #define malloc(size) ar_malloc(size) #define calloc(count, size) ar_calloc(count, size) #define free(ptr) ar_free(ptr) #define realloc(ptr, size) _use_malloc_memcpy_free_instead(ptr, size) #define strdup(str) _use_malloc_memcpy_instead(str) #elif !defined(NDEBUG) && defined(_MSC_VER) #include #endif #endif unarr-1.0.1/common/conv.c000066400000000000000000000073351317727033500152610ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #include "unarr-imp.h" #include /* data from http://en.wikipedia.org/wiki/Cp437 */ static const wchar_t gCp437[256] = { 0, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266C, 0x263C, 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x2302, 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, }; size_t ar_conv_rune_to_utf8(wchar_t rune, char *out, size_t size) { if (size < 1) return 0; if (rune < 0x0080) { *out++ = rune & 0x7F; return 1; } if (rune < 0x0800 && size >= 2) { *out++ = 0xC0 | ((rune >> 6) & 0x1F); *out++ = 0x80 | (rune & 0x3F); return 2; } if (size >= 3) { if ((0xD800 <= rune && rune <= 0xDFFF) || rune >= 0x10000) rune = 0xFFFD; *out++ = 0xE0 | ((rune >> 12) & 0x0F); *out++ = 0x80 | ((rune >> 6) & 0x3F); *out++ = 0x80 | (rune & 0x3F); return 3; } *out++ = '?'; return 1; } char *ar_conv_dos_to_utf8(const char *astr) { char *str, *out; const char *in; size_t size; size = 0; for (in = astr; *in; in++) { char buf[4]; size += ar_conv_rune_to_utf8(gCp437[(uint8_t)*in], buf, sizeof(buf)); } if (size == (size_t)-1) return NULL; str = malloc(size + 1); if (!str) return NULL; for (in = astr, out = str; *in; in++) { out += ar_conv_rune_to_utf8(gCp437[(uint8_t)*in], out, str + size - out); } *out = '\0'; return str; } time64_t ar_conv_dosdate_to_filetime(uint32_t dosdate) { struct tm tm; time_t t1, t2; tm.tm_sec = (dosdate & 0x1F) * 2; tm.tm_min = (dosdate >> 5) & 0x3F; tm.tm_hour = (dosdate >> 11) & 0x1F; tm.tm_mday = (dosdate >> 16) & 0x1F; tm.tm_mon = ((dosdate >> 21) & 0x0F) - 1; tm.tm_year = ((dosdate >> 25) & 0x7F) + 80; tm.tm_isdst = -1; t1 = mktime(&tm); t2 = mktime(gmtime(&t1)); return (time64_t)(2 * t1 - t2 + 11644473600) * 10000000; } unarr-1.0.1/common/crc32.c000066400000000000000000000024121317727033500152170ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #include "unarr-imp.h" #ifndef HAVE_ZLIB /* code adapted from https://gnunet.org/svn/gnunet/src/util/crypto_crc.c (public domain) */ static bool crc_table_ready = false; static uint32_t crc_table[256]; uint32_t ar_crc32(uint32_t crc32, const unsigned char *data, size_t data_len) { if (!crc_table_ready) { uint32_t i, j; uint32_t h = 1; crc_table[0] = 0; for (i = 128; i; i >>= 1) { h = (h >> 1) ^ ((h & 1) ? 0xEDB88320 : 0); for (j = 0; j < 256; j += 2 * i) { crc_table[i + j] = crc_table[j] ^ h; } } crc_table_ready = true; } crc32 = crc32 ^ 0xFFFFFFFF; while (data_len-- > 0) { crc32 = (crc32 >> 8) ^ crc_table[(crc32 ^ *data++) & 0xFF]; } return crc32 ^ 0xFFFFFFFF; } #else #include uint32_t ar_crc32(uint32_t crc, const unsigned char *data, size_t data_len) { #if SIZE_MAX > UINT32_MAX while (data_len > UINT32_MAX) { crc = crc32(crc, data, UINT32_MAX); data += UINT32_MAX; data_len -= UINT32_MAX; } #endif return crc32(crc, data, (uint32_t)data_len); } #endif unarr-1.0.1/common/custalloc.c000066400000000000000000000024221317727033500162750ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #include #include #include typedef void *(* custom_malloc_fn)(void *opaque, size_t size); typedef void (* custom_free_fn)(void *opaque, void *ptr); static void *default_malloc(void *opaque, size_t size) { (void)opaque; return malloc(size); } static void default_free(void *opaque, void *ptr) { (void)opaque; free(ptr); } static struct { custom_malloc_fn malloc; custom_free_fn free; void *opaque; } gAllocator = { default_malloc, default_free, NULL, }; void *ar_malloc(size_t size) { return gAllocator.malloc(gAllocator.opaque, size); } void *ar_calloc(size_t count, size_t size) { void *ptr = NULL; if (size <= SIZE_MAX / count) ptr = ar_malloc(count * size); if (ptr) memset(ptr, 0, count * size); return ptr; } void ar_free(void *ptr) { gAllocator.free(gAllocator.opaque, ptr); } void ar_set_custom_allocator(custom_malloc_fn custom_malloc, custom_free_fn custom_free, void *opaque) { gAllocator.malloc = custom_malloc ? custom_malloc : default_malloc; gAllocator.free = custom_free ? custom_free : default_free; gAllocator.opaque = opaque; } unarr-1.0.1/common/stream.c000066400000000000000000000122411317727033500155770ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #include "unarr-imp.h" ar_stream *ar_open_stream(void *data, ar_stream_close_fn close, ar_stream_read_fn read, ar_stream_seek_fn seek, ar_stream_tell_fn tell) { ar_stream *stream = malloc(sizeof(ar_stream)); if (!stream) { close(data); return NULL; } stream->data = data; stream->close = close; stream->read = read; stream->seek = seek; stream->tell = tell; return stream; } void ar_close(ar_stream *stream) { if (stream) stream->close(stream->data); free(stream); } size_t ar_read(ar_stream *stream, void *buffer, size_t count) { return stream->read(stream->data, buffer, count); } bool ar_seek(ar_stream *stream, off64_t offset, int origin) { return stream->seek(stream->data, offset, origin); } bool ar_skip(ar_stream *stream, off64_t count) { return stream->seek(stream->data, count, SEEK_CUR); } off64_t ar_tell(ar_stream *stream) { return stream->tell(stream->data); } /***** stream based on FILE *****/ static void file_close(void *data) { fclose(data); } static size_t file_read(void *data, void *buffer, size_t count) { return fread(buffer, 1, count, data); } static bool file_seek(void *data, off64_t offset, int origin) { #ifdef _MSC_VER return _fseeki64(data, offset, origin) == 0; #else #if _POSIX_C_SOURCE >= 200112L if (sizeof(off_t) == 8) return fseeko(data, offset, origin) == 0; #endif if (offset > INT32_MAX || offset < INT32_MIN) return false; return fseek(data, (long)offset, origin) == 0; #endif } static off64_t file_tell(void *data) { #ifdef _MSC_VER return _ftelli64(data); #elif _POSIX_C_SOURCE >= 200112L return ftello(data); #else return ftell(data); #endif } ar_stream *ar_open_file(const char *path) { FILE *f = path ? fopen(path, "rb") : NULL; if (!f) return NULL; return ar_open_stream(f, file_close, file_read, file_seek, file_tell); } #ifdef _WIN32 ar_stream *ar_open_file_w(const wchar_t *path) { FILE *f = path ? _wfopen(path, L"rb") : NULL; if (!f) return NULL; return ar_open_stream(f, file_close, file_read, file_seek, file_tell); } #endif /***** stream based on preallocated memory *****/ struct MemoryStream { const uint8_t *data; size_t length; size_t offset; }; static void memory_close(void *data) { struct MemoryStream *stm = data; free(stm); } static size_t memory_read(void *data, void *buffer, size_t count) { struct MemoryStream *stm = data; if (count > stm->length - stm->offset) count = stm->length - stm->offset; memcpy(buffer, stm->data + stm->offset, count); stm->offset += count; return count; } static bool memory_seek(void *data, off64_t offset, int origin) { struct MemoryStream *stm = data; if (origin == SEEK_CUR) offset += stm->offset; else if (origin == SEEK_END) offset += stm->length; if (offset < 0 || offset > (off64_t)stm->length || (size_t)offset > stm->length) return false; stm->offset = (size_t)offset; return true; } static off64_t memory_tell(void *data) { struct MemoryStream *stm = data; return stm->offset; } ar_stream *ar_open_memory(const void *data, size_t datalen) { struct MemoryStream *stm = malloc(sizeof(struct MemoryStream)); if (!stm) return NULL; stm->data = data; stm->length = datalen; stm->offset = 0; return ar_open_stream(stm, memory_close, memory_read, memory_seek, memory_tell); } #ifdef _WIN32 /***** stream based on IStream *****/ #define COBJMACROS #include static void stream_close(void *data) { IUnknown_Release((IStream *)data); } static size_t stream_read(void *data, void *buffer, size_t count) { size_t read = 0; HRESULT res; ULONG cbRead; #ifdef _WIN64 while (count > ULONG_MAX) { res = IStream_Read((IStream *)data, buffer, ULONG_MAX, &cbRead); if (FAILED(res)) return read; read += cbRead; buffer = (BYTE *)buffer + ULONG_MAX; count -= ULONG_MAX; } #endif res = IStream_Read((IStream *)data, buffer, (ULONG)count, &cbRead); if (SUCCEEDED(res)) read += cbRead; return read; } static bool stream_seek(void *data, off64_t offset, int origin) { LARGE_INTEGER off; ULARGE_INTEGER n; HRESULT res; off.QuadPart = offset; res = IStream_Seek((IStream *)data, off, origin, &n); return SUCCEEDED(res); } static off64_t stream_tell(void *data) { LARGE_INTEGER zero = { 0 }; ULARGE_INTEGER n = { 0 }; IStream_Seek((IStream *)data, zero, SEEK_CUR, &n); return (off64_t)n.QuadPart; } ar_stream *ar_open_istream(IStream *stream) { LARGE_INTEGER zero = { 0 }; HRESULT res = IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL); if (FAILED(res)) return NULL; IUnknown_AddRef(stream); return ar_open_stream(stream, stream_close, stream_read, stream_seek, stream_tell); } #endif unarr-1.0.1/common/unarr-imp.h000066400000000000000000000052151317727033500162260ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ /* this is the common private/implementation API of unarr which should only be used by unarr code */ #ifndef common_unarr_imp_h #define common_unarr_imp_h #include "../unarr.h" #include "allocator.h" #include #include #include #include #include /***** conv ****/ size_t ar_conv_rune_to_utf8(wchar_t rune, char *out, size_t size); char *ar_conv_dos_to_utf8(const char *astr); time64_t ar_conv_dosdate_to_filetime(uint32_t dosdate); /***** crc32 *****/ uint32_t ar_crc32(uint32_t crc32, const unsigned char *data, size_t data_len); /***** stream *****/ typedef void (* ar_stream_close_fn)(void *data); typedef size_t (* ar_stream_read_fn)(void *data, void *buffer, size_t count); typedef bool (* ar_stream_seek_fn)(void *data, off64_t offset, int origin); typedef off64_t (* ar_stream_tell_fn)(void *data); struct ar_stream_s { ar_stream_close_fn close; ar_stream_read_fn read; ar_stream_seek_fn seek; ar_stream_tell_fn tell; void *data; }; ar_stream *ar_open_stream(void *data, ar_stream_close_fn close, ar_stream_read_fn read, ar_stream_seek_fn seek, ar_stream_tell_fn tell); /***** unarr *****/ #define warn(...) ar_log("!", __FILE__, __LINE__, __VA_ARGS__) #ifndef NDEBUG #define log(...) ar_log("-", __FILE__, __LINE__, __VA_ARGS__) #else #define log(...) ((void)0) #endif void ar_log(const char *prefix, const char *file, int line, const char *msg, ...); typedef void (* ar_archive_close_fn)(ar_archive *ar); typedef bool (* ar_parse_entry_fn)(ar_archive *ar, off64_t offset); typedef const char *(* ar_entry_get_name_fn)(ar_archive *ar); typedef bool (* ar_entry_uncompress_fn)(ar_archive *ar, void *buffer, size_t count); typedef size_t (* ar_get_global_comment_fn)(ar_archive *ar, void *buffer, size_t count); struct ar_archive_s { ar_archive_close_fn close; ar_parse_entry_fn parse_entry; ar_entry_get_name_fn get_name; ar_entry_uncompress_fn uncompress; ar_get_global_comment_fn get_comment; ar_stream *stream; bool at_eof; off64_t entry_offset; off64_t entry_offset_first; off64_t entry_offset_next; size_t entry_size_uncompressed; time64_t entry_filetime; }; ar_archive *ar_open_archive(ar_stream *stream, size_t struct_size, ar_archive_close_fn close, ar_parse_entry_fn parse_entry, ar_entry_get_name_fn get_name, ar_entry_uncompress_fn uncompress, ar_get_global_comment_fn get_comment, off64_t first_entry_offset); #endif unarr-1.0.1/common/unarr.c000066400000000000000000000053631317727033500154420ustar00rootroot00000000000000/* 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); } unarr-1.0.1/lzmasdk/000077500000000000000000000000001317727033500143155ustar00rootroot00000000000000unarr-1.0.1/lzmasdk/7zTypes.h000066400000000000000000000217331317727033500160610ustar00rootroot00000000000000/* 7zTypes.h -- Basic types 2017-07-17 : Igor Pavlov : Public domain */ #ifndef __7Z_TYPES_H #define __7Z_TYPES_H #ifdef _WIN32 /* #include */ #endif #include #ifndef EXTERN_C_BEGIN #ifdef __cplusplus #define EXTERN_C_BEGIN extern "C" { #define EXTERN_C_END } #else #define EXTERN_C_BEGIN #define EXTERN_C_END #endif #endif EXTERN_C_BEGIN #define SZ_OK 0 #define SZ_ERROR_DATA 1 #define SZ_ERROR_MEM 2 #define SZ_ERROR_CRC 3 #define SZ_ERROR_UNSUPPORTED 4 #define SZ_ERROR_PARAM 5 #define SZ_ERROR_INPUT_EOF 6 #define SZ_ERROR_OUTPUT_EOF 7 #define SZ_ERROR_READ 8 #define SZ_ERROR_WRITE 9 #define SZ_ERROR_PROGRESS 10 #define SZ_ERROR_FAIL 11 #define SZ_ERROR_THREAD 12 #define SZ_ERROR_ARCHIVE 16 #define SZ_ERROR_NO_ARCHIVE 17 typedef int SRes; #ifdef _WIN32 /* typedef DWORD WRes; */ typedef unsigned WRes; #define MY_SRes_HRESULT_FROM_WRes(x) HRESULT_FROM_WIN32(x) #else typedef int WRes; #define MY__FACILITY_WIN32 7 #define MY__FACILITY__WRes MY__FACILITY_WIN32 #define MY_SRes_HRESULT_FROM_WRes(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (MY__FACILITY__WRes << 16) | 0x80000000))) #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_FORCE_INLINE __forceinline #define MY_CDECL __cdecl #define MY_FAST_CALL __fastcall #else #define MY_NO_INLINE #define MY_FORCE_INLINE #define MY_CDECL #define MY_FAST_CALL /* inline keyword : for C++ / C99 */ /* GCC, clang: */ /* #if defined (__GNUC__) && (__GNUC__ >= 4) #define MY_FORCE_INLINE __attribute__((always_inline)) #define MY_NO_INLINE __attribute__((noinline)) #endif */ #endif /* The following interfaces use first parameter as pointer to structure */ typedef struct IByteIn IByteIn; struct IByteIn { Byte (*Read)(const IByteIn *p); /* reads one byte, returns 0 in case of EOF or error */ }; #define IByteIn_Read(p) (p)->Read(p) typedef struct IByteOut IByteOut; struct IByteOut { void (*Write)(const IByteOut *p, Byte b); }; #define IByteOut_Write(p, b) (p)->Write(p, b) typedef struct ISeqInStream ISeqInStream; struct ISeqInStream { SRes (*Read)(const ISeqInStream *p, void *buf, size_t *size); /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. (output(*size) < input(*size)) is allowed */ }; #define ISeqInStream_Read(p, buf, size) (p)->Read(p, buf, size) /* it can return SZ_ERROR_INPUT_EOF */ SRes SeqInStream_Read(const ISeqInStream *stream, void *buf, size_t size); SRes SeqInStream_Read2(const ISeqInStream *stream, void *buf, size_t size, SRes errorType); SRes SeqInStream_ReadByte(const ISeqInStream *stream, Byte *buf); typedef struct ISeqOutStream ISeqOutStream; struct ISeqOutStream { size_t (*Write)(const ISeqOutStream *p, const void *buf, size_t size); /* Returns: result - the number of actually written bytes. (result < size) means error */ }; #define ISeqOutStream_Write(p, buf, size) (p)->Write(p, buf, size) typedef enum { SZ_SEEK_SET = 0, SZ_SEEK_CUR = 1, SZ_SEEK_END = 2 } ESzSeek; typedef struct ISeekInStream ISeekInStream; struct ISeekInStream { SRes (*Read)(const ISeekInStream *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ SRes (*Seek)(const ISeekInStream *p, Int64 *pos, ESzSeek origin); }; #define ISeekInStream_Read(p, buf, size) (p)->Read(p, buf, size) #define ISeekInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin) typedef struct ILookInStream ILookInStream; struct ILookInStream { SRes (*Look)(const ILookInStream *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)(const ILookInStream *p, size_t offset); /* offset must be <= output(*size) of Look */ SRes (*Read)(const ILookInStream *p, void *buf, size_t *size); /* reads directly (without buffer). It's same as ISeqInStream::Read */ SRes (*Seek)(const ILookInStream *p, Int64 *pos, ESzSeek origin); }; #define ILookInStream_Look(p, buf, size) (p)->Look(p, buf, size) #define ILookInStream_Skip(p, offset) (p)->Skip(p, offset) #define ILookInStream_Read(p, buf, size) (p)->Read(p, buf, size) #define ILookInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin) SRes LookInStream_LookRead(const ILookInStream *stream, void *buf, size_t *size); SRes LookInStream_SeekTo(const ILookInStream *stream, UInt64 offset); /* reads via ILookInStream::Read */ SRes LookInStream_Read2(const ILookInStream *stream, void *buf, size_t size, SRes errorType); SRes LookInStream_Read(const ILookInStream *stream, void *buf, size_t size); typedef struct { ILookInStream vt; const ISeekInStream *realStream; size_t pos; size_t size; /* it's data size */ /* the following variables must be set outside */ Byte *buf; size_t bufSize; } CLookToRead2; void LookToRead2_CreateVTable(CLookToRead2 *p, int lookahead); #define LookToRead2_Init(p) { (p)->pos = (p)->size = 0; } typedef struct { ISeqInStream vt; const ILookInStream *realStream; } CSecToLook; void SecToLook_CreateVTable(CSecToLook *p); typedef struct { ISeqInStream vt; const ILookInStream *realStream; } CSecToRead; void SecToRead_CreateVTable(CSecToRead *p); typedef struct ICompressProgress ICompressProgress; struct ICompressProgress { SRes (*Progress)(const ICompressProgress *p, UInt64 inSize, UInt64 outSize); /* Returns: result. (result != SZ_OK) means break. Value (UInt64)(Int64)-1 for size means unknown value. */ }; #define ICompressProgress_Progress(p, inSize, outSize) (p)->Progress(p, inSize, outSize) typedef struct ISzAlloc ISzAlloc; typedef const ISzAlloc * ISzAllocPtr; struct ISzAlloc { void *(*Alloc)(ISzAllocPtr p, size_t size); void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */ }; #define ISzAlloc_Alloc(p, size) (p)->Alloc(p, size) #define ISzAlloc_Free(p, a) (p)->Free(p, a) /* deprecated */ #define IAlloc_Alloc(p, size) ISzAlloc_Alloc(p, size) #define IAlloc_Free(p, a) ISzAlloc_Free(p, a) #ifndef MY_offsetof #ifdef offsetof #define MY_offsetof(type, m) offsetof(type, m) /* #define MY_offsetof(type, m) FIELD_OFFSET(type, m) */ #else #define MY_offsetof(type, m) ((size_t)&(((type *)0)->m)) #endif #endif #ifndef MY_container_of /* #define MY_container_of(ptr, type, m) container_of(ptr, type, m) #define MY_container_of(ptr, type, m) CONTAINING_RECORD(ptr, type, m) #define MY_container_of(ptr, type, m) ((type *)((char *)(ptr) - offsetof(type, m))) #define MY_container_of(ptr, type, m) (&((type *)0)->m == (ptr), ((type *)(((char *)(ptr)) - MY_offsetof(type, m)))) */ /* GCC shows warning: "perhaps the 'offsetof' macro was used incorrectly" GCC 3.4.4 : classes with constructor GCC 4.8.1 : classes with non-public variable members" */ #define MY_container_of(ptr, type, m) ((type *)((char *)(1 ? (ptr) : &((type *)0)->m) - MY_offsetof(type, m))) #endif #define CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) ((type *)(ptr)) /* #define CONTAINER_FROM_VTBL(ptr, type, m) CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) */ #define CONTAINER_FROM_VTBL(ptr, type, m) MY_container_of(ptr, type, m) #define CONTAINER_FROM_VTBL_CLS(ptr, type, m) CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) /* #define CONTAINER_FROM_VTBL_CLS(ptr, type, m) CONTAINER_FROM_VTBL(ptr, type, m) */ #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 unarr-1.0.1/lzmasdk/CpuArch.c000066400000000000000000000103631317727033500160110ustar00rootroot00000000000000/* CpuArch.c -- CPU specific code 2016-02-25: Igor Pavlov : Public domain */ #include "Precomp.h" #include "CpuArch.h" #ifdef MY_CPU_X86_OR_AMD64 #if (defined(_MSC_VER) && !defined(MY_CPU_AMD64)) || defined(__GNUC__) #define USE_ASM #endif #if !defined(USE_ASM) && _MSC_VER >= 1500 #include #endif #if defined(USE_ASM) && !defined(MY_CPU_AMD64) static UInt32 CheckFlag(UInt32 flag) { #ifdef _MSC_VER __asm pushfd; __asm pop EAX; __asm mov EDX, EAX; __asm xor EAX, flag; __asm push EAX; __asm popfd; __asm pushfd; __asm pop EAX; __asm xor EAX, EDX; __asm push EDX; __asm popfd; __asm and flag, EAX; #else __asm__ __volatile__ ( "pushf\n\t" "pop %%EAX\n\t" "movl %%EAX,%%EDX\n\t" "xorl %0,%%EAX\n\t" "push %%EAX\n\t" "popf\n\t" "pushf\n\t" "pop %%EAX\n\t" "xorl %%EDX,%%EAX\n\t" "push %%EDX\n\t" "popf\n\t" "andl %%EAX, %0\n\t": "=c" (flag) : "c" (flag) : "%eax", "%edx"); #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 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_AMD64) && defined(__PIC__) "mov %%rbx, %%rdi;" "cpuid;" "xchg %%rbx, %%rdi;" : "=a" (*a) , "=D" (*b) , #elif 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 const 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.ver); model = x86cpuid_GetModel(p.ver); firm = x86cpuid_GetFirm(&p); switch (firm) { case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && ( /* In-Order Atom CPU */ model == 0x1C /* 45 nm, N4xx, D4xx, N5xx, D5xx, 230, 330 */ || model == 0x26 /* 45 nm, Z6xx */ || model == 0x27 /* 32 nm, Z2460 */ || model == 0x35 /* 32 nm, Z2760 */ || model == 0x36 /* 32 nm, N2xxx, D2xxx */ ))); case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA))); case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF)); } return True; } #if !defined(MY_CPU_AMD64) && defined(_WIN32) #include static Bool CPU_Sys_Is_SSE_Supported() { OSVERSIONINFO vi; vi.dwOSVersionInfoSize = sizeof(vi); if (!GetVersionEx(&vi)) return False; return (vi.dwMajorVersion >= 5); } #define CHECK_SYS_SSE_SUPPORT if (!CPU_Sys_Is_SSE_Supported()) return False; #else #define CHECK_SYS_SSE_SUPPORT #endif Bool CPU_Is_Aes_Supported() { Cx86cpuid p; CHECK_SYS_SSE_SUPPORT if (!x86cpuid_CheckAndRead(&p)) return False; return (p.c >> 25) & 1; } #endif unarr-1.0.1/lzmasdk/CpuArch.h000066400000000000000000000170641317727033500160230ustar00rootroot00000000000000/* CpuArch.h -- CPU specific code 2017-06-30 : 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. MY_CPU_BE means that CPU is BIG ENDIAN. If MY_CPU_LE and MY_CPU_BE are not defined, we don't know about ENDIANNESS of platform. MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. */ #if defined(_M_X64) \ || defined(_M_AMD64) \ || defined(__x86_64__) \ || defined(__AMD64__) \ || defined(__amd64__) #define MY_CPU_AMD64 #ifdef __ILP32__ #define MY_CPU_NAME "x32" #else #define MY_CPU_NAME "x64" #endif #define MY_CPU_64BIT #endif #if defined(_M_IX86) \ || defined(__i386__) #define MY_CPU_X86 #define MY_CPU_NAME "x86" #define MY_CPU_32BIT #endif #if defined(_M_ARM64) \ || defined(__AARCH64EL__) \ || defined(__AARCH64EB__) \ || defined(__aarch64__) #define MY_CPU_ARM64 #define MY_CPU_NAME "arm64" #define MY_CPU_64BIT #endif #if defined(_M_ARM) \ || defined(_M_ARM_NT) \ || defined(_M_ARMT) \ || defined(__arm__) \ || defined(__thumb__) \ || defined(__ARMEL__) \ || defined(__ARMEB__) \ || defined(__THUMBEL__) \ || defined(__THUMBEB__) #define MY_CPU_ARM #define MY_CPU_NAME "arm" #define MY_CPU_32BIT #endif #if defined(_M_IA64) \ || defined(__ia64__) #define MY_CPU_IA64 #define MY_CPU_NAME "ia64" #define MY_CPU_64BIT #endif #if defined(__mips64) \ || defined(__mips64__) \ || (defined(__mips) && (__mips == 64 || __mips == 4 || __mips == 3)) #define MY_CPU_NAME "mips64" #define MY_CPU_64BIT #elif defined(__mips__) #define MY_CPU_NAME "mips" /* #define MY_CPU_32BIT */ #endif #if defined(__ppc64__) \ || defined(__powerpc64__) #ifdef __ILP32__ #define MY_CPU_NAME "ppc64-32" #else #define MY_CPU_NAME "ppc64" #endif #define MY_CPU_64BIT #elif defined(__ppc__) \ || defined(__powerpc__) #define MY_CPU_NAME "ppc" #define MY_CPU_32BIT #endif #if defined(__sparc64__) #define MY_CPU_NAME "sparc64" #define MY_CPU_64BIT #elif defined(__sparc__) #define MY_CPU_NAME "sparc" /* #define MY_CPU_32BIT */ #endif #if defined(MY_CPU_X86) || defined(MY_CPU_AMD64) #define MY_CPU_X86_OR_AMD64 #endif #ifdef _WIN32 #ifdef MY_CPU_ARM #define MY_CPU_ARM_LE #endif #ifdef MY_CPU_ARM64 #define MY_CPU_ARM64_LE #endif #ifdef _M_IA64 #define MY_CPU_IA64_LE #endif #endif #if defined(MY_CPU_X86_OR_AMD64) \ || defined(MY_CPU_ARM_LE) \ || defined(MY_CPU_ARM64_LE) \ || defined(MY_CPU_IA64_LE) \ || defined(__LITTLE_ENDIAN__) \ || defined(__ARMEL__) \ || defined(__THUMBEL__) \ || defined(__AARCH64EL__) \ || defined(__MIPSEL__) \ || defined(__MIPSEL) \ || defined(_MIPSEL) \ || defined(__BFIN__) \ || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) #define MY_CPU_LE #endif #if defined(__BIG_ENDIAN__) \ || defined(__ARMEB__) \ || defined(__THUMBEB__) \ || defined(__AARCH64EB__) \ || defined(__MIPSEB__) \ || defined(__MIPSEB) \ || defined(_MIPSEB) \ || defined(__m68k__) \ || defined(__s390__) \ || defined(__s390x__) \ || defined(__zarch__) \ || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) #define MY_CPU_BE #endif #if defined(MY_CPU_LE) && defined(MY_CPU_BE) #error Stop_Compiling_Bad_Endian #endif #if defined(MY_CPU_32BIT) && defined(MY_CPU_64BIT) #error Stop_Compiling_Bad_32_64_BIT #endif #ifndef MY_CPU_NAME #ifdef MY_CPU_LE #define MY_CPU_NAME "LE" #elif defined (MY_CPU_BE) #define MY_CPU_NAME "BE" #else /* #define MY_CPU_NAME "" */ #endif #endif #ifdef MY_CPU_LE #if defined(MY_CPU_X86_OR_AMD64) \ || defined(MY_CPU_ARM64) \ || defined(__ARM_FEATURE_UNALIGNED) #define MY_CPU_LE_UNALIGN #endif #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, v) { *(UInt16 *)(p) = (v); } #define SetUi32(p, v) { *(UInt32 *)(p) = (v); } #define SetUi64(p, v) { *(UInt64 *)(p) = (v); } #else #define GetUi16(p) ( (UInt16) ( \ ((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, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ _ppp_[0] = (Byte)_vvv_; \ _ppp_[1] = (Byte)(_vvv_ >> 8); } #define SetUi32(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ _ppp_[0] = (Byte)_vvv_; \ _ppp_[1] = (Byte)(_vvv_ >> 8); \ _ppp_[2] = (Byte)(_vvv_ >> 16); \ _ppp_[3] = (Byte)(_vvv_ >> 24); } #define SetUi64(p, v) { Byte *_ppp2_ = (Byte *)(p); UInt64 _vvv2_ = (v); \ SetUi32(_ppp2_ , (UInt32)_vvv2_); \ SetUi32(_ppp2_ + 4, (UInt32)(_vvv2_ >> 32)); } #endif #ifdef __has_builtin #define MY__has_builtin(x) __has_builtin(x) #else #define MY__has_builtin(x) 0 #endif #if defined(MY_CPU_LE_UNALIGN) && /* defined(_WIN64) && */ (_MSC_VER >= 1300) /* Note: we use bswap instruction, that is unsupported in 386 cpu */ #include #pragma intrinsic(_byteswap_ushort) #pragma intrinsic(_byteswap_ulong) #pragma intrinsic(_byteswap_uint64) /* #define GetBe16(p) _byteswap_ushort(*(const UInt16 *)(const Byte *)(p)) */ #define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) #define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) #define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = _byteswap_ulong(v) #elif defined(MY_CPU_LE_UNALIGN) && ( \ (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) \ || (defined(__clang__) && MY__has_builtin(__builtin_bswap16)) ) /* #define GetBe16(p) __builtin_bswap16(*(const UInt16 *)(const Byte *)(p)) */ #define GetBe32(p) __builtin_bswap32(*(const UInt32 *)(const Byte *)(p)) #define GetBe64(p) __builtin_bswap64(*(const UInt64 *)(const Byte *)(p)) #define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = __builtin_bswap32(v) #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)) #define SetBe32(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ _ppp_[0] = (Byte)(_vvv_ >> 24); \ _ppp_[1] = (Byte)(_vvv_ >> 16); \ _ppp_[2] = (Byte)(_vvv_ >> 8); \ _ppp_[3] = (Byte)_vvv_; } #endif #ifndef GetBe16 #define GetBe16(p) ( (UInt16) ( \ ((UInt16)((const Byte *)(p))[0] << 8) | \ ((const Byte *)(p))[1] )) #endif #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 }; void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d); Bool x86cpuid_CheckAndRead(Cx86cpuid *p); int x86cpuid_GetFirm(const Cx86cpuid *p); #define x86cpuid_GetFamily(ver) (((ver >> 16) & 0xFF0) | ((ver >> 8) & 0xF)) #define x86cpuid_GetModel(ver) (((ver >> 12) & 0xF0) | ((ver >> 4) & 0xF)) #define x86cpuid_GetStepping(ver) (ver & 0xF) Bool CPU_Is_InOrder(void); Bool CPU_Is_Aes_Supported(void); #endif EXTERN_C_END #endif unarr-1.0.1/lzmasdk/LzmaDec.c000066400000000000000000000741641317727033500160140ustar00rootroot00000000000000/* LzmaDec.c -- LZMA Decoder 2017-04-03 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "LzmaDec.h" #include #define kNumTopBits 24 #define kTopValue ((UInt32)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define RC_INIT_SIZE 5 #define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) #define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); #define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); #define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ { UPDATE_0(p); i = (i + i); A0; } else \ { UPDATE_1(p); i = (i + i) + 1; A1; } #define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) #define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } #define TREE_DECODE(probs, limit, i) \ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } /* #define _LZMA_SIZE_OPT */ #ifdef _LZMA_SIZE_OPT #define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) #else #define TREE_6_DECODE(probs, i) \ { i = 1; \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ i -= 0x40; } #endif #define NORMAL_LITER_DEC GET_BIT(prob + symbol, symbol) #define MATCHED_LITER_DEC \ matchByte <<= 1; \ bit = (matchByte & offs); \ probLit = prob + offs + bit + symbol; \ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) #define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) #define UPDATE_0_CHECK range = bound; #define UPDATE_1_CHECK range -= bound; code -= bound; #define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ { UPDATE_0_CHECK; i = (i + i); A0; } else \ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } #define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) #define TREE_DECODE_CHECK(probs, limit, i) \ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } #define kNumPosBitsMax 4 #define kNumPosStatesMax (1 << kNumPosBitsMax) #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) #define kLenNumMidBits 3 #define kLenNumMidSymbols (1 << kLenNumMidBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) #define LenChoice 0 #define LenChoice2 (LenChoice + 1) #define LenLow (LenChoice2 + 1) #define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) #define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) #define kNumLenProbs (LenHigh + kLenNumHighSymbols) #define kNumStates 12 #define kNumLitStates 7 #define kStartPosModelIndex 4 #define kEndPosModelIndex 14 #define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) #define kNumPosSlotBits 6 #define kNumLenToPosStates 4 #define kNumAlignBits 4 #define kAlignTableSize (1 << kNumAlignBits) #define kMatchMinLen 2 #define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) #define IsMatch 0 #define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) #define IsRepG0 (IsRep + kNumStates) #define IsRepG1 (IsRepG0 + kNumStates) #define IsRepG2 (IsRepG1 + kNumStates) #define IsRep0Long (IsRepG2 + kNumStates) #define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) #define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) #define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) #define LenCoder (Align + kAlignTableSize) #define RepLenCoder (LenCoder + kNumLenProbs) #define Literal (RepLenCoder + kNumLenProbs) #define LZMA_BASE_SIZE 1846 #define LZMA_LIT_SIZE 0x300 #if Literal != LZMA_BASE_SIZE StopCompilingDueBUG #endif #define LzmaProps_GetNumProbs(p) (Literal + ((UInt32)LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) #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 (unused now) = kMatchSpecLenStart + 2 : State Init Marker (unused now) */ 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 (processedPos != 0 || checkDicSize != 0) prob += ((UInt32)LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); processedPos++; 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; continue; } { 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; } #ifdef _LZMA_SIZE_OPT { unsigned lim, offset; CLzmaProb *probLen = prob + LenChoice; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenLow + (posState << kLenNumLowBits); offset = 0; lim = (1 << kLenNumLowBits); } else { UPDATE_1(probLen); probLen = prob + LenChoice2; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenMid + (posState << kLenNumMidBits); offset = kLenNumLowSymbols; lim = (1 << kLenNumMidBits); } else { UPDATE_1(probLen); probLen = prob + LenHigh; offset = kLenNumLowSymbols + kLenNumMidSymbols; lim = (1 << kLenNumHighBits); } } TREE_DECODE(probLen, lim, len); len += offset; } #else { CLzmaProb *probLen = prob + LenChoice; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenLow + (posState << kLenNumLowBits); len = 1; TREE_GET_BIT(probLen, len); TREE_GET_BIT(probLen, len); TREE_GET_BIT(probLen, len); len -= 8; } else { UPDATE_1(probLen); probLen = prob + LenChoice2; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenMid + (posState << kLenNumMidBits); len = 1; TREE_GET_BIT(probLen, len); TREE_GET_BIT(probLen, len); TREE_GET_BIT(probLen, len); } else { UPDATE_1(probLen); probLen = prob + LenHigh; TREE_DECODE(probLen, (1 << kLenNumHighBits), len); len += kLenNumLowSymbols + kLenNumMidSymbols; } } } #endif 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; unsigned numDirectBits = (unsigned)(((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) { p->dicPos = dicPos; return SZ_ERROR_DATA; } } else if (distance >= checkDicSize) { p->dicPos = dicPos; return SZ_ERROR_DATA; } state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; } len += kMatchMinLen; { SizeT rem; unsigned curLen; SizeT pos; if ((rem = limit - dicPos) == 0) { p->dicPos = dicPos; return SZ_ERROR_DATA; } curLen = ((rem < len) ? (unsigned)rem : len); pos = dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0); processedPos += curLen; len -= curLen; if (curLen <= dicBufSize - pos) { 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; SizeT rep0 = p->reps[0]; /* we use SizeT to avoid the BUG of VC14 for AMD64 */ SizeT rem = limit - dicPos; if (rem < len) len = (unsigned)(rem); 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->checkDicSize == 0 && 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; const CLzmaProb *probs = p->probs; unsigned state = p->state; ELzmaDummy res; { const 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 += ((UInt32)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; const 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; const 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) { unsigned 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_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) { SizeT numProbs = LzmaProps_GetNumProbs(&p->prop); SizeT 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) { 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; p->code = ((UInt32)p->tempBuf[1] << 24) | ((UInt32)p->tempBuf[2] << 16) | ((UInt32)p->tempBuf[3] << 8) | ((UInt32)p->tempBuf[4]); p->range = 0xFFFFFFFF; p->needFlush = 0; 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; { unsigned kkk = (unsigned)(p->buf - p->tempBuf); if (rem < kkk) return SZ_ERROR_FAIL; /* some internal error */ rem -= kkk; if (lookAhead < rem) return SZ_ERROR_FAIL; /* some internal error */ lookAhead -= rem; } (*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, ISzAllocPtr alloc) { ISzAlloc_Free(alloc, p->probs); p->probs = NULL; } static void LzmaDec_FreeDict(CLzmaDec *p, ISzAllocPtr alloc) { ISzAlloc_Free(alloc, p->dic); p->dic = NULL; } void LzmaDec_Free(CLzmaDec *p, ISzAllocPtr 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, ISzAllocPtr alloc) { UInt32 numProbs = LzmaProps_GetNumProbs(propNew); if (!p->probs || numProbs != p->numProbs) { LzmaDec_FreeProbs(p, alloc); p->probs = (CLzmaProb *)ISzAlloc_Alloc(alloc, numProbs * sizeof(CLzmaProb)); p->numProbs = numProbs; if (!p->probs) return SZ_ERROR_MEM; } return SZ_OK; } SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr 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, ISzAllocPtr alloc) { CLzmaProps propNew; SizeT dicBufSize; RINOK(LzmaProps_Decode(&propNew, props, propsSize)); RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); { UInt32 dictSize = propNew.dicSize; SizeT mask = ((UInt32)1 << 12) - 1; if (dictSize >= ((UInt32)1 << 30)) mask = ((UInt32)1 << 22) - 1; else if (dictSize >= ((UInt32)1 << 22)) mask = ((UInt32)1 << 20) - 1;; dicBufSize = ((SizeT)dictSize + mask) & ~mask; if (dicBufSize < dictSize) dicBufSize = dictSize; } if (!p->dic || dicBufSize != p->dicBufSize) { LzmaDec_FreeDict(p, alloc); p->dic = (Byte *)ISzAlloc_Alloc(alloc, dicBufSize); if (!p->dic) { 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, ISzAllocPtr 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; } unarr-1.0.1/lzmasdk/LzmaDec.h000066400000000000000000000156311317727033500160130ustar00rootroot00000000000000/* LzmaDec.h -- LZMA Decoder 2017-04-03 : 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, ISzAllocPtr alloc); void LzmaDec_FreeProbs(CLzmaDec *p, ISzAllocPtr alloc); SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAllocPtr alloc); void LzmaDec_Free(CLzmaDec *state, ISzAllocPtr 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, ISzAllocPtr alloc); EXTERN_C_END #endif unarr-1.0.1/lzmasdk/Ppmd.h000066400000000000000000000041051317727033500153660ustar00rootroot00000000000000/* Ppmd.h -- PPMD codec common code 2017-04-03 : 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) \ { size_t z; for (z = 0; z < 256 / sizeof(p[0]); z += 8) { \ p[z+7] = p[z+6] = p[z+5] = p[z+4] = p[z+3] = p[z+2] = p[z+1] = p[z+0] = ~(size_t)0; }} EXTERN_C_END #endif unarr-1.0.1/lzmasdk/Ppmd7.c000066400000000000000000000423701317727033500154560ustar00rootroot00000000000000/* Ppmd7.c -- PPMdH codec 2017-04-03 : Igor Pavlov : Public domain This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ #include "Precomp.h" #include #include "Ppmd7.h" const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; #define MAX_FREQ 124 #define UNIT_SIZE 12 #define U2B(nu) ((UInt32)(nu) * UNIT_SIZE) #define U2I(nu) (p->Units2Indx[(size_t)(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, ISzAllocPtr alloc) { ISzAlloc_Free(alloc, p->Base); p->Size = 0; p->Base = 0; } Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAllocPtr alloc) { if (!p->Base || p->Size != size) { size_t size2; Ppmd7_Free(p, alloc); size2 = 0 #ifndef PPMD_32BIT + UNIT_SIZE #endif ; p->AlignOffset = #ifdef PPMD_32BIT (4 - size) & 3; #else 4 - (size & 3); #endif if ((p->Base = (Byte *)ISzAlloc_Alloc(alloc, p->AlignOffset + size + size2)) == 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((size_t)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[(unsigned)p->NS2Indx[(size_t)nonMasked - 1]] + (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) + 2 * (unsigned)(p->MinContext->SummFreq < 11 * p->MinContext->NumStats) + 4 * (unsigned)(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); } unarr-1.0.1/lzmasdk/Ppmd7.h000066400000000000000000000075731317727033500154710ustar00rootroot00000000000000/* Ppmd7.h -- PPMdH compression codec 2017-04-03 : 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, ISzAllocPtr alloc); void Ppmd7_Free(CPpmd7 *p, ISzAllocPtr 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[(size_t)(unsigned)Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \ p->NS2BSIndx[(size_t)Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \ (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \ 2 * p->HB2Flag[(unsigned)Ppmd7Context_OneState(p->MinContext)->Symbol] + \ ((p->RunLength >> 26) & 0x20)] CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *scale); /* ---------- Decode ---------- */ typedef struct IPpmd7_RangeDec IPpmd7_RangeDec; struct IPpmd7_RangeDec { UInt32 (*GetThreshold)(const IPpmd7_RangeDec *p, UInt32 total); void (*Decode)(const IPpmd7_RangeDec *p, UInt32 start, UInt32 size); UInt32 (*DecodeBit)(const IPpmd7_RangeDec *p, UInt32 size0); }; typedef struct { IPpmd7_RangeDec vt; 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, const 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 unarr-1.0.1/lzmasdk/Ppmd7Dec.c000066400000000000000000000114711317727033500160700ustar00rootroot00000000000000/* Ppmd7Dec.c -- PPMdH Decoder 2017-04-03 : 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 (IByteIn_Read(p->Stream) != 0) return False; for (i = 0; i < 4; i++) p->Code = (p->Code << 8) | IByteIn_Read(p->Stream); return (p->Code < 0xFFFFFFFF); } #define GET_Ppmd7z_RangeDec CPpmd7z_RangeDec *p = CONTAINER_FROM_VTBL(pp, CPpmd7z_RangeDec, vt); static UInt32 Range_GetThreshold(const IPpmd7_RangeDec *pp, UInt32 total) { GET_Ppmd7z_RangeDec return p->Code / (p->Range /= total); } static void Range_Normalize(CPpmd7z_RangeDec *p) { if (p->Range < kTopValue) { p->Code = (p->Code << 8) | IByteIn_Read(p->Stream); p->Range <<= 8; if (p->Range < kTopValue) { p->Code = (p->Code << 8) | IByteIn_Read(p->Stream); p->Range <<= 8; } } } static void Range_Decode(const IPpmd7_RangeDec *pp, UInt32 start, UInt32 size) { GET_Ppmd7z_RangeDec p->Code -= start * p->Range; p->Range *= size; Range_Normalize(p); } static UInt32 Range_DecodeBit(const IPpmd7_RangeDec *pp, UInt32 size0) { GET_Ppmd7z_RangeDec 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->vt.GetThreshold = Range_GetThreshold; p->vt.Decode = Range_Decode; p->vt.DecodeBit = Range_DecodeBit; } #define MASK(sym) ((signed char *)charMask)[sym] int Ppmd7_DecodeSymbol(CPpmd7 *p, const 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); } } unarr-1.0.1/lzmasdk/Ppmd8.c000066400000000000000000000702261317727033500154600ustar00rootroot00000000000000/* Ppmd8.c -- PPMdI codec 2017-04-03 : Igor Pavlov : Public domain This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */ #include "Precomp.h" #include #include "Ppmd8.h" const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; #define MAX_FREQ 124 #define UNIT_SIZE 12 #define U2B(nu) ((UInt32)(nu) * UNIT_SIZE) #define U2I(nu) (p->Units2Indx[(size_t)(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, ISzAllocPtr alloc) { ISzAlloc_Free(alloc, p->Base); p->Size = 0; p->Base = 0; } Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size, ISzAllocPtr alloc) { if (!p->Base || p->Size != size) { Ppmd8_Free(p, alloc); p->AlignOffset = #ifdef PPMD_32BIT (4 - size) & 3; #else 4 - (size & 3); #endif if ((p->Base = (Byte *)ISzAlloc_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 *z = (const UInt32 *)src; UInt32 n = num; \ do { d[0] = z[0]; d[1] = z[1]; d[2] = z[2]; z += 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[(size_t)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 = (Byte)((ctx->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40)); *ONE_STATE(ctx) = *s; FreeUnits(p, s, tmp); /* 9.31: the code was fixed. It's was not BUG, if Freq <= MAX_FREQ = 124 */ 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 = (Byte)((c->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40)); *ONE_STATE(c) = *s; SpecialFreeUnit(p, s); ONE_STATE(c)->Freq = (Byte)(((unsigned)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 = (Byte)(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 = (Byte)(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 = (Byte)(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 = (Byte)(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 *s2 = p->FoundState; p->FoundState = s; successor = CreateSuccessors(p, False, NULL, c); if (successor == NULL) SetSuccessor(s, 0); else SetSuccessor(s, REF(successor)); p->FoundState = s2; } 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 = (Byte)(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((size_t)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 *s2 = (CPpmd_State*)AllocUnits(p, 0); if (!s2) { RESTORE_MODEL(c, CTX(fSuccessor)); return; } *s2 = *ONE_STATE(c); c->Stats = REF(s2); if (s2->Freq < MAX_FREQ / 4 - 1) s2->Freq <<= 1; else s2->Freq = MAX_FREQ - 4; c->SummFreq = (UInt16)(s2->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 *s2 = STATS(c) + ns1 + 1; SetSuccessor(s2, successor); s2->Symbol = fSymbol; s2->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 = (Byte)((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[(size_t)(unsigned)p->NS2Indx[(size_t)(unsigned)p->MinContext->NumStats + 2] - 3] + (p->MinContext->SummFreq > 11 * ((unsigned)p->MinContext->NumStats + 1)) + 2 * (unsigned)(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 */ unarr-1.0.1/lzmasdk/Ppmd8.h000066400000000000000000000070341317727033500154620ustar00rootroot00000000000000/* Ppmd8.h -- PPMdI codec 2017-04-03 : 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, ISzAllocPtr alloc); void Ppmd8_Free(CPpmd8 *p, ISzAllocPtr 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[(size_t)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 unarr-1.0.1/lzmasdk/Ppmd8Dec.c000066400000000000000000000101561317727033500160700ustar00rootroot00000000000000/* Ppmd8Dec.c -- PPMdI Decoder 2017-04-03 : 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) | IByteIn_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) | IByteIn_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); } } unarr-1.0.1/lzmasdk/Precomp.h000066400000000000000000000002741317727033500160760ustar00rootroot00000000000000/* Precomp.h -- StdAfx 2013-11-12 : Igor Pavlov : Public domain */ #ifndef __7Z_PRECOMP_H #define __7Z_PRECOMP_H /* #include "Compiler.h" */ /* #include "7zTypes.h" */ #endif unarr-1.0.1/pkg-config.pc.cmake000066400000000000000000000005271317727033500163030ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ Name: @PROJECT_NAME@ Description: @PROJECT_DESCRIPTION@ Version: @PROJECT_VERSION@ Cflags: -I${includedir} Requires.private: @PROJECT_REQUIRES_PRIVATE@ Libs: -L${libdir} -l@PROJECT_NAME@ Libs.private: @PROJECT_LIBS_PRIVATE@ unarr-1.0.1/rar/000077500000000000000000000000001317727033500134345ustar00rootroot00000000000000unarr-1.0.1/rar/filter-rar.c000066400000000000000000000567611317727033500156660ustar00rootroot00000000000000/* 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); } unarr-1.0.1/rar/huffman-rar.c000066400000000000000000000105411317727033500160070ustar00rootroot00000000000000/* 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)); } unarr-1.0.1/rar/lzss.h000066400000000000000000000062341317727033500146050ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ /* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/LZSS.h */ #ifndef rar_lzss_h #define rar_lzss_h #include #include #include #include #if defined(_MSC_VER) && !defined(inline) #define inline __inline #endif typedef struct { uint8_t *window; int mask; int64_t position; } LZSS; static inline int64_t lzss_position(LZSS *self) { return self->position; } static inline int lzss_mask(LZSS *self) { return self->mask; } static inline int lzss_size(LZSS *self) { return self->mask + 1; } static inline uint8_t *lzss_window_pointer(LZSS *self) { return self->window; } static inline int lzss_offset_for_position(LZSS *self, int64_t pos) { return (int)(pos & self->mask); } static inline uint8_t *lzss_window_pointer_for_position(LZSS *self, int64_t pos) { return &self->window[lzss_offset_for_position(self, pos)]; } static inline int lzss_current_window_offset(LZSS *self) { return lzss_offset_for_position(self, self->position); } static inline uint8_t *lzss_current_window_pointer(LZSS *self) { return lzss_window_pointer_for_position(self, self->position); } static inline int64_t lzss_next_window_edge_after_position(LZSS *self, int64_t pos) { return (pos + lzss_size(self)) & ~(int64_t)lzss_mask(self); } static inline int64_t lzss_next_window_edge(LZSS *self) { return lzss_next_window_edge_after_position(self, self->position); } static inline uint8_t lzss_get_byte_from_window(LZSS *self, int64_t pos) { return *lzss_window_pointer_for_position(self, pos); } static inline void lzss_emit_literal(LZSS *self, uint8_t literal) { /* self->window[(self->position & self->mask)] = literal; */ *lzss_current_window_pointer(self) = literal; self->position++; } static inline void lzss_emit_match(LZSS *self, int offset, int length) { int windowoffs = lzss_current_window_offset(self); int i; for (i = 0; i < length; i++) { self->window[(windowoffs + i) & lzss_mask(self)] = self->window[(windowoffs + i - offset) & lzss_mask(self)]; } self->position += length; } static inline void lzss_copy_bytes_from_window(LZSS *self, uint8_t *buffer, int64_t startpos, int length) { int windowoffs = lzss_offset_for_position(self, startpos); int firstpart = lzss_size(self) - windowoffs; if (length <= firstpart) { /* Request fits inside window */ memcpy(buffer, &self->window[windowoffs], length); } else { /* Request wraps around window */ memcpy(buffer, &self->window[windowoffs], firstpart); memcpy(buffer + firstpart, &self->window[0], length - firstpart); } } static inline bool lzss_initialize(LZSS *self, int windowsize) { self->window = malloc(windowsize); if (!self->window) return false; self->mask = windowsize - 1; /* Assume windows are power-of-two sized! */ memset(self->window, 0, lzss_size(self)); self->position = 0; return true; } static inline void lzss_cleanup(LZSS *self) { free(self->window); } #endif unarr-1.0.1/rar/parse-rar.c000066400000000000000000000170401317727033500154760ustar00rootroot00000000000000/* 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 = (const uint8_t *)data + strlen(data) + 1; const uint8_t *end_in = (const 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; } unarr-1.0.1/rar/rar.c000066400000000000000000000204561317727033500143730ustar00rootroot00000000000000/* 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); } unarr-1.0.1/rar/rar.h000066400000000000000000000146571317727033500144060ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #ifndef rar_rar_h #define rar_rar_h #include "../common/unarr-imp.h" #include "lzss.h" #include "../lzmasdk/Ppmd7.h" #include static inline size_t smin(size_t a, size_t b) { return a < b ? a : b; } typedef struct ar_archive_rar_s ar_archive_rar; /***** parse-rar *****/ #define FILE_SIGNATURE_SIZE 7 enum block_types { TYPE_FILE_SIGNATURE = 0x72, TYPE_MAIN_HEADER = 0x73, TYPE_FILE_ENTRY = 0x74, TYPE_NEWSUB = 0x7A, TYPE_END_OF_ARCHIVE = 0x7B, }; enum archive_flags { MHD_VOLUME = 1 << 0, MHD_COMMENT = 1 << 1, MHD_LOCK = 1 << 2, MHD_SOLID = 1 << 3, MHD_PACK_COMMENT = 1 << 4, MHD_AV = 1 << 5, MHD_PROTECT = 1 << 6, MHD_PASSWORD = 1 << 7, MHD_FIRSTVOLUME = 1 << 8, MHD_ENCRYPTVER = 1 << 9, MHD_LONG_BLOCK = 1 << 15, }; enum entry_flags { LHD_SPLIT_BEFORE = 1 << 0, LHD_SPLIT_AFTER = 1 << 1, LHD_PASSWORD = 1 << 2, LHD_COMMENT = 1 << 3, LHD_SOLID = 1 << 4, LHD_DIRECTORY = (1 << 5) | (1 << 6) | (1 << 7), LHD_LARGE = 1 << 8, LHD_UNICODE = 1 << 9, LHD_SALT = 1 << 10, LHD_VERSION = 1 << 11, LHD_EXTTIME = 1 << 12, LHD_EXTFLAGS = 1 << 13, LHD_LONG_BLOCK = 1 << 15, }; enum compression_method { METHOD_STORE = 0x30, METHOD_FASTEST = 0x31, METHOD_FAST = 0x32, METHOD_NORMAL = 0x33, METHOD_GOOD = 0x34, METHOD_BEST = 0x35, }; struct rar_header { uint16_t crc; uint8_t type; uint16_t flags; uint16_t size; uint64_t datasize; }; struct rar_entry { uint64_t size; uint8_t os; uint32_t crc; uint32_t dosdate; uint8_t version; uint8_t method; uint16_t namelen; uint32_t attrs; }; struct ar_archive_rar_entry { uint8_t version; uint8_t method; uint32_t crc; uint16_t header_size; bool solid; char *name; }; bool rar_parse_header(ar_archive *ar, struct rar_header *header); bool rar_check_header_crc(ar_archive *ar); bool rar_parse_header_entry(ar_archive_rar *rar, struct rar_header *header, struct rar_entry *entry); const char *rar_get_name(ar_archive *ar); /***** filter-rar *****/ struct RARVirtualMachine; struct RARProgramCode; struct RARFilter; struct ar_archive_rar_filters { struct RARVirtualMachine *vm; struct RARProgramCode *progs; struct RARFilter *stack; size_t filterstart; uint32_t lastfilternum; size_t lastend; uint8_t *bytes; size_t bytes_ready; }; bool rar_parse_filter(ar_archive_rar *rar, const uint8_t *bytes, uint16_t length, uint8_t flags); bool rar_run_filters(ar_archive_rar *rar); void rar_clear_filters(struct ar_archive_rar_filters *filters); /***** huffman-rar *****/ struct huffman_code { struct { int branches[2]; } *tree; int numentries; int capacity; int minlength; int maxlength; struct { int length; int value; } *table; int tablesize; }; bool rar_new_node(struct huffman_code *code); bool rar_add_value(struct huffman_code *code, int value, int codebits, int length); bool rar_create_code(struct huffman_code *code, uint8_t *lengths, int numsymbols); bool rar_make_table(struct huffman_code *code); void rar_free_code(struct huffman_code *code); static inline bool rar_is_leaf_node(struct huffman_code *code, int node) { return code->tree[node].branches[0] == code->tree[node].branches[1]; } /***** uncompress-rar *****/ #define LZSS_WINDOW_SIZE 0x400000 #define LZSS_OVERFLOW_SIZE 288 #define MAINCODE_SIZE 299 #define OFFSETCODE_SIZE 60 #define LOWOFFSETCODE_SIZE 17 #define LENGTHCODE_SIZE 28 #define HUFFMAN_TABLE_SIZE MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE + LENGTHCODE_SIZE struct ByteReader { IByteIn super; ar_archive_rar *rar; }; struct CPpmdRAR_RangeDec { IPpmd7_RangeDec super; UInt32 Range; UInt32 Code; UInt32 Low; IByteIn *Stream; }; struct ar_archive_rar_uncomp_v3 { struct huffman_code maincode; struct huffman_code offsetcode; struct huffman_code lowoffsetcode; struct huffman_code lengthcode; uint8_t lengthtable[HUFFMAN_TABLE_SIZE]; uint32_t lastlength; uint32_t lastoffset; uint32_t oldoffset[4]; uint32_t lastlowoffset; uint32_t numlowoffsetrepeats; bool is_ppmd_block; int ppmd_escape; CPpmd7 ppmd7_context; struct CPpmdRAR_RangeDec range_dec; struct ByteReader bytein; struct ar_archive_rar_filters filters; }; #define MAINCODE_SIZE_20 298 #define OFFSETCODE_SIZE_20 48 #define LENGTHCODE_SIZE_20 28 #define HUFFMAN_TABLE_SIZE_20 4 * 257 struct AudioState { int8_t weight[5]; int16_t delta[4]; int8_t lastdelta; int error[11]; int count; uint8_t lastbyte; }; struct ar_archive_rar_uncomp_v2 { struct huffman_code maincode; struct huffman_code offsetcode; struct huffman_code lengthcode; struct huffman_code audiocode[4]; uint8_t lengthtable[HUFFMAN_TABLE_SIZE_20]; uint32_t lastoffset; uint32_t lastlength; uint32_t oldoffset[4]; uint32_t oldoffsetindex; bool audioblock; uint8_t channel; uint8_t numchannels; struct AudioState audiostate[4]; int8_t channeldelta; }; struct ar_archive_rar_uncomp { uint8_t version; LZSS lzss; size_t bytes_ready; bool start_new_table; union { struct ar_archive_rar_uncomp_v3 v3; struct ar_archive_rar_uncomp_v2 v2; } state; struct StreamBitReader { uint64_t bits; int available; bool at_eof; } br; }; bool rar_uncompress_part(ar_archive_rar *rar, void *buffer, size_t buffer_size); int64_t rar_expand(ar_archive_rar *rar, int64_t end); void rar_clear_uncompress(struct ar_archive_rar_uncomp *uncomp); static inline void br_clear_leftover_bits(struct ar_archive_rar_uncomp *uncomp) { uncomp->br.available &= ~0x07; } /***** rar *****/ struct ar_archive_rar_progress { size_t data_left; size_t bytes_done; uint32_t crc; }; struct ar_archive_rar_solid { size_t size_total; bool part_done; bool restart; }; struct ar_archive_rar_s { ar_archive super; uint16_t archive_flags; struct ar_archive_rar_entry entry; struct ar_archive_rar_uncomp uncomp; struct ar_archive_rar_progress progress; struct ar_archive_rar_solid solid; }; #endif unarr-1.0.1/rar/rarvm.c000066400000000000000000000600261317727033500147330ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ /* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/RARVirtualMachine.c */ #include "rarvm.h" #include "../common/allocator.h" #include #include typedef struct RAROpcode_s RAROpcode; struct RAROpcode_s { uint8_t instruction; uint8_t bytemode; uint8_t addressingmode1; uint8_t addressingmode2; uint32_t value1; uint32_t value2; }; struct RARProgram_s { RAROpcode *opcodes; uint32_t length; uint32_t capacity; }; /* Program building */ RARProgram *RARCreateProgram(void) { return calloc(1, sizeof(RARProgram)); } void RARDeleteProgram(RARProgram *prog) { if (prog) free(prog->opcodes); free(prog); } bool RARProgramAddInstr(RARProgram *prog, uint8_t instruction, bool bytemode) { if (instruction >= RARNumberOfInstructions) return false; if (bytemode && !RARInstructionHasByteMode(instruction)) return false; if (prog->length + 1 >= prog->capacity) { /* in my small file sample, 16 is the value needed most often */ uint32_t newCapacity = prog->capacity ? prog->capacity * 4 : 32; RAROpcode *newCodes = calloc(newCapacity, sizeof(*prog->opcodes)); if (!newCodes) return false; memcpy(newCodes, prog->opcodes, prog->capacity * sizeof(*prog->opcodes)); free(prog->opcodes); prog->opcodes = newCodes; prog->capacity = newCapacity; } memset(&prog->opcodes[prog->length], 0, sizeof(prog->opcodes[prog->length])); prog->opcodes[prog->length].instruction = instruction; if (instruction == RARMovzxInstruction || instruction == RARMovsxInstruction) prog->opcodes[prog->length].bytemode = 2; /* second argument only */ else if (bytemode) prog->opcodes[prog->length].bytemode = (1 | 2); else prog->opcodes[prog->length].bytemode = 0; prog->length++; return true; } bool RARSetLastInstrOperands(RARProgram *prog, uint8_t addressingmode1, uint32_t value1, uint8_t addressingmode2, uint32_t value2) { RAROpcode *opcode = &prog->opcodes[prog->length - 1]; int numoperands; if (addressingmode1 >= RARNumberOfAddressingModes || addressingmode2 >= RARNumberOfAddressingModes) return false; if (!prog->length || opcode->addressingmode1 || opcode->value1 || opcode->addressingmode2 || opcode->value2) return false; numoperands = NumberOfRARInstructionOperands(opcode->instruction); if (numoperands == 0) return true; if (addressingmode1 == RARImmediateAddressingMode && RARInstructionWritesFirstOperand(opcode->instruction)) return false; opcode->addressingmode1 = addressingmode1; opcode->value1 = value1; if (numoperands == 2) { if (addressingmode2 == RARImmediateAddressingMode && RARInstructionWritesSecondOperand(opcode->instruction)) return false; opcode->addressingmode2 = addressingmode2; opcode->value2 = value2; } return true; } bool RARIsProgramTerminated(RARProgram *prog) { return prog->length > 0 && RARInstructionIsUnconditionalJump(prog->opcodes[prog->length - 1].instruction); } /* Execution */ #define EXTMACRO_BEGIN do { #ifdef _MSC_VER #define EXTMACRO_END } __pragma(warning(push)) __pragma(warning(disable:4127)) while (0) __pragma(warning(pop)) #else #define EXTMACRO_END } while (0) #endif #define CarryFlag 1 #define ZeroFlag 2 #define SignFlag 0x80000000 #define SignExtend(a) ((uint32_t)((int8_t)(a))) static uint32_t _RARGetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode); static void _RARSetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode, uint32_t data); #define GetOperand1() _RARGetOperand(vm, opcode->addressingmode1, opcode->value1, opcode->bytemode & 1) #define GetOperand2() _RARGetOperand(vm, opcode->addressingmode2, opcode->value2, opcode->bytemode & 2) #define SetOperand1(data) _RARSetOperand(vm, opcode->addressingmode1, opcode->value1, opcode->bytemode & 1, data) #define SetOperand2(data) _RARSetOperand(vm, opcode->addressingmode2, opcode->value2, opcode->bytemode & 2, data) #define SetFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint32_t result = (res); flags = (result == 0 ? ZeroFlag : (result & SignFlag)) | ((carry) ? CarryFlag : 0); EXTMACRO_END #define SetByteFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint8_t result = (res); flags = (result == 0 ? ZeroFlag : (SignExtend(result) & SignFlag)) | ((carry) ? CarryFlag : 0); EXTMACRO_END #define SetFlags(res) SetFlagsWithCarry(res, 0) #define SetOperand1AndFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint32_t r = (res); SetFlagsWithCarry(r, carry); SetOperand1(r); EXTMACRO_END #define SetOperand1AndByteFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint8_t r = (res); SetByteFlagsWithCarry(r, carry); SetOperand1(r); EXTMACRO_END #define SetOperand1AndFlags(res) EXTMACRO_BEGIN uint32_t r = (res); SetFlags(r); SetOperand1(r); EXTMACRO_END #define NextInstruction() { opcode++; continue; } #define Jump(offs) { uint32_t o = (offs); if (o >= prog->length) return false; opcode = &prog->opcodes[o]; continue; } bool RARExecuteProgram(RARVirtualMachine *vm, RARProgram *prog) { RAROpcode *opcode = prog->opcodes; uint32_t flags = 0; uint32_t op1, op2, carry, i; uint32_t counter = 0; if (!RARIsProgramTerminated(prog)) return false; while ((uint32_t)(opcode - prog->opcodes) < prog->length && counter++ < RARRuntimeMaxInstructions) { switch (opcode->instruction) { case RARMovInstruction: SetOperand1(GetOperand2()); NextInstruction(); case RARCmpInstruction: op1 = GetOperand1(); SetFlagsWithCarry(op1 - GetOperand2(), result > op1); NextInstruction(); case RARAddInstruction: op1 = GetOperand1(); if (opcode->bytemode) SetOperand1AndByteFlagsWithCarry((op1 + GetOperand2()) & 0xFF, result < op1); else SetOperand1AndFlagsWithCarry(op1 + GetOperand2(), result < op1); NextInstruction(); case RARSubInstruction: op1 = GetOperand1(); #if 0 /* apparently not correctly implemented in the RAR VM */ if (opcode->bytemode) SetOperand1AndByteFlagsWithCarry((op1 - GetOperand2()) & 0xFF, result > op1); else #endif SetOperand1AndFlagsWithCarry(op1 - GetOperand2(), result > op1); NextInstruction(); case RARJzInstruction: if ((flags & ZeroFlag)) Jump(GetOperand1()); NextInstruction(); case RARJnzInstruction: if (!(flags & ZeroFlag)) Jump(GetOperand1()); NextInstruction(); case RARIncInstruction: if (opcode->bytemode) SetOperand1AndFlags((GetOperand1() + 1) & 0xFF); else SetOperand1AndFlags(GetOperand1() + 1); NextInstruction(); case RARDecInstruction: if (opcode->bytemode) SetOperand1AndFlags((GetOperand1() - 1) & 0xFF); else SetOperand1AndFlags(GetOperand1() - 1); NextInstruction(); case RARJmpInstruction: Jump(GetOperand1()); case RARXorInstruction: SetOperand1AndFlags(GetOperand1() ^ GetOperand2()); NextInstruction(); case RARAndInstruction: SetOperand1AndFlags(GetOperand1() & GetOperand2()); NextInstruction(); case RAROrInstruction: SetOperand1AndFlags(GetOperand1() | GetOperand2()); NextInstruction(); case RARTestInstruction: SetFlags(GetOperand1() & GetOperand2()); NextInstruction(); case RARJsInstruction: if ((flags & SignFlag)) Jump(GetOperand1()); NextInstruction(); case RARJnsInstruction: if (!(flags & SignFlag)) Jump(GetOperand1()); NextInstruction(); case RARJbInstruction: if ((flags & CarryFlag)) Jump(GetOperand1()); NextInstruction(); case RARJbeInstruction: if ((flags & (CarryFlag | ZeroFlag))) Jump(GetOperand1()); NextInstruction(); case RARJaInstruction: if (!(flags & (CarryFlag | ZeroFlag))) Jump(GetOperand1()); NextInstruction(); case RARJaeInstruction: if (!(flags & CarryFlag)) Jump(GetOperand1()); NextInstruction(); case RARPushInstruction: vm->registers[7] -= 4; RARVirtualMachineWrite32(vm, vm->registers[7], GetOperand1()); NextInstruction(); case RARPopInstruction: SetOperand1(RARVirtualMachineRead32(vm, vm->registers[7])); vm->registers[7] += 4; NextInstruction(); case RARCallInstruction: vm->registers[7] -= 4; RARVirtualMachineWrite32(vm, vm->registers[7], (uint32_t)(opcode - prog->opcodes + 1)); Jump(GetOperand1()); case RARRetInstruction: if (vm->registers[7] >= RARProgramMemorySize) return true; i = RARVirtualMachineRead32(vm, vm->registers[7]); vm->registers[7] += 4; Jump(i); case RARNotInstruction: SetOperand1(~GetOperand1()); NextInstruction(); case RARShlInstruction: op1 = GetOperand1(); op2 = GetOperand2(); SetOperand1AndFlagsWithCarry(op1 << op2, ((op1 << (op2 - 1)) & 0x80000000) != 0); NextInstruction(); case RARShrInstruction: op1 = GetOperand1(); op2 = GetOperand2(); SetOperand1AndFlagsWithCarry(op1 >> op2, ((op1 >> (op2 - 1)) & 1) != 0); NextInstruction(); case RARSarInstruction: op1 = GetOperand1(); op2 = GetOperand2(); SetOperand1AndFlagsWithCarry(((int32_t)op1) >> op2, ((op1 >> (op2 - 1)) & 1) != 0); NextInstruction(); case RARNegInstruction: SetOperand1AndFlagsWithCarry(-(int32_t)GetOperand1(), result != 0); NextInstruction(); case RARPushaInstruction: vm->registers[7] -= 32; for (i = 0; i < 8; i++) RARVirtualMachineWrite32(vm, vm->registers[7] + (7 - i) * 4, vm->registers[i]); NextInstruction(); case RARPopaInstruction: for (i = 0; i < 8; i++) vm->registers[i] = RARVirtualMachineRead32(vm, vm->registers[7] + (7 - i) * 4); vm->registers[7] += 32; NextInstruction(); case RARPushfInstruction: vm->registers[7] -= 4; RARVirtualMachineWrite32(vm, vm->registers[7], flags); NextInstruction(); case RARPopfInstruction: flags = RARVirtualMachineRead32(vm, vm->registers[7]); vm->registers[7] += 4; NextInstruction(); case RARMovzxInstruction: SetOperand1(GetOperand2()); NextInstruction(); case RARMovsxInstruction: SetOperand1(SignExtend(GetOperand2())); NextInstruction(); case RARXchgInstruction: op1 = GetOperand1(); op2 = GetOperand2(); SetOperand1(op2); SetOperand2(op1); NextInstruction(); case RARMulInstruction: SetOperand1(GetOperand1() * GetOperand2()); NextInstruction(); case RARDivInstruction: op2 = GetOperand2(); if (op2 != 0) SetOperand1(GetOperand1() / op2); NextInstruction(); case RARAdcInstruction: op1 = GetOperand1(); carry = (flags & CarryFlag); if (opcode->bytemode) SetOperand1AndFlagsWithCarry((op1 + GetOperand2() + carry) & 0xFF, result < op1 || (result == op1 && carry)); /* does not correctly set sign bit */ else SetOperand1AndFlagsWithCarry(op1 + GetOperand2() + carry, result < op1 || (result == op1 && carry)); NextInstruction(); case RARSbbInstruction: op1 = GetOperand1(); carry = (flags & CarryFlag); if (opcode->bytemode) SetOperand1AndFlagsWithCarry((op1 - GetOperand2() - carry) & 0xFF, result > op1 || (result == op1 && carry)); /* does not correctly set sign bit */ else SetOperand1AndFlagsWithCarry(op1 - GetOperand2() - carry, result > op1 || (result == op1 && carry)); NextInstruction(); case RARPrintInstruction: /* TODO: ??? */ NextInstruction(); } } return false; } /* Memory and register access */ static uint32_t _RARRead32(const uint8_t *b) { return ((uint32_t)b[3] << 24) | ((uint32_t)b[2] << 16) | ((uint32_t)b[1] << 8) | (uint32_t)b[0]; } static void _RARWrite32(uint8_t *b, uint32_t n) { b[3] = (n >> 24) & 0xFF; b[2] = (n >> 16) & 0xFF; b[1] = (n >> 8) & 0xFF; b[0] = n & 0xFF; } void RARSetVirtualMachineRegisters(RARVirtualMachine *vm, uint32_t registers[8]) { if (registers) memcpy(vm->registers, registers, sizeof(vm->registers)); else memset(vm->registers, 0, sizeof(vm->registers)); } uint32_t RARVirtualMachineRead32(RARVirtualMachine *vm, uint32_t address) { return _RARRead32(&vm->memory[address & RARProgramMemoryMask]); } void RARVirtualMachineWrite32(RARVirtualMachine *vm, uint32_t address, uint32_t val) { _RARWrite32(&vm->memory[address & RARProgramMemoryMask], val); } uint8_t RARVirtualMachineRead8(RARVirtualMachine *vm, uint32_t address) { return vm->memory[address & RARProgramMemoryMask]; } void RARVirtualMachineWrite8(RARVirtualMachine *vm, uint32_t address, uint8_t val) { vm->memory[address & RARProgramMemoryMask] = val; } static uint32_t _RARGetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode) { if (/*RARRegisterAddressingMode(0) <= addressingmode && */addressingmode <= RARRegisterAddressingMode(7)) { uint32_t result = vm->registers[addressingmode % 8]; if (bytemode) result = result & 0xFF; return result; } if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7)) { if (bytemode) return RARVirtualMachineRead8(vm, vm->registers[addressingmode % 8]); return RARVirtualMachineRead32(vm, vm->registers[addressingmode % 8]); } if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7)) { if (bytemode) return RARVirtualMachineRead8(vm, value + vm->registers[addressingmode % 8]); return RARVirtualMachineRead32(vm, value + vm->registers[addressingmode % 8]); } if (addressingmode == RARAbsoluteAddressingMode) { if (bytemode) return RARVirtualMachineRead8(vm, value); return RARVirtualMachineRead32(vm, value); } /* if (addressingmode == RARImmediateAddressingMode) */ return value; } static void _RARSetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode, uint32_t data) { if (/*RARRegisterAddressingMode(0) <= addressingmode &&*/ addressingmode <= RARRegisterAddressingMode(7)) { if (bytemode) data = data & 0xFF; vm->registers[addressingmode % 8] = data; } else if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7)) { if (bytemode) RARVirtualMachineWrite8(vm, vm->registers[addressingmode % 8], (uint8_t)data); else RARVirtualMachineWrite32(vm, vm->registers[addressingmode % 8], data); } else if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7)) { if (bytemode) RARVirtualMachineWrite8(vm, value + vm->registers[addressingmode % 8], (uint8_t)data); else RARVirtualMachineWrite32(vm, value + vm->registers[addressingmode % 8], data); } else if (addressingmode == RARAbsoluteAddressingMode) { if (bytemode) RARVirtualMachineWrite8(vm, value, (uint8_t)data); else RARVirtualMachineWrite32(vm, value, data); } } /* Instruction properties */ #define RAR0OperandsFlag 0 #define RAR1OperandFlag 1 #define RAR2OperandsFlag 2 #define RAROperandsFlag 3 #define RARHasByteModeFlag 4 #define RARIsUnconditionalJumpFlag 8 #define RARIsRelativeJumpFlag 16 #define RARWritesFirstOperandFlag 32 #define RARWritesSecondOperandFlag 64 #define RARReadsStatusFlag 128 #define RARWritesStatusFlag 256 static const int InstructionFlags[RARNumberOfInstructions] = { /*RARMovInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, /*RARCmpInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesStatusFlag, /*RARAddInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARSubInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARJzInstruction*/ RAR1OperandFlag | RARIsUnconditionalJumpFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJnzInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARIncInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARDecInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARJmpInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag, /*RARXorInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARAndInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RAROrInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARTestInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesStatusFlag, /*RARJsInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJnsInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJbInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJbeInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJaInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARJaeInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag, /*RARPushInstruction*/ RAR1OperandFlag, /*RARPopInstruction*/ RAR1OperandFlag, /*RARCallInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag, /*RARRetInstruction*/ RAR0OperandsFlag | RARIsUnconditionalJumpFlag, /*RARNotInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, /*RARShlInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARShrInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARSarInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARNegInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag, /*RARPushaInstruction*/ RAR0OperandsFlag, /*RARPopaInstruction*/ RAR0OperandsFlag, /*RARPushfInstruction*/ RAR0OperandsFlag | RARReadsStatusFlag, /*RARPopfInstruction*/ RAR0OperandsFlag | RARWritesStatusFlag, /*RARMovzxInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag, /*RARMovsxInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag, /*RARXchgInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag | RARWritesSecondOperandFlag | RARHasByteModeFlag, /*RARMulInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, /*RARDivInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag, /*RARAdcInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARReadsStatusFlag | RARWritesStatusFlag, /*RARSbbInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARReadsStatusFlag | RARWritesStatusFlag, /*RARPrintInstruction*/ RAR0OperandsFlag }; int NumberOfRARInstructionOperands(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return 0; return InstructionFlags[instruction] & RAROperandsFlag; } bool RARInstructionHasByteMode(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return false; return (InstructionFlags[instruction] & RARHasByteModeFlag)!=0; } bool RARInstructionIsUnconditionalJump(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return false; return (InstructionFlags[instruction] & RARIsUnconditionalJumpFlag) != 0; } bool RARInstructionIsRelativeJump(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return false; return (InstructionFlags[instruction] & RARIsRelativeJumpFlag) != 0; } bool RARInstructionWritesFirstOperand(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return false; return (InstructionFlags[instruction] & RARWritesFirstOperandFlag) != 0; } bool RARInstructionWritesSecondOperand(uint8_t instruction) { if (instruction >= RARNumberOfInstructions) return false; return (InstructionFlags[instruction] & RARWritesSecondOperandFlag) != 0; } /* Program debugging */ #ifndef NDEBUG #include static void RARPrintOperand(uint8_t addressingmode, uint32_t value) { if (/*RARRegisterAddressingMode(0) <= addressingmode && */addressingmode <= RARRegisterAddressingMode(7)) printf("r%d", addressingmode % 8); else if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7)) printf("@(r%d)", addressingmode % 8); else if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7)) printf("@(r%d+$%02x)", addressingmode % 8, value); else if (addressingmode == RARAbsoluteAddressingMode) printf("@($%02x)", value); else if (addressingmode == RARImmediateAddressingMode) printf("$%02x", value); } void RARPrintProgram(RARProgram *prog) { static const char *instructionNames[RARNumberOfInstructions] = { "Mov", "Cmp", "Add", "Sub", "Jz", "Jnz", "Inc", "Dec", "Jmp", "Xor", "And", "Or", "Test", "Js", "Jns", "Jb", "Jbe", "Ja", "Jae", "Push", "Pop", "Call", "Ret", "Not", "Shl", "Shr", "Sar", "Neg", "Pusha", "Popa", "Pushf", "Popf", "Movzx", "Movsx", "Xchg", "Mul", "Div", "Adc", "Sbb", "Print", }; uint32_t i; for (i = 0; i < prog->length; i++) { RAROpcode *opcode = &prog->opcodes[i]; int numoperands = NumberOfRARInstructionOperands(opcode->instruction); printf(" %02x: %s", i, instructionNames[opcode->instruction]); if (opcode->bytemode) printf("B"); if (numoperands >= 1) { printf(" "); RARPrintOperand(opcode->addressingmode1, opcode->value1); } if (numoperands == 2) { printf(", "); RARPrintOperand(opcode->addressingmode2, opcode->value2); } printf("\n"); } } #endif unarr-1.0.1/rar/rarvm.h000066400000000000000000000075071317727033500147450ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ /* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/RARVirtualMachine.h */ #ifndef rar_vm_h #define rar_vm_h #include #include #define RARProgramMemorySize 0x40000 #define RARProgramMemoryMask (RARProgramMemorySize - 1) #define RARProgramWorkSize 0x3c000 #define RARProgramGlobalSize 0x2000 #define RARProgramSystemGlobalAddress RARProgramWorkSize #define RARProgramSystemGlobalSize 64 #define RARProgramUserGlobalAddress (RARProgramSystemGlobalAddress + RARProgramSystemGlobalSize) #define RARProgramUserGlobalSize (RARProgramGlobalSize - RARProgramSystemGlobalSize) #define RARRuntimeMaxInstructions 250000000 #define RARRegisterAddressingMode(n) (0 + (n)) #define RARRegisterIndirectAddressingMode(n) (8 + (n)) #define RARIndexedAbsoluteAddressingMode(n) (16 + (n)) #define RARAbsoluteAddressingMode 24 #define RARImmediateAddressingMode 25 #define RARNumberOfAddressingModes 26 typedef struct RARVirtualMachine RARVirtualMachine; struct RARVirtualMachine { uint32_t registers[8]; uint8_t memory[RARProgramMemorySize + sizeof(uint32_t) /* overflow sentinel */]; }; typedef struct RARProgram_s RARProgram; /* Program building */ enum { RARMovInstruction = 0, RARCmpInstruction = 1, RARAddInstruction = 2, RARSubInstruction = 3, RARJzInstruction = 4, RARJnzInstruction = 5, RARIncInstruction = 6, RARDecInstruction = 7, RARJmpInstruction = 8, RARXorInstruction = 9, RARAndInstruction = 10, RAROrInstruction = 11, RARTestInstruction = 12, RARJsInstruction = 13, RARJnsInstruction = 14, RARJbInstruction = 15, RARJbeInstruction = 16, RARJaInstruction = 17, RARJaeInstruction = 18, RARPushInstruction = 19, RARPopInstruction = 20, RARCallInstruction = 21, RARRetInstruction = 22, RARNotInstruction = 23, RARShlInstruction = 24, RARShrInstruction = 25, RARSarInstruction = 26, RARNegInstruction = 27, RARPushaInstruction = 28, RARPopaInstruction = 29, RARPushfInstruction = 30, RARPopfInstruction = 31, RARMovzxInstruction = 32, RARMovsxInstruction = 33, RARXchgInstruction = 34, RARMulInstruction = 35, RARDivInstruction = 36, RARAdcInstruction = 37, RARSbbInstruction = 38, RARPrintInstruction = 39, RARNumberOfInstructions = 40, }; RARProgram *RARCreateProgram(void); 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 unarr-1.0.1/rar/uncompress-rar.c000066400000000000000000001115501317727033500165630ustar00rootroot00000000000000/* 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(ISzAllocPtr self, size_t size) { (void)self; return malloc(size); } static void gSzAlloc_Free(ISzAllocPtr 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(const IByteIn *p) { struct ByteReader *self = (struct ByteReader *) 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(const IPpmd7_RangeDec *p, UInt32 total) { struct CPpmdRAR_RangeDec *self = (struct CPpmdRAR_RangeDec *) p; return self->Code / (self->Range /= total); } static void Range_Decode_RAR(const IPpmd7_RangeDec *p, UInt32 start, UInt32 size) { struct CPpmdRAR_RangeDec *self = (struct CPpmdRAR_RangeDec *) 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(const IPpmd7_RangeDec *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; } 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, 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 < (int)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 < (int)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; } } unarr-1.0.1/tar/000077500000000000000000000000001317727033500134365ustar00rootroot00000000000000unarr-1.0.1/tar/parse-tar.c000066400000000000000000000177371317727033500155170ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #include "tar.h" static bool tar_is_number(const char *data, size_t size) { size_t i; for (i = 0; i < size; i++) { if ((data[i] < '0' || '7' < data[i]) && data[i] != ' ' && data[i] != '\0') return false; } return true; } static uint64_t tar_parse_number(const char *data, size_t size) { uint64_t value = 0; size_t i; for (i = 0; i < size; i++) { if (data[i] == ' ' || data[i] == '\0') continue; if (data[i] < '0' || '7' < data[i]) break; value = value * 8 + (data[i] - '0'); } return value; } static bool tar_is_zeroed_block(const char *data) { size_t i; for (i = 0; i < TAR_BLOCK_SIZE; i++) { if (data[i] != 0) return false; } return true; } static bool ar_is_valid_utf8(const char *string) { const unsigned char *s; for (s = (const unsigned char *)string; *s; s++) { int skip = *s < 0x80 ? 0 : *s < 0xC0 ? -1 : *s < 0xE0 ? 1 : *s < 0xF0 ? 2 : *s < 0xF5 ? 3 : -1; if (skip < 0) return false; while (skip-- > 0) { if ((*++s & 0xC0) != 0x80) return false; } } return true; } bool tar_parse_header(ar_archive_tar *tar) { char data[TAR_BLOCK_SIZE]; uint32_t checksum; int32_t checksum2; size_t i; if (ar_read(tar->super.stream, data, sizeof(data)) != sizeof(data)) return false; if (tar_is_zeroed_block(data)) { free(tar->entry.name); memset(&tar->entry, 0, sizeof(tar->entry)); return true; } if (!tar_is_number(data + 124, 12) || !tar_is_number(data + 136, 12) || !tar_is_number(data + 148, 8)) return false; tar->entry.filesize = (size_t)tar_parse_number(data + 124, 12); tar->entry.mtime = (tar_parse_number(data + 136, 12) + 11644473600) * 10000000; tar->entry.checksum = (uint32_t)tar_parse_number(data + 148, 8); tar->entry.filetype = data[156]; free(tar->entry.name); tar->entry.name = NULL; if (tar->entry.filetype == TYPE_FILE_OLD) { i = 100; while (--i > 0 && data[i] == '\0'); if (data[i] == '/') tar->entry.filetype = TYPE_DIRECTORY; } tar->entry.is_ustar = memcmp(data + 257, "ustar\x00""00", 8) == 0 && memcmp(data + 508, "tar\0", 4) != 0; if (tar->entry.filesize > (size_t)-1 - tar->super.entry_offset - 2 * TAR_BLOCK_SIZE) return false; checksum = 0; checksum2 = 0; memset(data + 148, ' ', 8); for (i = 0; i < sizeof(data); i++) { checksum += (unsigned char)data[i]; checksum2 += (signed char)data[i]; } if (checksum != (uint32_t)checksum2 && tar->entry.checksum == (uint32_t)checksum2) { log("Checksum was calculated using signed data"); tar->entry.checksum = checksum; } return tar->entry.checksum == checksum; } bool tar_handle_pax_extended(ar_archive *ar) { ar_archive_tar *tar = (ar_archive_tar *)ar; off64_t offset = ar->entry_offset; size_t size = tar->entry.filesize; char *data, *line; data = malloc(size); if (!data) { log("Ignoring PAX extended header on OOM"); return ar_parse_entry(ar); } if (!ar_entry_uncompress(ar, data, size) || !ar_parse_entry(ar)) { free(data); return false; } if (tar->last_seen_dir > offset) { free(data); return true; } line = data; while (line < data + size) { char *key, *value, *ptr; size_t length, max_size = line - data + size; ptr = memchr(line, '=', max_size); if (!ptr || *line < '1' || '9' < *line) { warn("Invalid PAX extended header record @%" PRIi64, offset); break; } value = ptr + 1; *ptr = '\0'; length = (size_t)strtoul(line, &ptr, 10); if (max_size < length || length <= (size_t)(value - line) || line[length - 1] != '\n' || *ptr != ' ') { warn("Invalid PAX extended header record @%" PRIi64, offset); break; } key = ptr + 1; line += length; line[-1] = '\0'; if (strcmp(key, "path") == 0) { ptr = malloc(strlen(value) + 1); if (ptr) { strcpy(ptr, value); free(tar->entry.name); tar->entry.name = ptr; } } else if (strcmp(key, "mtime") == 0) tar->entry.mtime = (time64_t)((strtod(value, &ptr) + 11644473600) * 10000000); else if (strcmp(key, "size") == 0) tar->entry.filesize = (size_t)strtoul(value, &ptr, 10); else log("Skipping value for %s", key); } free(data); tar_get_name(ar); ar->entry_offset = offset; ar->entry_size_uncompressed = tar->entry.filesize; ar->entry_filetime = tar->entry.mtime; return true; } bool tar_handle_gnu_longname(ar_archive *ar) { ar_archive_tar *tar = (ar_archive_tar *)ar; off64_t offset = ar->entry_offset; size_t size = tar->entry.filesize; char *longname; longname = malloc(size + 1); if (!longname || size == (size_t)-1) { log("Falling back to the short filename on OOM"); free(longname); return ar_parse_entry(ar); } if (!ar_entry_uncompress(ar, longname, size) || !ar_parse_entry(ar)) { free(longname); return false; } if (tar->last_seen_dir > offset) { free(longname); return true; } if (tar->entry.name) { log("Skipping GNU long filename in favor of PAX name"); free(longname); return true; } longname[size] = '\0'; ar->entry_offset = offset; /* name could be in any encoding, assume UTF-8 or whatever (DOS) */ if (ar_is_valid_utf8(longname)) { tar->entry.name = longname; } else { tar->entry.name = ar_conv_dos_to_utf8(longname); free(longname); } return true; } const char *tar_get_name(ar_archive *ar) { ar_archive_tar *tar = (ar_archive_tar *)ar; if (!tar->entry.name) { char *name; if (!ar_seek(ar->stream, ar->entry_offset, SEEK_SET)) return NULL; name = malloc(100 + 1); if (!name || ar_read(ar->stream, name, 100) != 100) { free(name); ar_seek(ar->stream, ar->entry_offset + TAR_BLOCK_SIZE, SEEK_SET); return NULL; } name[100] = '\0'; if (tar->entry.is_ustar) { char *prefixed = malloc(256 + 1); if (!prefixed || !ar_skip(ar->stream, 245) || ar_read(ar->stream, prefixed, 167) != 167) { free(name); free(prefixed); ar_seek(ar->stream, ar->entry_offset + TAR_BLOCK_SIZE, SEEK_SET); return NULL; } if (prefixed[0] != '\0') { prefixed[156] = '\0'; strcat(prefixed, "/"); strcat(prefixed, name); free(name); name = prefixed; prefixed = NULL; } free(prefixed); } else ar_skip(ar->stream, TAR_BLOCK_SIZE - 100); /* name could be in any encoding, assume UTF-8 or whatever (DOS) */ if (ar_is_valid_utf8(name)) { tar->entry.name = name; } else { tar->entry.name = ar_conv_dos_to_utf8(name); free(name); } /* normalize path separators */ if (tar->entry.name) { char *p = tar->entry.name; while ((p = strchr(p, '\\')) != NULL) { *p = '/'; } } } return tar->entry.name; } unarr-1.0.1/tar/tar.c000066400000000000000000000053741317727033500144010ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #include "tar.h" static void tar_close(ar_archive *ar) { ar_archive_tar *tar = (ar_archive_tar *)ar; free(tar->entry.name); } static bool tar_parse_entry(ar_archive *ar, off64_t offset) { ar_archive_tar *tar = (ar_archive_tar *)ar; if (!ar_seek(ar->stream, offset, SEEK_SET)) { warn("Couldn't seek to offset %" PRIi64, offset); return false; } if (!tar_parse_header(tar)) { warn("Invalid tar header data @%" PRIi64, offset); return false; } if (!tar->entry.checksum) { ar->at_eof = true; return false; } ar->entry_offset = offset; ar->entry_offset_next = offset + TAR_BLOCK_SIZE + (tar->entry.filesize + TAR_BLOCK_SIZE - 1) / TAR_BLOCK_SIZE * TAR_BLOCK_SIZE; ar->entry_size_uncompressed = tar->entry.filesize; ar->entry_filetime = tar->entry.mtime; tar->bytes_done = 0; if (tar->last_seen_dir > offset) tar->last_seen_dir = 0; switch (tar->entry.filetype) { case TYPE_FILE: case TYPE_FILE_OLD: return true; case TYPE_DIRECTORY: log("Skipping directory entry \"%s\"", tar_get_name(ar)); tar->last_seen_dir = ar->entry_offset; return tar_parse_entry(ar, ar->entry_offset_next); case TYPE_PAX_GLOBAL: log("Skipping PAX global extended header record"); return tar_parse_entry(ar, ar->entry_offset_next); case TYPE_PAX_EXTENDED: return tar_handle_pax_extended(ar); case TYPE_GNU_LONGNAME: return tar_handle_gnu_longname(ar); default: warn("Unknown entry type '%c'", tar->entry.filetype); return true; } } static bool tar_uncompress(ar_archive *ar, void *buffer, size_t count) { ar_archive_tar *tar = (ar_archive_tar *)ar; if (count > ar->entry_size_uncompressed - tar->bytes_done) { warn("Requesting too much data (%" PRIuPTR " < %" PRIuPTR ")", ar->entry_size_uncompressed - tar->bytes_done, count); return false; } if (ar_read(ar->stream, buffer, count) != count) { warn("Unexpected EOF in stored data"); return false; } tar->bytes_done += count; return true; } ar_archive *ar_open_tar_archive(ar_stream *stream) { ar_archive *ar; ar_archive_tar *tar; if (!ar_seek(stream, 0, SEEK_SET)) return NULL; ar = ar_open_archive(stream, sizeof(ar_archive_tar), tar_close, tar_parse_entry, tar_get_name, tar_uncompress, NULL, 0); if (!ar) return NULL; tar = (ar_archive_tar *)ar; if (!tar_parse_header(tar) || !tar->entry.checksum) { free(ar); return NULL; } return ar; } unarr-1.0.1/tar/tar.h000066400000000000000000000017541317727033500144040ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #ifndef tar_tar_h #define tar_tar_h #include "../common/unarr-imp.h" typedef struct ar_archive_tar_s ar_archive_tar; /***** parse-tar *****/ #define TAR_BLOCK_SIZE 512 enum tar_filetype { TYPE_FILE = '0', TYPE_FILE_OLD = '\0', TYPE_HARD_LINK = '1', TYPE_SOFT_LINK = '2', TYPE_DIRECTORY = '5', TYPE_GNU_LONGNAME = 'L', TYPE_PAX_GLOBAL = 'g', TYPE_PAX_EXTENDED = 'x', }; struct tar_entry { char *name; size_t filesize; time64_t mtime; uint32_t checksum; char filetype; bool is_ustar; }; bool tar_parse_header(ar_archive_tar *tar); bool tar_handle_pax_extended(ar_archive *ar); bool tar_handle_gnu_longname(ar_archive *ar); const char *tar_get_name(ar_archive *ar); /***** tar *****/ struct ar_archive_tar_s { ar_archive super; struct tar_entry entry; size_t bytes_done; off64_t last_seen_dir; }; #endif unarr-1.0.1/test/000077500000000000000000000000001317727033500136275ustar00rootroot00000000000000unarr-1.0.1/test/Makefile000066400000000000000000000027241317727033500152740ustar00rootroot00000000000000# GNU Makefile build ?= debug OUT := build/$(build) default: all # --- Configuration --- CFLAGS += -Wall -D_FILE_OFFSET_BITS=64 LIBS += -lm ifeq "$(build)" "debug" CFLAGS += -pipe -g -DDEBUG else ifeq "$(build)" "profile" CFLAGS += -pipe -O3 -DNDEBUG -pg LDFLAGS += -pg else ifeq "$(build)" "release" CFLAGS += -pipe -O3 -DNDEBUG -fomit-frame-pointer else ifeq "$(build)" "coverage" CFLAGS += -pipe -g -DDEBUG -pg -fprofile-arcs -ftest-coverage LIBS += -lgcov else $(error unknown build setting: '$(build)') endif # --- Commands --- ifneq "$(verbose)" "yes" QUIET_AR = @ echo ' ' ' ' AR $@ ; QUIET_CC = @ echo ' ' ' ' CC $@ ; QUIET_LINK = @ echo ' ' ' ' LINK $@ ; endif CC_CMD = $(QUIET_CC) mkdir -p $(@D) ; $(CC) $(CFLAGS) -o $@ -c $< AR_CMD = $(QUIET_AR) $(AR) cr $@ $^ LINK_CMD = $(QUIET_LINK) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) # --- Third party libraries --- # TODO: build zlib, bzip2 and 7z when available # --- unarr files --- UNARR_OUT := $(OUT)/unarr UNARR_DIRS := .. ../common ../lzmasdk ../rar ../tar ../zip ../_7z UNARR_SRC := $(wildcard $(UNARR_DIRS:=/*.c)) UNARR_OBJ := $(addprefix $(UNARR_OUT)/, $(addsuffix .o, $(basename $(UNARR_SRC)))) $(UNARR_OUT)/%.o : %.c $(CC_CMD) UNARR_LIB := $(OUT)/libunarr.a $(UNARR_LIB): $(UNARR_OBJ) $(AR_CMD) UNARR_TEST := $(OUT)/unarr-test $(UNARR_TEST) : $(UNARR_OUT)/main.o $(UNARR_LIB) $(LINK_CMD) # TODO: add header dependencies # --- Clean and Default --- all: $(UNARR_TEST) clean: rm -rf build .PHONY: all clean unarr-1.0.1/test/main.c000066400000000000000000000047451317727033500147310ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ /* demonstration of most of the public unarr API: parses and decompresses an archive into memory (integrity test) */ #include "unarr.h" #include #include #include #if !defined(NDEBUG) && defined(_MSC_VER) #include #include #endif ar_archive *ar_open_any_archive(ar_stream *stream, const char *fileext) { ar_archive *ar = ar_open_rar_archive(stream); if (!ar) ar = ar_open_zip_archive(stream, fileext && (strcmp(fileext, ".xps") == 0 || strcmp(fileext, ".epub") == 0)); if (!ar) ar = ar_open_7z_archive(stream); if (!ar) ar = ar_open_tar_archive(stream); return ar; } #define FailIf(cond, msg, ...) if (cond) { fprintf(stderr, msg "\n", __VA_ARGS__); goto CleanUp; } error_step++ int main(int argc, char *argv[]) { ar_stream *stream = NULL; ar_archive *ar = NULL; int entry_count = 1; int entry_skips = 0; int error_step = 1; #if !defined(NDEBUG) && defined(_MSC_VER) if (!IsDebuggerPresent()) { _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); } _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif FailIf(argc != 2, "Syntax: %s ", argv[0]); stream = ar_open_file(argv[1]); FailIf(!stream, "Error: File \"%s\" not found!", argv[1]); printf("Parsing \"%s\":\n", argv[1]); ar = ar_open_any_archive(stream, strrchr(argv[1], '.')); FailIf(!ar, "Error: No valid %s archive!", "RAR, ZIP, 7Z or TAR"); while (ar_parse_entry(ar)) { size_t size = ar_entry_get_size(ar); printf("%02d. %s (@%" PRIi64 ")\n", entry_count++, ar_entry_get_name(ar), ar_entry_get_offset(ar)); while (size > 0) { unsigned char buffer[1024]; size_t count = size < sizeof(buffer) ? size : sizeof(buffer); if (!ar_entry_uncompress(ar, buffer, count)) break; size -= count; } if (size > 0) { fprintf(stderr, "Warning: Failed to uncompress... skipping\n"); entry_skips++; } } FailIf(!ar_at_eof(ar), "Error: Failed to parse entry %d!", entry_count); error_step = entry_skips > 0 ? 1000 + entry_skips : 0; CleanUp: ar_close_archive(ar); ar_close(stream); return error_step; } unarr-1.0.1/unarr.h000066400000000000000000000126761317727033500141640ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #ifndef unarr_h #define unarr_h #ifdef __cplusplus extern "C" { #endif #include #include #include /* macros for shared library usage */ #if defined (UNARR_IS_SHARED_LIBRARY) #if defined (_WIN32) #if defined (UNARR_EXPORT_SYMBOLS) #define UNARR_EXPORT __declspec(dllexport) #else #define UNARR_EXPORT __declspec(dllimport) #endif // UNARR_EXPORT_SYMBOLS #else // _WIN32 #if defined (UNARR_EXPORT_SYMBOLS) #define UNARR_EXPORT __attribute__((visibility("default"))) #else #define UNARR_EXPORT #endif // UNARR_EXPORT_SYMBOLS #endif // _WIN32 #else // defined UNARR_IS_SHARED_LIBRARY #define UNARR_EXPORT #endif // UNARR_IS_SHARED_LIBRARY 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 */ UNARR_EXPORT ar_stream *ar_open_file(const char *path); #ifdef _WIN32 UNARR_EXPORT 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 */ UNARR_EXPORT 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 */ UNARR_EXPORT ar_stream *ar_open_istream(IStream *stream); #endif /* closes the stream and releases underlying resources */ UNARR_EXPORT 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 */ UNARR_EXPORT size_t ar_read(ar_stream *stream, void *buffer, size_t count); /* moves the read offset pointer (same as fseek); returns false on failure */ UNARR_EXPORT bool ar_seek(ar_stream *stream, off64_t offset, int origin); /* shortcut for ar_seek(stream, count, SEEK_CUR); returns false on failure */ UNARR_EXPORT bool ar_skip(ar_stream *stream, off64_t count); /* returns the current read offset (or 0 on error) */ UNARR_EXPORT 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 */ UNARR_EXPORT 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) */ UNARR_EXPORT 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 */ UNARR_EXPORT 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 */ UNARR_EXPORT 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 */ UNARR_EXPORT 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 */ UNARR_EXPORT const char *ar_entry_get_name(ar_archive *ar); /* returns the stream offset of the current entry for use with ar_parse_entry_at */ UNARR_EXPORT 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 */ UNARR_EXPORT size_t ar_entry_get_size(ar_archive *ar); /* returns the stored modification date of the current entry in 100ns since 1601/01/01 */ UNARR_EXPORT 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 */ UNARR_EXPORT 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) */ UNARR_EXPORT 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 */ UNARR_EXPORT 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 */ UNARR_EXPORT 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 */ UNARR_EXPORT 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 */ UNARR_EXPORT ar_archive *ar_open_7z_archive(ar_stream *stream); #ifdef __cplusplus } #endif #endif //unarr_h unarr-1.0.1/zip/000077500000000000000000000000001317727033500134525ustar00rootroot00000000000000unarr-1.0.1/zip/inflate.c000066400000000000000000000412061317727033500152430ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #include "inflate.h" #include "../common/allocator.h" #include #include #include #ifndef _MSC_VER #define __forceinline inline #endif #define MAX_BITS 16 #define TREE_FAST_BITS 10 #define MAX_TREE_NODES 288 enum inflate_step { STEP_NEXT_BLOCK = 0, STEP_COPY_INIT, STEP_COPY, STEP_INFLATE_STATIC_INIT, STEP_INFLATE_DYNAMIC_INIT, STEP_INFLATE_DYNAMIC_INIT_PRETREE, STEP_INFLATE_DYNAMIC_INIT_TREES, STEP_INFLATE_CODE, STEP_INFLATE, STEP_INFLATE_DISTANCE_CODE, STEP_INFLATE_DISTANCE, STEP_INFLATE_REPEAT, }; enum { RESULT_EOS = -1, RESULT_NOT_DONE = 0, RESULT_ERROR = 1 }; #if defined(_MSC_VER) || defined(__GNUC__) #define RESULT_ERROR (RESULT_ERROR + __COUNTER__) #endif struct tree { struct { unsigned value : 11; unsigned is_value : 1; unsigned length : 4; } nodes[(1 << TREE_FAST_BITS) + MAX_TREE_NODES * 2]; int next_node; }; struct inflate_state_s { enum inflate_step step; struct { int value; int length; int dist; int tree_idx; } state; struct { int hlit; int hdist; int hclen; int idx; int clens[288 + 32]; } prepare; bool inflate64; bool is_final_block; struct tree tree_lengths; struct tree tree_dists; struct { const uint8_t *data_in; size_t *avail_in; uint64_t bits; int available; } in; struct { uint8_t *data_out; size_t *avail_out; uint8_t window[1 << 16]; size_t offset; } out; }; static const struct { int bits; int length; } table_lengths[30] = { { 0, 3 }, { 0, 4 }, { 0, 5 }, { 0, 6 }, { 0, 7 }, { 0, 8 }, { 0, 9 }, { 0, 10 }, { 1, 11 }, { 1, 13 }, { 1, 15 }, { 1, 17 }, { 2, 19 }, { 2, 23 }, { 2, 27 }, { 2, 31 }, { 3, 35 }, { 3, 43 }, { 3, 51 }, { 3, 59 }, { 4, 67 }, { 4, 83 }, { 4, 99 }, { 4, 115 }, { 5, 131 }, { 5, 163 }, { 5, 195 }, { 5, 227 }, { 0, 258 }, /* Deflate64 (replaces { 0, 258 }) */ { 16, 3 } }; static const struct { int bits; int dist; } table_dists[32] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 1, 5 }, { 1, 7 }, { 2, 9 }, { 2, 13 }, { 3, 17 }, { 3, 25 }, { 4, 33 }, { 4, 49 }, { 5, 65 }, { 5, 97 }, { 6, 129 }, { 6, 193 }, { 7, 257 }, { 7, 385 }, { 8, 513 }, { 8, 769 }, { 9, 1025 }, { 9, 1537 }, { 10, 2049 }, { 10, 3073 }, { 11, 4097 }, { 11, 6145 }, { 12, 8193 }, { 12, 12289 }, { 13, 16385 }, { 13, 24577 }, /* Deflate64 */ { 14, 32769 }, { 14, 49153 } }; static const int table_code_length_idxs[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static __forceinline bool br_ensure(inflate_state *state, int bits) { while (state->in.available < bits) { if (*state->in.avail_in == 0) return false; state->in.bits |= ((uint64_t)*state->in.data_in++ << state->in.available); (*state->in.avail_in)--; state->in.available += 8; } return true; } static __forceinline uint64_t br_bits(inflate_state *state, int bits) { uint64_t res = state->in.bits & (((uint64_t)1 << bits) - 1); state->in.available -= bits; state->in.bits >>= bits; return res; } static __forceinline void output(inflate_state *state, uint8_t value) { *state->out.data_out++ = value; (*state->out.avail_out)--; state->out.window[state->out.offset++ & (sizeof(state->out.window) - 1)] = value; } static bool tree_add_value(struct tree *tree, int key, int bits, int value) { int rkey = 0, i; for (i = 0; i < bits; i++) rkey = (rkey << 1) | ((key >> i) & 1); if (bits <= TREE_FAST_BITS) { if (tree->nodes[rkey].length) return false; tree->nodes[rkey].length = bits; tree->nodes[rkey].value = value; tree->nodes[rkey].is_value = true; for (i = 1; i < (1 << (TREE_FAST_BITS - bits)); i++) { if (tree->nodes[rkey | (i << bits)].length) return false; tree->nodes[rkey | (i << bits)] = tree->nodes[rkey]; } return true; } rkey &= (1 << TREE_FAST_BITS) - 1; if (tree->nodes[rkey].is_value) return false; tree->nodes[rkey].length = TREE_FAST_BITS + 1; if (!tree->nodes[rkey].value) tree->nodes[rkey].value = (1 << TREE_FAST_BITS) + tree->next_node++ * 2; i = tree->nodes[rkey].value; bits -= TREE_FAST_BITS; while (bits > 1) { i |= (key >> (bits - 1)) & 1; if (tree->nodes[i].is_value) return false; if (!tree->nodes[i].value) { if (tree->next_node == MAX_TREE_NODES) return false; tree->nodes[i].value = (1 << TREE_FAST_BITS) + tree->next_node++ * 2; } i = tree->nodes[i].value; bits--; } i |= key & 1; if (tree->nodes[i].value || tree->nodes[i].is_value) return false; tree->nodes[i].value = value; tree->nodes[i].is_value = true; return true; } static __forceinline int tree_get_value(inflate_state *state, const struct tree *tree, bool not_fast) { if (state->state.tree_idx == 0) { int key = state->in.bits & ((1 << TREE_FAST_BITS) - 1); while (not_fast && state->in.available < TREE_FAST_BITS && state->in.available < (int)tree->nodes[key].length) { if (!br_ensure(state, tree->nodes[key].length)) return RESULT_NOT_DONE; key = state->in.bits & ((1 << TREE_FAST_BITS) - 1); } if (tree->nodes[key].is_value) { state->state.value = tree->nodes[key].value; (void)br_bits(state, tree->nodes[key].length); return RESULT_EOS; } if (tree->nodes[key].length == 0) return RESULT_ERROR; (void)br_bits(state, TREE_FAST_BITS); state->state.tree_idx = tree->nodes[key].value; } while (state->state.value == -1) { int idx; if (not_fast && !br_ensure(state, 1)) return RESULT_NOT_DONE; idx = state->state.tree_idx | (int)br_bits(state, 1); if (tree->nodes[idx].is_value) state->state.value = tree->nodes[idx].value; else if (tree->nodes[idx].value) state->state.tree_idx = tree->nodes[idx].value; else return RESULT_ERROR; } state->state.tree_idx = 0; return RESULT_EOS; } static void setup_static_trees(inflate_state *state) { int i; memset(&state->tree_lengths, 0, sizeof(state->tree_lengths)); for (i = 0; i < 144; i++) tree_add_value(&state->tree_lengths, i + 48, 8, i); for (i = 144; i < 256; i++) tree_add_value(&state->tree_lengths, i + 256, 9, i); for (i = 256; i < 280; i++) tree_add_value(&state->tree_lengths, i - 256, 7, i); for (i = 280; i < 288; i++) tree_add_value(&state->tree_lengths, i - 88, 8, i); memset(&state->tree_dists, 0, sizeof(state->tree_dists)); for (i = 0; i < 32; i++) tree_add_value(&state->tree_dists, i, 5, i); } static bool setup_dynamic_tree(struct tree *tree, int *clens, int count) { int code, i; int bl_count[MAX_BITS]; int next_code[MAX_BITS]; memset(bl_count, 0, sizeof(bl_count)); for (i = 0; i < count; i++) bl_count[clens[i]]++; bl_count[0] = 0; code = 0; for (i = 1; i < MAX_BITS; i++) { code = (code + bl_count[i - 1]) << 1; next_code[i] = code; } memset(tree, 0, sizeof(*tree)); for (i = 0; i < count; i++) { if (clens[i] != 0) { if (!tree_add_value(tree, next_code[clens[i]], clens[i], i)) return false; next_code[clens[i]]++; } } return true; } inflate_state *inflate_create(bool inflate64) { inflate_state *state = calloc(1, sizeof(inflate_state)); if (state) state->inflate64 = inflate64; return state; } void inflate_free(inflate_state *state) { free(state); } int inflate_process(inflate_state *state, const void *data_in, size_t *avail_in, void *data_out, size_t *avail_out) { bool not_fast = true; int res; if (!state || !data_in || !avail_in || !data_out || !avail_out) return RESULT_ERROR; state->in.data_in = data_in; state->in.avail_in = avail_in; state->out.data_out = data_out; state->out.avail_out = avail_out; for (;;) { switch (state->step) { case STEP_NEXT_BLOCK: if (state->is_final_block) return RESULT_EOS; if (!br_ensure(state, 3)) return RESULT_NOT_DONE; state->is_final_block = br_bits(state, 1) != 0; switch (br_bits(state, 2)) { case 0: state->step = STEP_COPY_INIT; break; case 1: state->step = STEP_INFLATE_STATIC_INIT; break; case 2: state->step = STEP_INFLATE_DYNAMIC_INIT; break; default: return RESULT_ERROR; } break; case STEP_COPY_INIT: if (!br_ensure(state, 32)) return RESULT_NOT_DONE; (void)br_bits(state, state->in.available & 0x7); state->state.length = (uint16_t)br_bits(state, 16); if (state->state.length != 0xFFFF - (uint16_t)br_bits(state, 16)) return RESULT_ERROR; state->step = STEP_COPY; /* fall through */ case STEP_COPY: while (state->state.length > 0) { if (!br_ensure(state, 8) || *avail_out == 0) return RESULT_NOT_DONE; output(state, (uint8_t)br_bits(state, 8)); state->state.length--; } state->step = STEP_NEXT_BLOCK; break; case STEP_INFLATE_STATIC_INIT: setup_static_trees(state); /* fall through */ STEP_INFLATE_START: not_fast = !br_ensure(state, state->inflate64 ? 49 : 48); state->state.value = -1; /* fall through */ case STEP_INFLATE_CODE: res = tree_get_value(state, &state->tree_lengths, not_fast); if (res != RESULT_EOS) { state->step = STEP_INFLATE_CODE; return res; } /* fall through */ case STEP_INFLATE: if (state->state.value < 256) { if (*avail_out == 0) { state->step = STEP_INFLATE; return RESULT_NOT_DONE; } output(state, (uint8_t)state->state.value); goto STEP_INFLATE_START; } if (state->state.value == 256) { state->step = STEP_NEXT_BLOCK; break; } if (state->state.value > 285) return RESULT_ERROR; if (state->inflate64 && state->state.value == 285) { not_fast = !br_ensure(state, 45); state->state.value = 286; } if (not_fast && !br_ensure(state, table_lengths[state->state.value - 257].bits)) { state->step = STEP_INFLATE; return RESULT_NOT_DONE; } state->state.length = table_lengths[state->state.value - 257].length + (int)br_bits(state, table_lengths[state->state.value - 257].bits); state->state.value = -1; /* fall through */ case STEP_INFLATE_DISTANCE_CODE: res = tree_get_value(state, &state->tree_dists, not_fast); if (res != RESULT_EOS) { state->step = STEP_INFLATE_DISTANCE_CODE; return res; } /* fall through */ case STEP_INFLATE_DISTANCE: if (not_fast && !br_ensure(state, table_dists[state->state.value].bits)) { state->step = STEP_INFLATE_DISTANCE; return RESULT_NOT_DONE; } state->state.dist = table_dists[state->state.value].dist + (int)br_bits(state, table_dists[state->state.value].bits); if ((size_t)state->state.dist > state->out.offset || (state->state.value > 30 && !state->inflate64)) return RESULT_ERROR; state->step = STEP_INFLATE_REPEAT; /* fall through */ case STEP_INFLATE_REPEAT: while (state->state.length > 0) { if (*avail_out == 0) return RESULT_NOT_DONE; output(state, state->out.window[(state->out.offset - state->state.dist) & (sizeof(state->out.window) - 1)]); state->state.length--; } goto STEP_INFLATE_START; case STEP_INFLATE_DYNAMIC_INIT: if (!br_ensure(state, 14)) return RESULT_NOT_DONE; state->prepare.hlit = (int)br_bits(state, 5) + 257; state->prepare.hdist = (int)br_bits(state, 5) + 1; state->prepare.hclen = (int)br_bits(state, 4) + 4; memset(state->prepare.clens, 0, sizeof(state->prepare.clens)); state->prepare.idx = 0; state->step = STEP_INFLATE_DYNAMIC_INIT_PRETREE; /* fall through */ case STEP_INFLATE_DYNAMIC_INIT_PRETREE: while (state->prepare.idx < state->prepare.hclen) { if (!br_ensure(state, 3)) return RESULT_NOT_DONE; state->prepare.clens[table_code_length_idxs[state->prepare.idx]] = (int)br_bits(state, 3); state->prepare.idx++; } if (!setup_dynamic_tree(&state->tree_lengths, state->prepare.clens, 19)) return RESULT_ERROR; memset(state->prepare.clens, 0, sizeof(state->prepare.clens)); state->prepare.idx = 0; state->state.value = -1; state->step = STEP_INFLATE_DYNAMIC_INIT_TREES; /* fall through */ case STEP_INFLATE_DYNAMIC_INIT_TREES: while (state->prepare.idx < state->prepare.hlit + state->prepare.hdist) { int value = 0, repeat = 0; if (state->state.value == -1) { res = tree_get_value(state, &state->tree_lengths, true); if (res != RESULT_EOS) return res; } if (state->state.value < 16) { state->prepare.clens[state->prepare.idx++] = state->state.value; } else if (state->state.value == 16) { if (state->prepare.idx == 0) return RESULT_ERROR; if (!br_ensure(state, 2)) return RESULT_NOT_DONE; value = state->prepare.clens[state->prepare.idx - 1]; repeat = (int)br_bits(state, 2) + 3; } else if (state->state.value == 17) { if (!br_ensure(state, 3)) return RESULT_NOT_DONE; value = 0; repeat = (int)br_bits(state, 3) + 3; } else { if (!br_ensure(state, 7)) return RESULT_NOT_DONE; value = 0; repeat = (int)br_bits(state, 7) + 11; } if (repeat) { if (state->prepare.idx + repeat > state->prepare.hlit + state->prepare.hdist) return RESULT_ERROR; while (repeat-- > 0) state->prepare.clens[state->prepare.idx++] = value; } state->state.value = -1; } if (!setup_dynamic_tree(&state->tree_lengths, state->prepare.clens, state->prepare.hlit)) return RESULT_ERROR; if (!setup_dynamic_tree(&state->tree_dists, state->prepare.clens + state->prepare.hlit, state->prepare.hdist)) return RESULT_ERROR; goto STEP_INFLATE_START; } } } int inflate_flush(inflate_state *state, unsigned char data_in[8]) { int count = 0; int keep = state->in.available & 0x7; while (count < state->in.available / 8) { data_in[count] = (state->in.bits >> (count * 8 + keep)) & 0xFF; count++; } state->in.available = keep; return count; } unarr-1.0.1/zip/inflate.h000066400000000000000000000012471317727033500152510ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #ifndef zip_inflate_h #define zip_inflate_h #include #include typedef struct inflate_state_s inflate_state; inflate_state *inflate_create(bool inflate64); /* updates avail_in and avail_out and returns -1 on EOF or any other non-zero value on error */ int inflate_process(inflate_state *state, const void *data_in, size_t *avail_in, void *data_out, size_t *avail_out); /* restores up to 8 bytes of data cached by inflate_process */ int inflate_flush(inflate_state *state, unsigned char data_in[8]); void inflate_free(inflate_state *state); #endif unarr-1.0.1/zip/parse-zip.c000066400000000000000000000255541317727033500155430ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #include "zip.h" #if defined(_MSC_VER) && !defined(inline) #define inline __inline #endif 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; } static inline uint64_t uint64le(unsigned char *data) { return (uint64_t)uint32le(data) | (uint64_t)uint32le(data + 4) << 32; } bool zip_seek_to_compressed_data(ar_archive_zip *zip) { struct zip_entry entry; if (!ar_seek(zip->super.stream, zip->entry.offset, SEEK_SET)) return false; if (!zip_parse_local_file_entry(zip, &entry)) return false; if (zip->entry.method != entry.method) { warn("Compression methods don't match: %d != %d", zip->entry.method, entry.method); if (!zip->entry.method) zip->entry.method = entry.method; } if (zip->entry.dosdate != entry.dosdate) { warn("Timestamps don't match"); if (!zip->entry.dosdate) { zip->entry.dosdate = entry.dosdate; zip->super.entry_filetime = ar_conv_dosdate_to_filetime(zip->entry.dosdate); } } return ar_seek(zip->super.stream, zip->entry.offset + ZIP_LOCAL_ENTRY_FIXED_SIZE + entry.namelen + entry.extralen, SEEK_SET); } static bool zip_parse_extra_fields(ar_archive_zip *zip, struct zip_entry *entry) { uint8_t *extra; uint32_t idx; if (!entry->extralen) return true; /* read ZIP64 values where needed */ if (!ar_skip(zip->super.stream, entry->namelen)) return false; extra = malloc(entry->extralen); if (!extra || ar_read(zip->super.stream, extra, entry->extralen) != entry->extralen) { free(extra); return false; } for (idx = 0; idx + 4 < entry->extralen; idx += 4 + uint16le(&extra[idx + 2])) { if (uint16le(&extra[idx]) == 0x0001) { uint16_t size = uint16le(&extra[idx + 2]); uint16_t offset = 0; if (entry->uncompressed == UINT32_MAX && offset + 8 <= size) { entry->uncompressed = uint64le(&extra[idx + 4 + offset]); offset += 8; } if (entry->datasize == UINT32_MAX && offset + 8 <= size) { entry->datasize = uint64le(&extra[idx + 4 + offset]); offset += 8; } if (entry->header_offset == UINT32_MAX && offset + 8 <= size) { entry->header_offset = (off64_t)uint64le(&extra[idx + 4 + offset]); offset += 8; } if (entry->disk == UINT16_MAX && offset + 4 <= size) { entry->disk = uint32le(&extra[idx + 4 + offset]); offset += 4; } break; } } free(extra); return true; } bool zip_parse_local_file_entry(ar_archive_zip *zip, struct zip_entry *entry) { uint8_t data[ZIP_LOCAL_ENTRY_FIXED_SIZE]; if (ar_read(zip->super.stream, data, sizeof(data)) != sizeof(data)) return false; memset(entry, 0, sizeof(*entry)); entry->signature = uint32le(data + 0); entry->version = uint16le(data + 4); entry->flags = uint16le(data + 6); entry->method = uint16le(data + 8); entry->dosdate = uint32le(data + 10); entry->crc = uint32le(data + 14); entry->datasize = uint32le(data + 18); entry->uncompressed = uint32le(data + 22); entry->namelen = uint16le(data + 26); entry->extralen = uint16le(data + 28); if (entry->signature != SIG_LOCAL_FILE_HEADER) return false; return zip_parse_extra_fields(zip, entry); } off64_t zip_find_next_local_file_entry(ar_stream *stream, off64_t offset) { uint8_t data[512]; int count, i; if (!ar_seek(stream, offset, SEEK_SET)) return -1; count = (int)ar_read(stream, data, sizeof(data)); while (count >= ZIP_LOCAL_ENTRY_FIXED_SIZE) { for (i = 0; i < count - 4; i++) { if (uint32le(data + i) == SIG_LOCAL_FILE_HEADER) return offset + i; } memmove(data, data + count - 4, count); offset += count - 4; count = (int)ar_read(stream, data + 4, sizeof(data) - 4) + 4; } return -1; } bool zip_parse_directory_entry(ar_archive_zip *zip, struct zip_entry *entry) { uint8_t data[ZIP_DIR_ENTRY_FIXED_SIZE]; if (ar_read(zip->super.stream, data, sizeof(data)) != sizeof(data)) return false; entry->signature = uint32le(data + 0); entry->version = uint16le(data + 4); entry->min_version = uint16le(data + 6); entry->flags = uint16le(data + 8); entry->method = uint16le(data + 10); entry->dosdate = uint32le(data + 12); entry->crc = uint32le(data + 16); entry->datasize = uint32le(data + 20); entry->uncompressed = uint32le(data + 24); entry->namelen = uint16le(data + 28); entry->extralen = uint16le(data + 30); entry->commentlen = uint16le(data + 32); entry->disk = uint16le(data + 34); entry->attr_internal = uint16le(data + 36); entry->attr_external = uint32le(data + 38); entry->header_offset = uint32le(data + 42); if (entry->signature != SIG_CENTRAL_DIRECTORY) return false; return zip_parse_extra_fields(zip, entry); } off64_t zip_find_end_of_last_directory_entry(ar_stream *stream, struct zip_eocd64 *eocd) { uint8_t data[ZIP_DIR_ENTRY_FIXED_SIZE]; uint64_t i; if (!ar_seek(stream, eocd->dir_offset, SEEK_SET)) return -1; for (i = 0; i < eocd->numentries; i++) { if (ar_read(stream, data, sizeof(data)) != sizeof(data)) return -1; if (uint32le(data + 0) != SIG_CENTRAL_DIRECTORY) return -1; if (!ar_skip(stream, uint16le(data + 28) + uint16le(data + 30) + uint16le(data + 32))) return -1; } return ar_tell(stream); } bool zip_parse_end_of_central_directory(ar_stream *stream, struct zip_eocd64 *eocd) { uint8_t data[56]; if (ar_read(stream, data, ZIP_END_OF_CENTRAL_DIR_SIZE) != ZIP_END_OF_CENTRAL_DIR_SIZE) return false; eocd->signature = uint32le(data + 0); eocd->diskno = uint16le(data + 4); eocd->diskno_dir = uint16le(data + 6); eocd->numentries_disk = uint16le(data + 8); eocd->numentries = uint16le(data + 10); eocd->dir_size = uint32le(data + 12); eocd->dir_offset = uint32le(data + 16); eocd->commentlen = uint16le(data + 20); if (eocd->signature != SIG_END_OF_CENTRAL_DIRECTORY) return false; /* try to locate the ZIP64 end of central directory */ if (!ar_skip(stream, -42)) return eocd->dir_size < 20; if (ar_read(stream, data, 20) != 20) return false; if (uint32le(data + 0) != SIG_END_OF_CENTRAL_DIRECTORY_64_LOCATOR) return true; if ((eocd->diskno != UINT16_MAX && uint32le(data + 4) != eocd->diskno) || uint32le(data + 16) != 1) { warn("Archive spanning isn't supported"); return false; } if (!ar_seek(stream, (off64_t)uint64le(data + 8), SEEK_SET)) return false; if (ar_read(stream, data, 56) != 56) return false; /* use data from ZIP64 end of central directory (when necessary) */ eocd->signature = uint32le(data + 0); eocd->version = uint16le(data + 12); eocd->min_version = uint16le(data + 14); if (eocd->diskno == UINT16_MAX) eocd->diskno = uint32le(data + 16); if (eocd->diskno_dir == UINT16_MAX) eocd->diskno_dir = uint32le(data + 20); if (eocd->numentries_disk == UINT16_MAX) eocd->numentries_disk = uint64le(data + 24); if (eocd->numentries == UINT16_MAX) eocd->numentries = uint64le(data + 32); if (eocd->dir_size == UINT32_MAX) eocd->dir_size = uint64le(data + 40); if (eocd->dir_offset == UINT32_MAX) eocd->dir_offset = (off64_t)uint64le(data + 48); if (eocd->signature != SIG_END_OF_CENTRAL_DIRECTORY_64) return false; if (eocd->diskno != eocd->diskno_dir || eocd->numentries != eocd->numentries_disk) { warn("Archive spanning isn't supported"); return false; } if (uint64le(data + 4) > 44) log("ZIP64 extensible data sector present @" PRIi64, ar_tell(stream)); return true; } off64_t zip_find_end_of_central_directory(ar_stream *stream) { uint8_t data[512]; off64_t filesize; int fromend = 0; int count, i; if (!ar_seek(stream, 0, SEEK_END)) return -1; filesize = ar_tell(stream); while (fromend < UINT16_MAX + ZIP_END_OF_CENTRAL_DIR_SIZE && fromend < filesize) { count = (filesize - fromend < (int)sizeof(data) ? (int)(filesize - fromend) : (int)sizeof(data)); fromend += count; if (count < ZIP_END_OF_CENTRAL_DIR_SIZE) return -1; if (!ar_seek(stream, -fromend, SEEK_END)) return -1; if (ar_read(stream, data, count) != (size_t)count) return -1; for (i = count - ZIP_END_OF_CENTRAL_DIR_SIZE; i >= 0; i--) { if (uint32le(data + i) == SIG_END_OF_CENTRAL_DIRECTORY) return filesize - fromend + i; } fromend -= ZIP_END_OF_CENTRAL_DIR_SIZE - 1; } return -1; } const char *zip_get_name(ar_archive *ar) { ar_archive_zip *zip = (ar_archive_zip *)ar; if (!zip->entry.name) { struct zip_entry entry; char *name; if (zip->dir.end_offset >= 0) { if (!ar_seek(ar->stream, ar->entry_offset, SEEK_SET)) return NULL; if (!zip_parse_directory_entry(zip, &entry)) return NULL; if (!ar_seek(ar->stream, ar->entry_offset + ZIP_DIR_ENTRY_FIXED_SIZE, SEEK_SET)) return NULL; } else { if (!ar_seek(ar->stream, zip->entry.offset, SEEK_SET)) return NULL; if (!zip_parse_local_file_entry(zip, &entry)) return NULL; if (!ar_seek(ar->stream, ar->entry_offset + ZIP_LOCAL_ENTRY_FIXED_SIZE, SEEK_SET)) return NULL; } name = malloc(entry.namelen + 1); if (!name || ar_read(ar->stream, name, entry.namelen) != entry.namelen) { free(name); return NULL; } name[entry.namelen] = '\0'; if ((entry.flags & (1 << 11))) { zip->entry.name = name; } else { zip->entry.name = ar_conv_dos_to_utf8(name); free(name); } /* normalize path separators */ if (zip->entry.name) { char *p = zip->entry.name; while ((p = strchr(p, '\\')) != NULL) { *p = '/'; } } } return zip->entry.name; } unarr-1.0.1/zip/uncompress-zip.c000066400000000000000000000457551317727033500166340ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #include "zip.h" #define ERR_UNCOMP UINT32_MAX static bool zip_fill_input_buffer(ar_archive_zip *zip) { struct ar_archive_zip_uncomp *uncomp = &zip->uncomp; size_t count; if (uncomp->input.offset) { memmove(&uncomp->input.data[0], &uncomp->input.data[uncomp->input.offset], uncomp->input.bytes_left); uncomp->input.offset = 0; } count = sizeof(uncomp->input.data) - uncomp->input.bytes_left; if (count > zip->progress.data_left) count = zip->progress.data_left; if (ar_read(zip->super.stream, &uncomp->input.data[uncomp->input.bytes_left], count) != count) { warn("Unexpected EOF during decompression (invalid data size?)"); return false; } zip->progress.data_left -= count; uncomp->input.bytes_left += (uint16_t)count; uncomp->input.at_eof = !zip->progress.data_left; return true; } /***** Deflate compression *****/ #ifdef HAVE_ZLIB static void *gZlib_Alloc(void *opaque, uInt count, uInt size) { (void)opaque; return calloc(count, size); } static void gZlib_Free(void *opaque, void *ptr) { (void)opaque; free(ptr); } static bool zip_init_uncompress_deflate(struct ar_archive_zip_uncomp *uncomp) { int err; uncomp->state.zstream.zalloc = gZlib_Alloc; uncomp->state.zstream.zfree = gZlib_Free; uncomp->state.zstream.opaque = NULL; err = inflateInit2(&uncomp->state.zstream, -15); return err == Z_OK; } static uint32_t zip_uncompress_data_deflate(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk) { int err; uncomp->state.zstream.next_in = &uncomp->input.data[uncomp->input.offset]; uncomp->state.zstream.avail_in = uncomp->input.bytes_left; uncomp->state.zstream.next_out = buffer; uncomp->state.zstream.avail_out = buffer_size; err = inflate(&uncomp->state.zstream, Z_SYNC_FLUSH); uncomp->input.offset += uncomp->input.bytes_left - (uint16_t)uncomp->state.zstream.avail_in; uncomp->input.bytes_left = (uint16_t)uncomp->state.zstream.avail_in; if (err != Z_OK && err != Z_STREAM_END) { warn("Unexpected ZLIB error %d", err); return ERR_UNCOMP; } if (err == Z_STREAM_END && (!is_last_chunk || uncomp->state.zstream.avail_out)) { warn("Premature EOS in Deflate stream"); return ERR_UNCOMP; } return buffer_size - uncomp->state.zstream.avail_out; } static void zip_clear_uncompress_deflate(struct ar_archive_zip_uncomp *uncomp) { inflateEnd(&uncomp->state.zstream); } #endif /***** Deflate(64) compression *****/ static bool zip_init_uncompress_deflate64(struct ar_archive_zip_uncomp *uncomp, bool deflate64) { uncomp->state.inflate = inflate_create(deflate64); return uncomp->state.inflate != NULL; } static uint32_t zip_uncompress_data_deflate64(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk) { size_t avail_in = uncomp->input.bytes_left; size_t avail_out = buffer_size; int result = inflate_process(uncomp->state.inflate, &uncomp->input.data[uncomp->input.offset], &avail_in, buffer, &avail_out); uncomp->input.offset += uncomp->input.bytes_left - (uint16_t)avail_in; uncomp->input.bytes_left = (uint16_t)avail_in; if (result && result != EOF) { warn("Unexpected Inflate error %d", result); return ERR_UNCOMP; } if (result == EOF && (!is_last_chunk || avail_out)) { warn("Premature EOS in Deflate stream"); return ERR_UNCOMP; } return buffer_size - (uint32_t)avail_out; } static void zip_clear_uncompress_deflate64(struct ar_archive_zip_uncomp *uncomp) { inflate_free(uncomp->state.inflate); } /***** BZIP2 compression *****/ #ifdef HAVE_BZIP2 static void *gBzip2_Alloc(void *opaque, int count, int size) { (void)opaque; return calloc(count, size); } static void gBzip2_Free(void *opaque, void *ptr) { (void)opaque; free(ptr); } static bool zip_init_uncompress_bzip2(struct ar_archive_zip_uncomp *uncomp) { int err; uncomp->state.bstream.bzalloc = gBzip2_Alloc; uncomp->state.bstream.bzfree = gBzip2_Free; uncomp->state.bstream.opaque = NULL; err = BZ2_bzDecompressInit(&uncomp->state.bstream, 0, 0); return err == BZ_OK; } static uint32_t zip_uncompress_data_bzip2(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk) { int err; uncomp->state.bstream.next_in = (char *)&uncomp->input.data[uncomp->input.offset]; uncomp->state.bstream.avail_in = uncomp->input.bytes_left; uncomp->state.bstream.next_out = (char *)buffer; uncomp->state.bstream.avail_out = buffer_size; err = BZ2_bzDecompress(&uncomp->state.bstream); uncomp->input.offset += uncomp->input.bytes_left - (uint16_t)uncomp->state.bstream.avail_in; uncomp->input.bytes_left = (uint16_t)uncomp->state.bstream.avail_in; if (err != BZ_OK && err != BZ_STREAM_END) { warn("Unexpected BZIP2 error %d", err); return ERR_UNCOMP; } if (err == BZ_STREAM_END && (!is_last_chunk || uncomp->state.bstream.avail_out)) { warn("Premature EOS in BZIP2 stream"); return ERR_UNCOMP; } return buffer_size - uncomp->state.bstream.avail_out; } static void zip_clear_uncompress_bzip2(struct ar_archive_zip_uncomp *uncomp) { BZ2_bzDecompressEnd(&uncomp->state.bstream); } #endif /***** LZMA compression *****/ #ifdef HAVE_LIBLZMA static void *gLzma_Alloc(void *opaque, size_t nmemb, size_t size) { (void)opaque; (void) nmemb; return malloc(size); } static void gLzma_Free(void *opaque, void *ptr) { (void)opaque; free(ptr); } static bool zip_init_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp) { lzma_stream strm = LZMA_STREAM_INIT; uncomp->state.lzmastream = strm; static const lzma_allocator allocator = { gLzma_Alloc, gLzma_Free, NULL }; uncomp->state.lzmastream.allocator = &allocator; return true; } static uint32_t zip_uncompress_data_lzma1(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk) { int err; if (uncomp->state.lzmastream.internal == NULL) { uint8_t propsize; propsize = uncomp->input.data[uncomp->input.offset + 2]; lzma_filter filters[2] = {{.id=LZMA_FILTER_LZMA1, .options=NULL}, {.id=LZMA_VLI_UNKNOWN, .options=NULL}}; err = lzma_properties_decode( &filters[0], NULL, &uncomp->input.data[uncomp->input.offset + 4], propsize); if (err != LZMA_OK) { warn("Properties error %d", err); return ERR_UNCOMP; } err = lzma_raw_decoder(&uncomp->state.lzmastream, filters); free(filters[0].options); if (err != LZMA_OK) { warn("Decoder init error %d", err); return ERR_UNCOMP; } uncomp->input.offset += 4 + propsize; uncomp->input.bytes_left -= 4 + propsize; } uncomp->state.lzmastream.next_in = &uncomp->input.data[uncomp->input.offset]; uncomp->state.lzmastream.avail_in = uncomp->input.bytes_left; uncomp->state.lzmastream.next_out = buffer; uncomp->state.lzmastream.avail_out = buffer_size; err = lzma_code(&uncomp->state.lzmastream, LZMA_RUN); uncomp->input.offset += (uint16_t)uncomp->input.bytes_left - (uint16_t)uncomp->state.lzmastream.avail_in; uncomp->input.bytes_left = (uint16_t)uncomp->state.lzmastream.avail_in; if (err != LZMA_OK && err != LZMA_STREAM_END) { warn("Unexpected LZMA error %d", err); warn("%d", buffer_size - uncomp->state.lzmastream.avail_out); return ERR_UNCOMP; } if (err == LZMA_STREAM_END && (!is_last_chunk || uncomp->state.lzmastream.avail_out)) { warn("Premature EOS in LZMA stream"); return ERR_UNCOMP; } return buffer_size - uncomp->state.lzmastream.avail_out; } static uint32_t zip_uncompress_data_xz(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk) { int err; if (uncomp->state.lzmastream.internal == NULL) { /* restrict decoder memory usage to 100 MB */ err = lzma_stream_decoder(&uncomp->state.lzmastream, 100 << 20, 0); if (err != LZMA_OK) { warn("Unexpected LZMA Decoder init error %d", err); return ERR_UNCOMP; } } uncomp->state.lzmastream.next_in = &uncomp->input.data[uncomp->input.offset]; uncomp->state.lzmastream.avail_in = uncomp->input.bytes_left; uncomp->state.lzmastream.next_out = buffer; uncomp->state.lzmastream.avail_out = buffer_size; err = lzma_code(&uncomp->state.lzmastream, LZMA_RUN); uncomp->input.offset += (uint16_t)uncomp->input.bytes_left - (uint16_t)uncomp->state.lzmastream.avail_in; uncomp->input.bytes_left = (uint16_t)uncomp->state.lzmastream.avail_in; if (err != LZMA_OK && err != LZMA_STREAM_END) { warn("Unexpected XZ error %d", err); warn("%d", buffer_size - uncomp->state.lzmastream.avail_out); return ERR_UNCOMP; } if (err == LZMA_STREAM_END && (!is_last_chunk || uncomp->state.lzmastream.avail_out)) { warn("Premature EOS in XZ stream"); return ERR_UNCOMP; } return buffer_size - uncomp->state.lzmastream.avail_out; } static void zip_clear_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp) { lzma_end(&uncomp->state.lzmastream); } #else //HAVE_LIBLZMA static void *gLzma_Alloc(ISzAllocPtr self, size_t size) { (void)self; return malloc(size); } static void gLzma_Free(ISzAllocPtr self, void *ptr) { (void)self; free(ptr); } static bool zip_init_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp, uint16_t flags) { uncomp->state.lzma.alloc.Alloc = gLzma_Alloc; uncomp->state.lzma.alloc.Free = gLzma_Free; uncomp->state.lzma.finish = (flags & (1 << 1)) ? LZMA_FINISH_END : LZMA_FINISH_ANY; LzmaDec_Construct(&uncomp->state.lzma.dec); return true; } static uint32_t zip_uncompress_data_lzma(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk) { SizeT srclen, dstlen; ELzmaStatus status; ELzmaFinishMode finish; SRes res; if (!uncomp->state.lzma.dec.dic) { uint8_t propsize; if (uncomp->input.bytes_left < 9) { warn("Insufficient data in compressed stream"); return ERR_UNCOMP; } propsize = uncomp->input.data[uncomp->input.offset + 2]; if (uncomp->input.data[uncomp->input.offset + 3] != 0 || uncomp->input.bytes_left < 4 + propsize) { warn("Insufficient data in compressed stream"); return ERR_UNCOMP; } res = LzmaDec_Allocate(&uncomp->state.lzma.dec, &uncomp->input.data[uncomp->input.offset + 4], propsize, &uncomp->state.lzma.alloc); uncomp->input.offset += 4 + propsize; uncomp->input.bytes_left -= 4 + propsize; if (res != SZ_OK) return ERR_UNCOMP; LzmaDec_Init(&uncomp->state.lzma.dec); } srclen = uncomp->input.bytes_left; dstlen = buffer_size; finish = uncomp->input.at_eof && is_last_chunk ? uncomp->state.lzma.finish : LZMA_FINISH_ANY; res = LzmaDec_DecodeToBuf(&uncomp->state.lzma.dec, buffer, &dstlen, &uncomp->input.data[uncomp->input.offset], &srclen, finish, &status); uncomp->input.offset += (uint16_t)srclen; uncomp->input.bytes_left -= (uint16_t)srclen; if (res != SZ_OK || (srclen == 0 && dstlen == 0)) { warn("Unexpected LZMA error %d", res); return ERR_UNCOMP; } if (status == LZMA_STATUS_FINISHED_WITH_MARK && (!is_last_chunk || dstlen != buffer_size)) { warn("Premature EOS in LZMA stream"); return ERR_UNCOMP; } return (uint32_t)dstlen; } static void zip_clear_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp) { LzmaDec_Free(&uncomp->state.lzma.dec, &uncomp->state.lzma.alloc); } #endif //HAVE_LIBLZMA /***** PPMd compression *****/ static void *gPpmd_Alloc(ISzAllocPtr self, size_t size) { (void)self; return malloc(size); } static void gPpmd_Free(ISzAllocPtr self, void *ptr) { (void)self; free(ptr); } static Byte gPpmd_ByteIn_Read(const IByteIn *p) { struct ByteReader *self = (struct ByteReader *) p; if (!self->input->bytes_left && (!self->zip->progress.data_left || !zip_fill_input_buffer(self->zip))) return 0xFF; self->input->bytes_left--; return self->input->data[self->input->offset++]; } static bool zip_init_uncompress_ppmd(ar_archive_zip *zip) { struct ar_archive_zip_uncomp *uncomp = &zip->uncomp; uncomp->state.ppmd8.alloc.Alloc = gPpmd_Alloc; uncomp->state.ppmd8.alloc.Free = gPpmd_Free; uncomp->state.ppmd8.bytein.super.Read = gPpmd_ByteIn_Read; uncomp->state.ppmd8.bytein.input = &uncomp->input; uncomp->state.ppmd8.bytein.zip = zip; uncomp->state.ppmd8.ctx.Stream.In = &uncomp->state.ppmd8.bytein.super; Ppmd8_Construct(&uncomp->state.ppmd8.ctx); return true; } static uint32_t zip_uncompress_data_ppmd(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk) { uint32_t bytes_done = 0; if (!uncomp->state.ppmd8.ctx.Base) { uint8_t order, size, method; if (uncomp->input.bytes_left < 2) { warn("Insufficient data in compressed stream"); return ERR_UNCOMP; } order = (uncomp->input.data[uncomp->input.offset] & 0x0F) + 1; size = ((uncomp->input.data[uncomp->input.offset] >> 4) | ((uncomp->input.data[uncomp->input.offset + 1] << 4) & 0xFF)); method = uncomp->input.data[uncomp->input.offset + 1] >> 4; uncomp->input.bytes_left -= 2; uncomp->input.offset += 2; if (order < 2 || method > 2) { warn("Invalid PPMd data stream"); return ERR_UNCOMP; } #ifndef PPMD8_FREEZE_SUPPORT if (order == 2) { warn("PPMd freeze method isn't supported"); return ERR_UNCOMP; } #endif if (!Ppmd8_Alloc(&uncomp->state.ppmd8.ctx, (size + 1) << 20, &uncomp->state.ppmd8.alloc)) return ERR_UNCOMP; if (!Ppmd8_RangeDec_Init(&uncomp->state.ppmd8.ctx)) return ERR_UNCOMP; Ppmd8_Init(&uncomp->state.ppmd8.ctx, order, method); } while (bytes_done < buffer_size) { int symbol = Ppmd8_DecodeSymbol(&uncomp->state.ppmd8.ctx); if (symbol < 0) { warn("Invalid PPMd data stream"); return ERR_UNCOMP; } ((uint8_t *)buffer)[bytes_done++] = (uint8_t)symbol; } if (is_last_chunk) { int symbol = Ppmd8_DecodeSymbol(&uncomp->state.ppmd8.ctx); if (symbol != -1 || !Ppmd8_RangeDec_IsFinishedOK(&uncomp->state.ppmd8.ctx)) { warn("Invalid PPMd data stream"); return ERR_UNCOMP; } } return bytes_done; } static void zip_clear_uncompress_ppmd(struct ar_archive_zip_uncomp *uncomp) { Ppmd8_Free(&uncomp->state.ppmd8.ctx, &uncomp->state.ppmd8.alloc); } /***** common decompression handling *****/ static bool zip_init_uncompress(ar_archive_zip *zip) { struct ar_archive_zip_uncomp *uncomp = &zip->uncomp; if (uncomp->initialized) return true; memset(uncomp, 0, sizeof(*uncomp)); if (zip->entry.method == METHOD_DEFLATE) { #ifdef HAVE_ZLIB if (zip_init_uncompress_deflate(uncomp)) { uncomp->uncompress_data = zip_uncompress_data_deflate; uncomp->clear_state = zip_clear_uncompress_deflate; } #else if (zip_init_uncompress_deflate64(uncomp, false)) { uncomp->uncompress_data = zip_uncompress_data_deflate64; uncomp->clear_state = zip_clear_uncompress_deflate64; } #endif } else if (zip->entry.method == METHOD_DEFLATE64) { if (zip_init_uncompress_deflate64(uncomp, true)) { uncomp->uncompress_data = zip_uncompress_data_deflate64; uncomp->clear_state = zip_clear_uncompress_deflate64; } } else if (zip->entry.method == METHOD_BZIP2) { #ifdef HAVE_BZIP2 if (zip_init_uncompress_bzip2(uncomp)) { uncomp->uncompress_data = zip_uncompress_data_bzip2; uncomp->clear_state = zip_clear_uncompress_bzip2; } #else warn("BZIP2 support requires BZIP2 (define HAVE_BZIP2)"); #endif } #ifdef HAVE_LIBLZMA else if (zip->entry.method == METHOD_LZMA) { if (zip_init_uncompress_lzma(uncomp)) { uncomp->uncompress_data = zip_uncompress_data_lzma1; uncomp->clear_state = zip_clear_uncompress_lzma; } } else if (zip->entry.method == METHOD_XZ) { if (zip_init_uncompress_lzma(uncomp)) { uncomp->uncompress_data = zip_uncompress_data_xz; uncomp->clear_state = zip_clear_uncompress_lzma; } } #else else if (zip->entry.method == METHOD_LZMA) { if (zip_init_uncompress_lzma(uncomp, zip->entry.flags)) { uncomp->uncompress_data = zip_uncompress_data_lzma; uncomp->clear_state = zip_clear_uncompress_lzma; } } #endif // HAVE_LIBLZMA else if (zip->entry.method == METHOD_PPMD) { if (zip_init_uncompress_ppmd(zip)) { uncomp->uncompress_data = zip_uncompress_data_ppmd; uncomp->clear_state = zip_clear_uncompress_ppmd; } } else warn("Unsupported compression method %d", zip->entry.method); uncomp->initialized = uncomp->uncompress_data != NULL && uncomp->clear_state != NULL; return uncomp->initialized; } void zip_clear_uncompress(struct ar_archive_zip_uncomp *uncomp) { if (!uncomp->initialized) return; uncomp->clear_state(uncomp); uncomp->initialized = false; } bool zip_uncompress_part(ar_archive_zip *zip, void *buffer, size_t buffer_size) { struct ar_archive_zip_uncomp *uncomp = &zip->uncomp; uint32_t count; if (!zip_init_uncompress(zip)) return false; for (;;) { if (buffer_size == 0) return true; if (uncomp->input.bytes_left < sizeof(uncomp->input.data) / 2 && zip->progress.data_left) { if (!zip_fill_input_buffer(zip)) return false; } count = buffer_size >= UINT32_MAX ? UINT32_MAX - 1 : (uint32_t)buffer_size; count = uncomp->uncompress_data(uncomp, buffer, count, zip->progress.bytes_done + count == zip->super.entry_size_uncompressed); if (count == ERR_UNCOMP) return false; if (count == 0 && !zip->progress.data_left) { warn("Insufficient data in compressed stream"); return false; } zip->progress.bytes_done += count; buffer = (uint8_t *)buffer + count; buffer_size -= count; } } unarr-1.0.1/zip/zip.c000066400000000000000000000164341317727033500144300ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #include "zip.h" static void zip_close(ar_archive *ar) { ar_archive_zip *zip = (ar_archive_zip *)ar; free(zip->entry.name); zip_clear_uncompress(&zip->uncomp); } static bool zip_parse_local_entry(ar_archive *ar, off64_t offset) { ar_archive_zip *zip = (ar_archive_zip *)ar; struct zip_entry entry; offset = zip_find_next_local_file_entry(ar->stream, offset); if (offset < 0) { if (ar->entry_offset_next) ar->at_eof = true; else warn("Work around failed, no entries found in this file"); return false; } if (!ar_seek(ar->stream, offset, SEEK_SET)) { warn("Couldn't seek to offset %" PRIi64, offset); return false; } if (!zip_parse_local_file_entry(zip, &entry)) return false; ar->entry_offset = offset; ar->entry_offset_next = offset + ZIP_LOCAL_ENTRY_FIXED_SIZE + entry.namelen + entry.extralen + (off64_t)entry.datasize; if (ar->entry_offset_next <= ar->entry_offset) { warn("Compressed size is too large (%" PRIu64 ")", entry.datasize); return false; } ar->entry_size_uncompressed = (size_t)entry.uncompressed; ar->entry_filetime = ar_conv_dosdate_to_filetime(entry.dosdate); zip->entry.offset = offset; zip->entry.method = entry.method; zip->entry.flags = entry.flags; zip->entry.crc = entry.crc; free(zip->entry.name); zip->entry.name = NULL; zip->entry.dosdate = entry.dosdate; zip->progress.crc = 0; zip->progress.bytes_done = 0; zip->progress.data_left = (size_t)entry.datasize; zip_clear_uncompress(&zip->uncomp); if (entry.datasize == 0 && ar_entry_get_name(ar) && *zip->entry.name && zip->entry.name[strlen(zip->entry.name) - 1] == '/') { log("Skipping directory entry \"%s\"", zip->entry.name); return zip_parse_local_entry(ar, ar->entry_offset_next); } if (entry.datasize == 0 && entry.uncompressed == 0 && (entry.flags & (1 << 3))) { warn("Deferring sizes to data descriptor isn't supported"); ar->entry_size_uncompressed = 1; } return true; } static bool zip_parse_entry(ar_archive *ar, off64_t offset) { ar_archive_zip *zip = (ar_archive_zip *)ar; struct zip_entry entry; if (offset >= zip->dir.end_offset) { ar->at_eof = true; return false; } if (!ar_seek(ar->stream, offset, SEEK_SET)) { warn("Couldn't seek to offset %" PRIi64, offset); return false; } if (!zip_parse_directory_entry(zip, &entry)) { warn("Couldn't read directory entry @%" PRIi64, offset); return false; } ar->entry_offset = offset; ar->entry_offset_next = offset + ZIP_DIR_ENTRY_FIXED_SIZE + entry.namelen + entry.extralen + entry.commentlen; ar->entry_size_uncompressed = (size_t)entry.uncompressed; ar->entry_filetime = ar_conv_dosdate_to_filetime(entry.dosdate); zip->entry.offset = entry.header_offset; zip->entry.method = entry.method; zip->entry.flags = entry.flags; zip->entry.crc = entry.crc; free(zip->entry.name); zip->entry.name = NULL; zip->entry.dosdate = entry.dosdate; zip->progress.crc = 0; zip->progress.bytes_done = 0; zip->progress.data_left = (size_t)entry.datasize; zip_clear_uncompress(&zip->uncomp); if (entry.datasize == 0 && ((entry.version >> 8) == 0 || (entry.version >> 8) == 3) && (entry.attr_external & 0x40000010)) { log("Skipping directory entry \"%s\"", zip_get_name(ar)); return zip_parse_entry(ar, ar->entry_offset_next); } return true; } static bool zip_copy_stored(ar_archive_zip *zip, void *buffer, size_t count) { if (count > zip->progress.data_left) { warn("Unexpected EOS in stored data"); return false; } if (ar_read(zip->super.stream, buffer, count) != count) { warn("Unexpected EOF in stored data"); return false; } zip->progress.data_left -= count; zip->progress.bytes_done += count; return true; } static bool zip_uncompress(ar_archive *ar, void *buffer, size_t count) { ar_archive_zip *zip = (ar_archive_zip *)ar; if (zip->progress.bytes_done == 0) { if ((zip->entry.flags & ((1 << 0) | (1 << 6)))) { warn("Encrypted archives aren't supported"); return false; } if (!zip_seek_to_compressed_data(zip)) { warn("Couldn't find data for file"); return false; } } if (count > ar->entry_size_uncompressed - zip->progress.bytes_done) { warn("Requesting too much data (%" PRIuPTR " < %" PRIuPTR ")", ar->entry_size_uncompressed - zip->progress.bytes_done, count); return false; } if (zip->entry.method == METHOD_STORE) { if (!zip_copy_stored(zip, buffer, count)) return false; } else if (zip->deflatedonly && zip->entry.method != METHOD_DEFLATE) { warn("Only store and deflate compression methods are allowed"); return false; } else { if (!zip_uncompress_part(zip, buffer, count)) return false; } zip->progress.crc = ar_crc32(zip->progress.crc, buffer, count); if (zip->progress.bytes_done < ar->entry_size_uncompressed) return true; if (zip->uncomp.initialized ? !zip->uncomp.input.at_eof || zip->uncomp.input.bytes_left : zip->progress.data_left) log("Compressed block has more data than required"); if (zip->progress.crc != zip->entry.crc) { warn("Checksum of extracted data doesn't match"); return false; } return true; } static size_t zip_get_global_comment(ar_archive *ar, void *buffer, size_t count) { ar_archive_zip *zip = (ar_archive_zip *)ar; if (!zip->comment_size) return 0; if (!buffer) return zip->comment_size; if (!ar_seek(ar->stream, zip->comment_offset, SEEK_SET)) return 0; if (count > zip->comment_size) count = zip->comment_size; return ar_read(ar->stream, buffer, count); } ar_archive *ar_open_zip_archive(ar_stream *stream, bool deflatedonly) { ar_archive *ar; ar_archive_zip *zip; struct zip_eocd64 eocd = { 0 }; off64_t offset = zip_find_end_of_central_directory(stream); if (offset < 0) return NULL; if (!ar_seek(stream, offset, SEEK_SET)) return NULL; if (!zip_parse_end_of_central_directory(stream, &eocd)) return NULL; ar = ar_open_archive(stream, sizeof(ar_archive_zip), zip_close, zip_parse_entry, zip_get_name, zip_uncompress, zip_get_global_comment, eocd.dir_offset); if (!ar) return NULL; zip = (ar_archive_zip *)ar; zip->dir.end_offset = zip_find_end_of_last_directory_entry(stream, &eocd); if (zip->dir.end_offset < 0) { warn("Couldn't read central directory @%" PRIi64 ", trying to work around...", eocd.dir_offset); ar->parse_entry = zip_parse_local_entry; ar->entry_offset_first = ar->entry_offset_next = 0; } zip->deflatedonly = deflatedonly; zip->comment_offset = offset + ZIP_END_OF_CENTRAL_DIR_SIZE; zip->comment_size = eocd.commentlen; return ar; } unarr-1.0.1/zip/zip.h000066400000000000000000000104711317727033500144300ustar00rootroot00000000000000/* Copyright 2015 the unarr project authors (see AUTHORS file). License: LGPLv3 */ #ifndef zip_zip_h #define zip_zip_h #include "../common/unarr-imp.h" #ifdef HAVE_ZLIB #include #endif #include "inflate.h" #ifdef HAVE_BZIP2 #include #endif #ifdef HAVE_LIBLZMA #include #else #include "../lzmasdk/LzmaDec.h" #endif #include "../lzmasdk/Ppmd8.h" typedef struct ar_archive_zip_s ar_archive_zip; /***** parse-zip *****/ enum zip_signatures { SIG_LOCAL_FILE_HEADER = 0x04034B50, SIG_CENTRAL_DIRECTORY = 0x02014B50, SIG_END_OF_CENTRAL_DIRECTORY_64 = 0x06064B50, SIG_END_OF_CENTRAL_DIRECTORY_64_LOCATOR = 0x07064B50, SIG_END_OF_CENTRAL_DIRECTORY = 0x06054B50, }; enum compression_method { METHOD_STORE = 0, METHOD_DEFLATE = 8, METHOD_DEFLATE64 = 9, METHOD_BZIP2 = 12, METHOD_LZMA = 14, METHOD_XZ = 95, METHOD_PPMD = 98, }; #define ZIP_LOCAL_ENTRY_FIXED_SIZE 30 #define ZIP_DIR_ENTRY_FIXED_SIZE 46 #define ZIP_END_OF_CENTRAL_DIR_SIZE 22 struct zip_entry { uint32_t signature; uint16_t version; uint16_t min_version; uint16_t flags; uint16_t method; uint32_t dosdate; uint32_t crc; uint64_t datasize; uint64_t uncompressed; uint16_t namelen; uint16_t extralen; uint16_t commentlen; uint32_t disk; uint16_t attr_internal; uint32_t attr_external; off64_t header_offset; }; struct zip_eocd64 { uint32_t signature; uint16_t version; uint16_t min_version; uint32_t diskno; uint32_t diskno_dir; uint64_t numentries_disk; uint64_t numentries; uint64_t dir_size; off64_t dir_offset; uint16_t commentlen; }; struct ar_archive_zip_entry { off64_t offset; uint16_t method; uint16_t flags; uint32_t crc; char *name; uint32_t dosdate; }; bool zip_seek_to_compressed_data(ar_archive_zip *zip); bool zip_parse_local_file_entry(ar_archive_zip *zip, struct zip_entry *entry); off64_t zip_find_next_local_file_entry(ar_stream *stream, off64_t offset); bool zip_parse_directory_entry(ar_archive_zip *zip, struct zip_entry *entry); off64_t zip_find_end_of_last_directory_entry(ar_stream *stream, struct zip_eocd64 *eocd); bool zip_parse_end_of_central_directory(ar_stream *stream, struct zip_eocd64 *eocd); off64_t zip_find_end_of_central_directory(ar_stream *stream); const char *zip_get_name(ar_archive *ar); /***** uncompress-zip *****/ struct ar_archive_zip_uncomp; typedef uint32_t (* zip_uncomp_uncompress_data_fn)(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk); typedef void (* zip_uncomp_clear_state_fn)(struct ar_archive_zip_uncomp *uncomp); struct InputBuffer { uint8_t data[4096]; uint16_t offset; uint16_t bytes_left; bool at_eof; }; struct ByteReader { IByteIn super; struct InputBuffer *input; ar_archive_zip *zip; }; struct ar_archive_zip_uncomp { bool initialized; zip_uncomp_uncompress_data_fn uncompress_data; zip_uncomp_clear_state_fn clear_state; union { #ifdef HAVE_ZLIB z_stream zstream; #endif inflate_state *inflate; #ifdef HAVE_BZIP2 bz_stream bstream; #endif #ifdef HAVE_LIBLZMA lzma_stream lzmastream; #else struct { CLzmaDec dec; ELzmaFinishMode finish; ISzAlloc alloc; } lzma; #endif //HAVE_LIBLZMA struct { CPpmd8 ctx; struct ByteReader bytein; ISzAlloc alloc; } ppmd8; } state; struct InputBuffer input; }; bool zip_uncompress_part(ar_archive_zip *zip, void *buffer, size_t buffer_size); void zip_clear_uncompress(struct ar_archive_zip_uncomp *uncomp); /***** zip *****/ struct ar_archive_zip_dir { /* off64_t offset; // use ar_archive::entry_offset_first */ off64_t end_offset; }; struct ar_archive_zip_progress { size_t data_left; size_t bytes_done; uint32_t crc; }; struct ar_archive_zip_s { ar_archive super; struct ar_archive_zip_dir dir; struct ar_archive_zip_entry entry; struct ar_archive_zip_uncomp uncomp; struct ar_archive_zip_progress progress; bool deflatedonly; off64_t comment_offset; uint16_t comment_size; }; #endif